/* app.css — Wegital Design System (DEC-056)
   สีมาจาก CSS variables เสมอ (รองรับ dark mode) — ห้าม hardcode hex ใน component
   Even-Pixel: ค่า px เป็นเลขคู่ (ยกเว้น border:1px, radius:999px) */

/* ---------- tokens (§8) ---------- */
:root {
  --bg:#F5F5F7; --surface:#FFFFFF; --surface-2:#F0F0F2; --border:#EDEDEF;
  --text:#1C1C1E; --text-2:#6C6C70; --alert-bg:#FFF0EF; --alert:#FF453A;
  --primary:linear-gradient(135deg,#FF7864,#DC3C32); --primary-solid:#DC3C32; --primary-light:#FF7864;
  /* coral ทึบเข้มพอให้ "ตัวอักษรขาว" ผ่าน WCAG AA — gradient --primary (ขาวบน) ตก AA (~2.6–4.4:1)
     #CE3329 = white contrast 5.09:1 (คำนวณจริง) ยังคงโทน coral แบรนด์ (ใช้เป็นพื้น bubble ผู้ใช้)
     S27 P0: .btn-primary เปลี่ยนมาใช้ token นี้เป็น background ตรง (ไม่ใช่แค่ bubble) */
  --primary-aa:#CE3329;
  /* S27 P0 (audit B4 + verify browser จริง): --alert-text ถูกออกแบบเป็น "text บน surface เข้ม/อ่อนของแต่ละ
     theme" (dark variant #FF6961 สว่าง เพื่ออ่านบนพื้นมืด) — เอาไปทำ "พื้นปุ่มที่มีตัวอักษรขาวทับ" ไม่ได้
     เพราะ dark variant สว่างเกินไป (ขาวบน #FF6961 = 2.82:1 fail จริง วัดจาก browser)
     → สร้าง --alert-aa แยก เป็นค่าคงที่ไม่ผันตาม theme (pattern เดียวกับ --primary-aa) ผ่าน AA ทั้ง 2 mode
     (ขาวบน #C42B21 = 5.65:1) ใช้เป็นพื้นปุ่ม .btn-danger โดยเฉพาะ ไม่ใช้ --alert-text ตรง ๆ */
  --alert-aa:#C42B21;
  --secondary:linear-gradient(135deg,#EB3241,#7846D2); --secondary-start:#EB3241; --secondary-end:#7846D2;
  /* --secondary-chart (S41 · เอ audit-s41-weteam-chart-contrast §1) — token แยกสำหรับ bar/progress
     chart fill (.chk-bar-fill/.rpt-bar-fill/.rel-progress-bar>span) เท่านั้น ไม่แตะ --secondary/
     --secondary-end ตรง (ยังใช้ที่ .badge--lead/.rel-chip tint + WeFin trend-line ปกติ ไม่ fail 1.4.11
     อยู่แล้ว — ทางเลือก A ที่ เอ เสนอ, surgical ไม่กระทบของอื่น) light = เท่า --secondary เดิม (ผ่านอยู่แล้ว) */
  --secondary-chart:var(--secondary);
  --green:#30D158; --warm:#FFB800; --cold:#64A0FF;
  /* text-safe variants: --warm/--alert เป็นสี fill/icon สว่าง fail AA เมื่อใช้เป็น "ตัวอักษร" บนพื้นขาว
     (warm 1.73:1, alert 3.41:1) → token เข้มพอผ่าน AA ≥4.5:1 เฉพาะกรณีใช้เป็นสีข้อความ (report ถัง C)
     S27: rename warn-text→warm-text ให้ตรงชื่อ base token --warm (Global DS SKILL.md §1.1) */
  --warm-text:#8A5A00; --alert-text:#C42B21;
  /* text-safe green/purple (S25): --green/--secondary-end เป็นสี fill สว่าง fail AA เมื่อใช้เป็น "ตัวอักษร"
     บนพื้นอ่อน (green 1.82:1 / purple 4.9 เฉียด) → token เข้มพอผ่าน AA. ดู dark override ด้านล่าง
     S27: rename rel-chip-text→secondary-text ให้ตรงชื่อ base token --secondary (Global DS SKILL.md §1.1) */
  --green-text:#1F7A3D; --secondary-text:#6B3FB8;
  /* S27 P0 (audit A5/B5): --cold ไม่มี text-safe variant มาก่อน (--cold เป็น text/badge = 2.28–2.62:1 fail)
     #1E5BB8 = 6.47:1 บน --surface (ผ่าน AA) — คำนวณจาก SKILL.md §1.1 */
  --cold-text:#1E5BB8;
  /* --warn-bg: พื้นเตือนเหลืองอ่อน (ใช้คู่ --warm-text). alpha → composite เหนือ --surface เอง
     → light บนขาว = contrast กับ #8A5A00 ~5.4:1 (ผ่าน AA); ดู dark override ด้านล่าง */
  --warn-bg:color-mix(in srgb, var(--warm) 16%, transparent);
  /* S27 P0 (audit-css #7 / synth-1 foundation): --border (#EDEDEF) เป็น hairline ตกแต่ง 1.17:1 —
     ใช้เป็นขอบ input/checkbox/dropdown (boundary จริงของ control) ไม่ผ่าน 1.4.11 (ต้อง ≥3:1)
     --border-control = ขอบ control ที่ต้องแยกจากพื้นชัดเจน
     S42 fix (พบตอน Gantt bar verify): #8E8E93 เดิม = 3.26:1 บน --surface แต่แค่ 2.86:1 บน --surface-2
     (< floor 3:1) เพราะ --surface-2 (#F0F0F2) สว่างใกล้ --surface (#FFFFFF) มาก ต้องเข้มขึ้นให้ผ่านทั้งคู่
     → #88888D = 3.53:1 บน --surface / 3.10:1 บน --surface-2 (คำนวณ relative-luminance จริง ผ่าน ≥3:1 ทั้งคู่) */
  --border-control:#88888D;
  /* S33 (เอส — Global DS toggle-switch primitive): motion easing token กลาง — ก่อนหน้านี้ DS ไม่มี easing
     token รวม (ใช้ `ease`/`.15s ease` ดิบกระจัดกระจาย ~6 จุด) → เพิ่ม --ease-standard (Material standard
     easing curve cubic-bezier(.4,0,.2,1)) ให้ toggle-switch ใช้ก่อน + component อื่น reuse ได้ทีหลัง
     (additive, ไม่กระทบ transition เดิมที่ยังเขียน ease ตรง ๆ) */
  --ease-standard:cubic-bezier(.4,0,.2,1);
  --r:16px;
  /* S27 Batch2 (audit-css #8 B8): typography scale token — DS §3 นิยาม H1/H2/Body/Label/Micro (24/18/14/12/11)
     แต่ของจริงมี 2 ขั้นแทรกที่ใช้ซ้ำเยอะโดยพฤตินัย (13px ×81 = "secondary/dense" text, 15px ×4 = "emphasis/heading เล็ก"
     เช่น .fc-name, .hol-formt, .cal-v-ag-num, avatar--md ตรงสเปก DS "Medium 36px/font15")
     → tokenize เป็น product-tier extension (DEC-070) ก่อน ค่าเดิมไม่เปลี่ยน (zero visual diff) — รอ เอส ตัดสินใจ
     ว่าจะ "รับรองเป็นขั้นทางการ" ใน Global SKILL.md §3 หรือ "รวมเข้า 12/14" ภายหลัง (audit เสนอ 2 ทางเลือกไว้แล้ว) */
  --fs-sm:13px; --fs-md:15px;
  /* --fs-micro = floor ของ Micro (DS §3) — ใช้แทนจุดที่เคย 10px (ต่ำกว่า floor ที่กำหนด) เฉพาะจุดที่เป็น
     "ข้อความอ่าน" จริง (badge label/timeline digit) — จุดที่เป็น icon inline (font-size ตาม DEC-068#3)
     หรือกล่อง fixed-height แคบเกิน 11px (เสี่ยง clip) ยังคง 10px literal ไว้ + comment กำกับเหตุผล */
  --fs-micro:11px;
  /* S27 Batch2 (audit-css #6): elevation scale กลาง — ก่อนหน้านี้มี box-shadow ~15 ค่าไม่ซ้ำกันสำหรับ
     "ชั้นลอย" (dropdown/menu/overlay) ทั้งที่ card shadow หลัก reuse ดีอยู่แล้ว 18 จุด
     --shadow-card = ชั้น Card (DS §4 — "light mode only" ปิดใน dark ด้านล่าง)
     --shadow-tab  = ชั้นปุ่ม/tab active เล็ก (segmented/view-tab lift)
     --shadow-dropdown = ชั้น dropdown/color-picker panel เล็ก · --shadow-overlay = ชั้น cross-view/location panel ใหญ่กว่า
     (ค่า one-off ที่เหลือ ~8 ตัว ของ modal/drawer ยังไม่รวมชุดนี้ — ต่างกันเชิงทิศทาง/บริบทมากพอที่ต้องให้ เอส ตัดสินค่ามาตรฐานก่อน) */
  --shadow-card:0 1px 4px rgba(0,0,0,.06);
  --shadow-tab:0 1px 3px rgba(0,0,0,.10);
  --shadow-dropdown:0 8px 24px rgba(0,0,0,.16);
  --shadow-overlay:0 12px 40px rgba(0,0,0,.16);
  /* Label Color Palette (DEC-055) — 13 สี logical (base). chip bg/fg คำนวณด้วย color-mix
     จาก --surface/--text → auto ปรับ light/dark เอง (ไม่ต้อง define bg/fg ต่อ theme)
     emerald = สถานะ Complete (status ที่ 8) — เขียวเข้มกว่า green(Done) เพื่อแยก 2 ชั้นจบ */
  --lb-gray:#8A94A0; --lb-red:#E5484D; --lb-orange:#E8722C; --lb-amber:#E8A100;
  --lb-yellow:#EBC634; --lb-green:#2E9E5B; --lb-teal:#2E9E92; --lb-blue:#3A72D4;
  --lb-indigo:#5A5FD6; --lb-violet:#8A78C4; --lb-pink:#E86AA6; --lb-brown:#A9761F;
  --lb-emerald:#0A8F5E;
  /* S42 (เอส Gantt Bar v2 §5, Global DS SKILL.md §8) — ink คงที่ไม่สลับตาม theme: ใช้กับ Gantt bar
     solid status fill เท่านั้น (--lb-* ไม่มี dark override → fill เดิมเหมือนกันทั้ง 2 theme →
     ink ที่จับคู่ก็ต้องคงที่ตาม ไม่ใช้ var(--text)/var(--surface) ที่สลับค่าไปตาม theme เพราะจะพัง) */
  --ink-light:#FFFFFF; --ink-dark:#1C1C1E;
  /* S42 — solid-safe fill สำหรับ 3 สถานะที่ raw --lb-* ไม่ผ่าน 4.5:1 ทั้งคู่ (white/dark ink เมื่อเป็น
     solid fill เต็มพื้น): red/brown/emerald. สูตร color-mix 90% retain + 10% black (คำนวณจาก base
     token ไม่ hardcode hex ตรง ๆ) — ★ ต้องประกาศ "หลัง" --lb-red/--lb-brown/--lb-emerald เสมอ */
  --lb-red-solid:color-mix(in srgb, var(--lb-red) 90%, black);         /* #CE4145 → white 4.70:1 */
  --lb-brown-solid:color-mix(in srgb, var(--lb-brown) 90%, black);     /* #986A1C → white 4.76:1 */
  --lb-emerald-solid:color-mix(in srgb, var(--lb-emerald) 90%, black); /* #098155 → white 4.90:1 */
  /* Categorical data-viz palette (S35 — apply พีอนุมัติแล้ว, docs/design/categorical-palette-proposal-s35.md)
     6 สี colorblind-safe ตั้งต้นจาก Okabe-Ito CUD (ตัด orange ออก — ชน --warm เกือบสนิท 1.8° hue;
     sky-blue เลื่อน hue -8° ไปทาง cyan กันชน --cold) ปรับ lightness แยกต่อ theme ด้วยเทคนิค
     "staggered contrast target" (เป้า contrast ต่างระดับกันต่อสี คง lightness-gap ที่ Okabe-Ito ตั้งใจ
     ไว้แยกสีที่ hue ใกล้กัน เช่น Teal/Blue/Cyan) — ทุกสีผ่าน WCAG 1.4.11 (≥3:1) กับพื้นแย่สุดทั้ง 2 theme
     + จำลอง CVD จริง (Machado-Oliveira-Fernandes 2009) ผ่านเกณฑ์แยกออกได้ทุกคู่ทุกชนิดตาบอดสี
     ใช้กับ chart แยกหมวด (stacked bar/bar-by-category/multi-line ≤6 เส้น) เท่านั้น — ห้ามใช้แทนความหมาย
     สถานะ (ใช้ semantic --green/--alert/--warm/--cold แทน) · เรียงลำดับ 1→6 เสมอ ห้าม loop สีเกิน 6 หมวด
     (เกิน 6 → รวม "อื่น ๆ" ใช้ --cat-other) · ดู SKILL.md §5 Data Visualization สำหรับ usage guide เต็ม */
  --cat-1:#C5679B; /* Purple */    --cat-2:#00523B; /* Teal */
  --cat-3:#008CDB; /* Blue */      --cat-4:#116C86; /* Cyan */
  --cat-5:#777009; /* Olive */     --cat-6:#C95800; /* Vermillion */
  --cat-other:var(--text-2);       /* bucket "อื่น ๆ" เกิน 6 หมวด — reuse ไม่สร้างสีใหม่ */
  /* Custom scrollbar (B2) — เทากลาง alpha จาง composite เหนือพื้นได้ทุก mode; บาง มน กลมกลืน */
  --scrollbar-thumb:rgba(60,60,67,.22); --scrollbar-thumb-hover:rgba(60,60,67,.40);
  /* Density system (DS §6.5 / DEC-068#2) — comfortable = default เสมอ (ตรงกับของเดิม 100% = 0 regression)
     token เชิง generic (density-pad/row-h/gap) = ตาม spec SKILL.md §6.5 verbatim (สำรองไว้ใช้กับ component
     อื่นในอนาคต — Card/List) · token tbl-cell-* = ผูกกับ Table โดยเฉพาะ (ค่าที่ใช้จริง batch นี้) */
  --density-pad:20px; --density-row-h:40px; --density-gap:16px;
  /* S42 (เอส flagged proposal → apply): comfortable 6px→8px ให้ตารางโปร่งขึ้นเทียบ Notion/Linear
     (row ~32px → ~36-40px กับ font-size 13-14px) — verify browser จริงทุกชนิดตารางแล้วไม่ล้น (ดู handoff) */
  --tbl-cell-py:8px; --tbl-cell-px:12px;
  /* S36 (เอส ds-additions §4 · ซี implement) — RGB triplet ของ --text-2 สำหรับใช้กับ rgba() ทำ
     grayscale qualitative-range band ของ Bullet chart (pattern เดียวกับ --primary-solid ที่มี rgba ใช้อยู่แล้ว
     ใน dropdown selected state) — ไม่ใช่สี semantic เอง แค่ตัวเลข RGB ของ token สีที่มีอยู่แล้ว */
  --text-2-rgb:108,108,112;
}
[data-density="compact"] {
  /* compact: ลดเฉพาะ "ช่องไฟรอบเนื้อหา" (padding ของ .tbl-th/.tbl-td) — ไม่แตะ padding ของปุ่ม/checkbox/
     input ภายในเซลล์ (.dd-cellbtn/.cell-input/.cbx-box มีขนาด hit-area ของตัวเองแยกจาก cell padding
     อยู่แล้ว) → เห็นแถวถี่ขึ้นโดยไม่ลด hit-area ของ control ที่มีอยู่เดิม */
  --density-pad:12px; --density-row-h:32px; --density-gap:8px;
  /* S42: compact 4px→6px (+2px สม่ำเสมอเท่า comfortable) — คงไว้แน่นสำหรับ view ข้อมูลเยอะ */
  --tbl-cell-py:6px; --tbl-cell-px:8px;
}
[data-theme="dark"] {
  --bg:#121212; --surface:#1E1E1E; --surface-2:#252525; --border:#262628;
  --scrollbar-thumb:rgba(235,235,245,.18); --scrollbar-thumb-hover:rgba(235,235,245,.34);
  /* S27 P0 (DEC-068#8): off-white #F0F0F2 แทนขาวจ๊ด #FFFFFF — กัน halation บนพื้นดำ
     (ยังผ่านสบาย 14.65:1) */
  --text:#F0F0F2; --text-2:#98989D; --alert-bg:#3A1C1C;
  /* dark: พื้นเข้ม → ใช้ค่าสว่างได้ (amber/red สว่างผ่าน AA บนพื้น #1E1E1E: 9.7:1 / 5.4:1) */
  --warm-text:#FFC53D; --alert-text:#FF6961;
  /* dark: พื้นเข้ม → ใช้ค่าสว่างผ่าน AA (green #5FD98A ~8:1, purple #B79CE8 ~6:1 บน #1E1E1E) */
  --green-text:#5FD98A; --secondary-text:#B79CE8;
  /* S27: --cold (#64A0FF) เองสว่างพอบนพื้นมืดแล้ว (6.36:1) — ใช้เป็น cold-text ตรง ไม่ต้องเข้มขึ้นแบบ light */
  --cold-text:#64A0FF;
  /* คงค่าเดิม (rgba เดียว composite เหนือ surface เข้มเอง) → contrast กับ #FFC53D ~7.4:1 (ผ่าน AA).
     override ไว้เป็น hook ให้ปรับ dark แยกได้ภายหลัง โดย light ไม่กระทบ */
  --warn-bg:color-mix(in srgb, var(--warm) 16%, transparent);
  /* S27 P0: ขอบ control บนพื้นมืด #262628 vs #1E1E1E = 1.10:1 fail → #6A6A6C = 3.09:1 ผ่าน 1.4.11
     S42 fix: #6A6A6C เดิมแค่ 2.84:1 บน --surface-2 dark (#252525, สว่างกว่า --surface #1E1E1E) — fail floor
     → #717173 (สว่างขึ้นเล็กน้อย) = 3.43:1 บน --surface / 3.15:1 บน --surface-2 (ผ่าน ≥3:1 ทั้งคู่) */
  --border-control:#717173;
  /* Categorical data-viz palette dark override (S35) — ค่าคนละชุดจาก light (raw Okabe-Ito ปรับ lightness
     แยกต่อ theme, คง Hue+Saturation เดิม) ผ่าน WCAG 1.4.11 (≥3:1, margin แคบสุด Teal 3.18:1 vs surf-2) */
  --cat-1:#E1AFCB; /* Purple */    --cat-2:#00825F; /* Teal */
  --cat-3:#0084CF; /* Blue */      --cat-4:#1CAFDB; /* Cyan */
  --cat-5:#978E0C; /* Olive */     --cat-6:#FF9948; /* Vermillion */
  /* S27 Batch2 (audit-css #7): DS §4 Card ระบุ box-shadow "light mode only" — เดิม override แค่ 1/18 จุด
     (.rel-card:hover) ตอนนี้ card shadow ทุกจุดอ้าง --shadow-card ตัวเดียว → ปิดที่นี่ที่เดียวพอ */
  --shadow-card:none;
  /* S36 — --text-2-rgb dark: #98989D → 152,152,157 */
  --text-2-rgb:152,152,157;
  /* S41 (เอ audit §1) — bar/progress chart fill dark: --secondary-end #7846D2 worst-case ตลอด
     gradient vs --surface-2 (track) = 2.61:1 fail 1.4.11 (≥3:1) → ปรับ lightness (HSL) เป็น #8E63DA
     (H/S คงเดิม ±0.3°/±0.008 — เทคนิคเดียวกับ -text variant/--cat-* dark) worst-case ใหม่ = 3.29:1 ผ่าน
     ทุกพื้น (surface/surface-2/bg) พร้อม margin — คำนวณ sample 101 จุดตลอด gradient แล้ว (ไม่ใช่แค่ 2 ปลาย) */
  --secondary-chart:linear-gradient(135deg,#EB3241,#8E63DA);
}

/* ---------- font: Prompt self-host (S31 perf P1 — เลิกพึ่ง Google Fonts CDN, กัน SPOF) ----------
   ไฟล์ woff2 คือของ Google Fonts เดิม (fonts.gstatic.com, v12) ดาวน์โหลดมา self-host ที่
   /static/fonts/ — คง subset เดิมของ Google (thai + latin ต่อ weight, ไม่รวม latin-ext/vietnamese
   ที่ไม่ใช้ในแอปนี้) กัน glyph ภาษาไทยตกหล่น (DS §3: Prompt 400/500/600) */
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/static/fonts/prompt-400-thai.woff2') format('woff2');
  unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC;
}
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/static/fonts/prompt-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/static/fonts/prompt-500-thai.woff2') format('woff2');
  unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC;
}
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/static/fonts/prompt-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:600; font-display:swap;
  src:url('/static/fonts/prompt-600-thai.woff2') format('woff2');
  unicode-range:U+02D7,U+0303,U+0331,U+0E01-0E5B,U+200C-200D,U+25CC;
}
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:600; font-display:swap;
  src:url('/static/fonts/prompt-600-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
/* fallback face กัน CLS: ใช้ font ที่เครื่อง user มีอยู่แล้ว (local Arial) แต่บังคับ ascent/descent/
   line-gap ให้ตรงกับ Prompt จริง — ค่าคำนวณจาก hhea table ของ Prompt v12 (unitsPerEm=1000,
   ascent=1090, descent=-422, lineGap=0 → 109% / 42.2% / 0%, ใช้ fontTools แกะตรง ไม่ได้กะประมาณ)
   ช่วงก่อนฟอนต์จริงโหลดเสร็จ browser render ด้วย fallback ที่ "กล่องบรรทัด" สูงเท่า Prompt อยู่แล้ว
   → สลับฟอนต์จริงเข้ามา (font-display:swap) ไม่เกิด reflow/CLS จาก line-height เปลี่ยน */
@font-face {
  font-family:'Prompt Fallback';
  src:local('Arial');
  ascent-override:109%; descent-override:42.2%; line-gap-override:0%;
}

/* ---------- tabular-nums real fix (S36 P1 — DEC ds-additions-s36.md §1.2, เอส) ----------
   บั๊กจริง: Prompt (ตรวจด้วย fontTools) ไม่มี GSUB feature `tnum` เลย → `.tnum`/
   `font-variant-numeric:tabular-nums` ที่มีอยู่ทั้งแอป (cost/manday/report/WeFin P&L)
   ไม่มีผลอะไร เลขแต่ละตัวกว้างไม่เท่ากัน (proportional figures) ตารางเลขไม่ align จริง.
   แก้ด้วย "unicode-range digit substitution": @font-face ชื่อ 'Prompt' เดิม แต่ src ชี้กลีฟ
   Inter (ตรวจแล้วมี tnum จริง — ทุกเลขแทนกลีฟ tabular advance-width เท่ากันหมดต่อ weight)
   จำกัด unicode-range เฉพาะ 0-9/,/./− เท่านั้น (ไม่รวม ฿ U+0E3F ไม่กระทบ) วางเป็น
   @font-face ตัวสุดท้าย (rule หลังชนะเมื่อ unicode-range ซ้อนกัน — MDN) → ทุกจุดที่ใช้
   font-family:'Prompt' ทั้งแอป เลขจะได้กลีฟจาก Inter อัตโนมัติ ไม่ต้องแก้ markup/class ใด ๆ
   ไฟล์เดียว = Inter variable font (wght axis 400-600) subset เฉพาะ 12 glyph ที่ต้องใช้
   (ตรวจ fontTools ยืนยัน: ทุก weight instance 400/500/600 ยัง tabular สม่ำเสมอในตัวเอง
   — 400=1328u, 500=1327u, 600=1325u ต่อ weight เท่ากันทุกเลข) ขนาดไฟล์ 8.2KB */
@font-face {
  font-family:'Prompt'; font-style:normal; font-weight:400 600; font-display:swap;
  src:url('/static/fonts/inter-digits-subset.woff2') format('woff2');
  unicode-range:U+30-39, U+2C, U+2E, U+2212;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing:border-box; }
html, body { margin:0; padding:0; }
body {
  font-family:'Prompt', 'Prompt Fallback', sans-serif;
  background:var(--bg); color:var(--text);
  font-size:14px; font-weight:400; line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
h1,h2,p { margin:0; overflow-wrap:anywhere; word-break:break-word; }
/* S31 poka-yoke (root cause: .cal-day เคยขาด border/appearance/width → UA default "2px outset black"
   + width ตาม content หลุดออกมา — เป็น class bug ไม่ใช่จุดเดียว) — custom control (<button>) ทุกตัวใน
   แอปนี้เป็น custom-styled เสมอ (DS ห้าม native form control) ให้เริ่มจาก "ผ้าใบเปล่า" กลางที่เดียว
   แล้วให้แต่ละ component class (.btn-primary/.dd-trigger/ฯลฯ) ทับด้วย border/background ของตัวเองอีกที
   — specificity: element selector (0,0,1) เตี้ยกว่า class selector (0,1,0) เสมอ ไม่มีทาง override
   component style ที่ตั้งใจ (เช่น .btn-primary background) ได้ ปลอดภัยจาก over-reset */
button {
  appearance:none; -webkit-appearance:none;
  border:0; background:none; font:inherit; color:inherit;
  cursor:pointer;
}

/* Uicons global fix (§7 CRITICAL) — ขาดไม่ได้ ไม่งั้นไอคอนไม่ center */
i.fi { display:inline-flex; align-items:center; line-height:1; }

/* ---------- brand mark ---------- */
.brand-mark {
  width:48px; height:48px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border-radius:14px; background:var(--primary); color:#fff; font-size:20px;
}
.brand-mark--sm { width:32px; height:32px; border-radius:10px; font-size:16px; }

/* ---------- shared: primary/ghost button ---------- */
.btn-primary {
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  /* S27 P0 (audit B3): gradient --primary (ขาวบน) ตก AA (2.59–4.43:1) — solid --primary-aa
     ผ่าน AA จริง (5.08:1). gradient --primary ยังใช้ได้เฉพาะพื้นที่ไม่มีตัวอักษรทับ (brand-mark ฯลฯ) */
  background:var(--primary-aa); color:#fff; border:none;
  border-radius:12px; padding:12px 24px; font-size:14px; font-weight:600; line-height:20px;
  /* S30 audit (ซี P1): line-height 20px → 24(padding)+20=44px, match .btn-ghost min-height:44px (even-pixel) */
  /* S56 (เต๋ เจอจากจอจริง): ปุ่มพวกนี้ถูกใช้เป็น <a> ~80 จุดทั่วแอป — UA ใส่ text-decoration:underline
     ให้ <a> เอง ทำให้ "ปุ่ม" หน้าตาเป็น "ลิงก์" (affordance ผิด · DS §ปุ่ม: ปุ่มไม่มีเส้นใต้)
     แก้ที่ต้นทาง component class (ไม่ใช่ a{} global — จะไปกินลิงก์ในเนื้อความที่ควรมีเส้นใต้) */
  text-decoration:none;
  transition:filter .15s, transform .05s;
}
.btn-primary:hover { filter:brightness(1.04); }
.btn-primary:active { transform:translateY(1px); }

.btn-ghost {
  display:inline-flex; align-items:center; gap:8px;
  background:var(--surface); color:var(--text);
  border:1px solid var(--border); border-radius:12px;
  padding:8px 16px; min-height:44px; font-size:14px; font-weight:500; /* touch target ≥44 (WCAG 2.5.5) */
  text-decoration:none; /* S56: เหตุผลเดียวกับ .btn-primary — ใช้เป็น <a> ~47 จุด ห้ามหน้าตาเป็นลิงก์ */
  transition:background .15s;
}
.btn-ghost:hover { background:var(--surface-2); }

/* ---------- alert box (§4 — ไม่มี left-accent) ---------- */
.alert-box {
  display:flex; align-items:center; gap:8px;
  /* --alert-text (text-safe) แทน --alert: --alert เป็นสี fill สว่าง fail AA เมื่อเป็นตัวอักษร
     บน --alert-bg (3.08:1) → --alert-text ผ่าน AA ทั้ง 2 mode (S25 B3 — ผู้ใช้ต้องอ่านออก) */
  background:var(--alert-bg); color:var(--alert-text);
  border-radius:var(--r); padding:12px 16px; font-size:var(--fs-sm); font-weight:500;
}

/* ---------- theme toggle ---------- */
.theme-toggle {
  width:40px; height:40px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); color:var(--text-2);
  border:1px solid var(--border); border-radius:999px; font-size:16px;
  transition:color .15s, background .15s;
}
.theme-toggle:hover { color:var(--primary-solid); background:var(--surface-2); }
.theme-toggle--float { position:fixed; top:24px; right:24px; }

/* ============ LOGIN ============ */
.auth-wrap {
  min-height:100vh;
  display:flex; align-items:center; justify-content:center;
  padding:24px;
}
.auth-card {
  width:100%; max-width:400px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:32px;
  box-shadow:var(--shadow-card);
  display:flex; flex-direction:column; gap:24px;
}
.auth-brand { display:flex; align-items:center; gap:16px; }
.brand-text h1 { font-size:24px; font-weight:600; }
.brand-sub { font-size:var(--fs-sm); color:var(--text-2); }

.auth-form { display:flex; flex-direction:column; gap:16px; }
.field { display:flex; flex-direction:column; gap:8px; border:0; margin:0; padding:0; min-width:0; }
/* F1: .field{display:flex} (0,1,0) ทับ UA [hidden]{display:none} (0,0,1) → field ที่ตั้ง hidden ยังโชว์.
   กฎนี้ specificity 0,2,0 ชนะ → ซ่อนจริง (ใช้กับ toggle field ฟอร์มลา + ทุกที่ที่ hidden .field) */
.field[hidden] { display:none; }
/* S31 (เอ audit #4): .field/.field-label ใช้กับทั้ง <div> เดิม และ <fieldset>/<legend> (radio-group a11y fix)
   reset UA fieldset/legend default (border/margin/padding/min-width) ให้ visual เหมือนเดิมทุกกรณี */
.field-label { font-size:12px; font-weight:500; color:var(--text-2); padding:0; }

/* custom input wrap + leading icon */
.input-wrap { position:relative; display:block; }
.input-wrap i {
  position:absolute; left:14px; top:50%; transform:translateY(-50%);
  color:var(--text-2); font-size:14px; pointer-events:none;
  display:inline-flex; align-items:center; line-height:1;
}
.input-wrap input {
  width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border-control); /* S27 P0: border-control (AA 1.4.11) แทน --border hairline */
  border-radius:12px; padding:10px 14px 10px 40px;
  /* S56 (เต๋): เดิม padding 12 + line-height:normal = สูง 47px — เลขคี่ (ผิด Even-Pixel) และเหลื่อม
     .dd-trigger/.dp-trigger ที่สูง 44px อยู่ 3px เวลาวางบรรทัดเดียวกัน (ขอบล่างไม่ตรง)
     → ตรึง 44px เท่ากันทุก control + padding 10px เท่า .dd-trigger (baseline ตรงกัน)
     content box เหลือ 22px > line box 21px ของ Inter 14px จึงไม่บีบตัวอักษร */
  height:44px;
  transition:border-color .15s, box-shadow .15s;
}
.input-wrap input::placeholder { color:var(--text-2); opacity:.7; }
/* Focus = ขอบ primary + ring แดงจาง (§4.5) — ห้ามปล่อย ring น้ำเงิน browser */
.input-wrap input:focus {
  outline:none; border-color:var(--primary-solid);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent);
}
.auth-form .btn-primary { margin-top:8px; width:100%; }
.auth-hint {
  font-size:11px; color:var(--text-2); text-align:center;
  border-top:1px solid var(--border); padding-top:16px;
}

/* ============ HOME / APP SHELL ============ */
/* app-shell ล็อกความสูง viewport → topbar/drawer นิ่ง, content (.page/.drawer-body) เลื่อนในตัว
   (จำเป็นสำหรับ split-pane การ์ด: feed เลื่อนในตัว + composer ปักล่างเสมอ — คลื่น 2)
   100dvh = หัก address bar มือถือ · fallback 100vh สำหรับ browser เก่า */
.shell { height:100vh; height:100dvh; overflow:hidden; display:flex; flex-direction:column; }
.topbar {
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; padding:12px 24px;
  background:var(--surface); border-bottom:1px solid var(--border);
}
.topbar-brand { display:flex; align-items:center; gap:12px; font-size:16px; flex-shrink:0; }
/* S42 followup fix (found ระหว่าง verify — pre-existing, ไม่ใช่จากงานนี้): flex-shrink:0 เดิม
   ทำให้ topbar-right (search+user+logout) "แข็ง" ไม่ยอมหด → deficit ทั้งหมดตกที่ .topbar-nav
   คนเดียวจนบีบเหลือ width:0 (nav หายทั้งแถบที่จอแคบ ~375px, ไม่ใช่แค่ wrap) + topbar-right เอง
   ก็ล้นขอบจอโดน .shell{overflow:hidden} ตัดหายไปด้วย (ดูรายงาน verify) → เปลี่ยนให้หดได้
   (มีที่ว่างพอ = ไม่หดจริง ไม่กระทบ desktop/tablet เดิม) */
.topbar-right { display:flex; align-items:center; gap:16px; flex-shrink:1; min-width:0; }

/* REQ-S12 ข้อ 2 — navigation บน top bar (ย้ายจาก drawer ซ้าย) */
/* ❗ ห้าม overflow ที่นี่ — จะ clip dropdown "จัดการ" (absolute) ที่ยื่นลงล่าง */
/* S42 followup fix: flex-wrap เดิม "wrap" ทำ nav ตกบรรทัด 2 บรรทัดตอนพื้นที่ไม่พอ (~800-830px) —
   เปลี่ยนเป็น nowrap ถาวร (ทุกความกว้าง) แล้วให้ breakpoint ด้านล่างยุบ/รวมรายการแทนการตกบรรทัด */
.topbar-nav { display:flex; align-items:center; gap:4px; flex:1 1 auto; min-width:0; flex-wrap:nowrap; }
/* S41 Wave4a: hamburger toggle ของ top-nav — ซ่อนไว้ default, โชว์เฉพาะ ≤320px (ดู media ท้าย block 768) */
.topnav-toggle { display:none; }
.topnav-link {
  display:inline-flex; align-items:center; gap:8px; padding:8px 12px; border-radius:10px;
  font-size:14px; color:var(--text-2); text-decoration:none; white-space:nowrap;
  background:none; border:none; cursor:pointer; font-family:inherit;
}
.topnav-link i.fi { font-size:16px; color:var(--text-2); }
.topnav-link:hover { background:var(--surface-2); color:var(--text); }
.topnav-link--active { background:color-mix(in srgb, var(--primary-solid) 8%, transparent); color:var(--text); font-weight:600; }
.topnav-link--active i.fi { color:var(--primary-solid); }
/* dropdown "จัดการ" */
.topnav-manage { position:relative; flex-shrink:0; }
.tm-caret { font-size:14px !important; transition:transform .15s; }
.topnav-manage.is-open .tm-caret { transform:rotate(180deg); }
.topnav-menu {
  position:absolute; top:calc(100% + 6px); left:0; z-index:50;
  min-width:220px; padding:8px; background:var(--surface);
  border:1px solid var(--border); border-radius:12px; box-shadow:0 6px 24px rgba(0,0,0,.14);
  display:flex; flex-direction:column; gap:2px;
  /* S49 nav restructure: dropdown "Menu" ตอนนี้มี 17 รายการ + 2 section head (~670px) — เกินความสูงจอหลายขนาด
     → cap ความสูง + scroll ในตัว กัน overlay ล้นขอบล่างจอ (ไม่กระทบ .ms-menu ของ module-switcher — คลาสเดียวกัน
     แต่ปกติรายการน้อยกว่า max-height ไม่มีผลจนกว่าจะเกิน) */
  max-height:min(72vh, calc(100vh - 80px)); overflow-y:auto; overscroll-behavior:contain;
}
.topnav-menu[hidden] { display:none; }
/* เส้นคั่น section ที่ 2 เป็นต้นไป (scope #topnav-menu กัน module-switcher .ms-menu ที่ reuse .ms-head อยู่ regress) */
#topnav-menu .ms-head:not(:first-child) { margin-top:6px; padding-top:8px; border-top:1px solid var(--border); }

/* ---- Global Search (FTS5) — topbar search + overlay results — S41 Wave 2 (เด) ---- */
.gs { position:relative; flex-shrink:1; min-width:0; }
.gs-field {
  display:flex; align-items:center; gap:8px; width:260px; max-width:32vw;
  padding:0 10px; height:38px; border-radius:10px;
  background:var(--surface-2); border:1px solid var(--border-control);
}
.gs.gs-open .gs-field { border-color:var(--primary-solid); background:var(--surface); }
.gs-field-icon { font-size:15px; color:var(--text-2); flex-shrink:0; }
.gs.gs-open .gs-field-icon { color:var(--primary-solid); }
.gs-input {
  flex:1 1 auto; min-width:0; border:none; background:none; outline:none;
  font-family:inherit; font-size:14px; color:var(--text);
}
.gs-input::placeholder { color:var(--text-2); }
.gs-kbd {
  flex-shrink:0; font-size:12px; line-height:1; color:var(--text-2);
  padding:2px 6px; border:1px solid var(--border-control); border-radius:6px; background:var(--surface);
}
.gs.gs-open .gs-kbd { display:none; }
/* spinner (htmx-indicator: htmx เติม .htmx-request ให้ #gs ตอนกำลังโหลด) */
.gs .gs-spin { display:none; font-size:14px; color:var(--primary-solid); flex-shrink:0; }
.gs.htmx-request .gs-spin { display:inline-block; animation:ie-spin 1s linear infinite; }
.gs.htmx-request .gs-kbd { display:none; }
.gs-clear {
  flex-shrink:0; width:24px; height:24px; display:flex; align-items:center; justify-content:center;
  border:none; background:none; color:var(--text-2); cursor:pointer; border-radius:6px; padding:0;
}
.gs-clear:hover { background:var(--surface-2); color:var(--text); }
.gs-clear i.fi { font-size:14px; }

.gs-panel {
  position:absolute; top:calc(100% + 8px); right:0; z-index:60;
  width:min(440px, 88vw); max-height:min(64vh, 520px); overflow-y:auto;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:var(--shadow-overlay); padding:6px;
}
.gs-panel[hidden] { display:none; }

.gs-hint, .gs-noresult {
  display:flex; flex-direction:column; align-items:center; gap:8px;
  padding:28px 16px; text-align:center; color:var(--text-2);
}
.gs-hint i.fi, .gs-noresult i.fi { font-size:26px; color:var(--text-2); }
.gs-hint p, .gs-noresult p { margin:0; font-size:14px; }
.gs-noresult p strong { color:var(--text); }
.gs-noresult-hint { font-size:12px; color:var(--text-2); }

.gs-group { padding:2px 0; }
.gs-group + .gs-group { border-top:1px solid var(--border); margin-top:4px; padding-top:6px; }
.gs-group-h {
  display:flex; align-items:center; gap:6px;
  padding:6px 10px 4px; font-size:12px; font-weight:600; color:var(--text-2);
  text-transform:uppercase; letter-spacing:.03em;
}
.gs-group-h i.fi { font-size:13px; }

.gs-hit {
  display:flex; align-items:center; gap:10px; padding:8px 10px;
  border-radius:8px; text-decoration:none; color:var(--text); cursor:pointer;
}
.gs-hit:hover, .gs-hit.gs-active {
  background:color-mix(in srgb, var(--primary-solid) 8%, transparent);
}
.gs-hit.gs-active { outline:2px solid var(--primary-solid); outline-offset:-2px; }
.gs-hit-icon {
  flex-shrink:0; width:28px; height:28px; border-radius:7px;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-2); color:var(--text-2);
}
.gs-hit-icon i.fi { font-size:14px; }
.gs-hit-body { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:1px; }
.gs-hit-title {
  font-size:14px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.gs-hit-snip {
  font-size:12px; color:var(--text-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.gs-hit-type {
  flex-shrink:0; font-size:11px; color:var(--text-2);
  padding:2px 7px; border-radius:999px; background:var(--surface-2);
}
.gs-hit mark, .gs-hit-title mark, .gs-hit-snip mark {
  background:color-mix(in srgb, var(--primary-light) 34%, transparent);
  color:inherit; padding:0 1px; border-radius:2px; font-weight:600;
}
@media (max-width:768px) {
  .gs-field { width:auto; max-width:none; }
  .gs-input { width:100%; }
  .gs-panel { width:min(440px, 92vw); }
}

/* hamburger (โชว์เฉพาะมือถือ — เปิด drawer overlay) — คลื่น 1 */
.topbar-menu {
  display:none; width:44px; height:44px; aspect-ratio:1/1; flex-shrink:0;
  border-radius:10px; border:1px solid var(--border); background:var(--surface); color:var(--text);
  align-items:center; justify-content:center; cursor:pointer; font-size:16px;
}

.user-chip { display:flex; align-items:center; gap:8px; }
.avatar {
  width:36px; height:36px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border-radius:999px; background:var(--primary); color:#fff;
  font-size:var(--fs-md); font-weight:600;
}
.user-meta { display:flex; flex-direction:column; line-height:1.2; min-width:0; }
.user-name { font-size:var(--fs-sm); font-weight:500; }
.user-role { font-size:11px; color:var(--text-2); }

.shell-main {
  flex:1; display:flex; align-items:center; justify-content:center; padding:32px;
}
.empty-card {
  width:100%; max-width:520px; text-align:center;
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:48px 32px;
  display:flex; flex-direction:column; align-items:center; gap:16px;
  box-shadow:var(--shadow-card);
}
.empty-icon {
  width:64px; height:64px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border-radius:999px; background:color-mix(in srgb, var(--green) 12%, transparent); color:var(--green-text); font-size:28px; /* S27 P0: icon 1.84:1 fail — green-text (AA) */
}
.empty-card h2 { font-size:18px; font-weight:600; }
.empty-sub { font-size:14px; color:var(--text-2); }
.empty-note {
  display:inline-flex; align-items:center; gap:8px;
  font-size:12px; color:var(--text-2);
  background:var(--surface-2); border-radius:12px; padding:8px 16px;
}

.brand-link { display:flex; align-items:center; gap:12px; text-decoration:none; color:var(--text); }

/* ============ APP LAYOUT (drawer + main) — คลื่น 1 App Shell ============ */
/* --drawer-w = ความกว้างจริง · --drawer-w-user = ค่าที่ user ลากไว้ (JS set จาก localStorage) */
.app-layout { flex:1; display:flex; min-height:0; position:relative; --drawer-w:var(--drawer-w-user,280px); }
html[data-drawer-collapsed="1"] .app-layout { --drawer-w:0px; }

/* ---------- Left drawer (App Shell — ร่วมทุกหน้า) ---------- */
.drawer {
  width:var(--drawer-w); flex-shrink:0;
  background:var(--surface); border-right:1px solid var(--border);
  display:flex; flex-direction:column; overflow:hidden;
  transition:width .2s ease;
}
html[data-drawer-collapsed="1"] .drawer { border-right:none; }

.dh-title { font-size:12px; font-weight:600; color:var(--text-2); text-transform:uppercase; letter-spacing:.04em; }

/* เมนูหลัก (บน) + section จัดการ (ล่างปัก) */
.drawer-nav { flex-shrink:0; padding:8px; display:flex; flex-direction:column; gap:2px; }
/* N3: เส้นคั่น "มุมมองรวม" (cross-view) กับหมวด Spaces — pattern เดียวกับ .drawer-manage */
.drawer-sec-head { padding:12px 16px 4px; margin-top:4px; border-top:1px solid var(--border); }
.drawer-manage { flex-shrink:0; padding:8px; border-top:1px solid var(--border); display:flex; flex-direction:column; gap:2px; }
/* S10 — section จัดการ กางหุบได้ (ปุ่มหัว + body พับ) */
.drawer-sec-toggle {
  display:flex; align-items:center; justify-content:space-between; width:100%;
  background:none; border:none; cursor:pointer; text-align:left;
}
.drawer-sec-toggle .dh-caret { color:var(--text-2); font-size:16px; transition:transform .15s; }
.drawer-manage.collapsed .dh-caret { transform:rotate(-90deg); }
.drawer-manage-body { display:flex; flex-direction:column; gap:2px; }
.drawer-manage.collapsed .drawer-manage-body { display:none; }
.dnav {
  display:flex; align-items:center; gap:10px; padding:8px 12px;
  border-radius:8px; font-size:14px; color:var(--text); text-decoration:none; overflow-wrap:anywhere;
}
.dnav i.fi { width:20px; aspect-ratio:1/1; flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:16px; color:var(--text-2); }
.dnav:hover { background:var(--surface-2); }
/* active = text สีปกติ + หนา (contrast AA บน tint) · คง tint bg + icon แดง เป็น affordance */
.dnav--active { background:color-mix(in srgb, var(--primary-solid) 8%, transparent); color:var(--text); font-weight:600; }
.dnav--active i.fi { color:var(--primary-solid); }

/* scroll region เดียว: Spaces tree + เมนูจัดการ เลื่อนด้วยกัน (nav บนปักอยู่กับที่)
   — แก้ bug เมนูจัดการ (shrink:0) เดิมบีบ space tree จนเหลือช่องจิ๋วบนจอเตี้ย */
.drawer-scroll { flex:1; min-height:0; overflow-y:auto; display:flex; flex-direction:column; }
.drawer-body { padding:4px 8px 8px; }
.drawer-empty { padding:16px; font-size:12px; color:var(--text-2); }
.drawer-empty--sm { padding:6px 12px; font-size:12px; }

/* ---------- M1.5 (DEC-105) — Project Group tier (sidebar) + จัดการ Group (zone A) ---------- */
/* section head + ปุ่มสร้างโปรเจค (ขวาสุด) — flex, ไม่แตะ .drawer-sec-head เดิม */
.drawer-sec-head--act { display:flex; align-items:center; justify-content:space-between; gap:8px; }
.dh-add {
  flex-shrink:0; width:24px; height:24px; border-radius:6px; padding:0; position:relative;
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; color:var(--text-2); cursor:pointer; font-size:14px; transition:color .15s, background .15s;
}
.dh-add::after { content:''; position:absolute; inset:-10px; } /* hit-area ≥44px (DS §0.10) */
.dh-add:hover { color:var(--primary-solid); background:var(--surface-2); }
/* Project Group tier — group เด่นกว่า project (weight 500) · ไม่มี left-accent (DS §0.9) */
.dnode--pg { font-weight:500; }
.dname--pg { flex:1; min-width:0; font-weight:500; color:var(--text); font-size:14px; overflow-wrap:anywhere; }
.dnode--pg .dicon { color:var(--text-2); }
.dgroup--pg > .dnode--pg:hover { background:var(--surface-2); }
/* project (=space) เยื้อง 1 ชั้นใต้ group (reuse indent เดิม + นัดจากขอบ group) */
.dgroup--pg > .dchildren { padding-left:8px; }
/* ปุ่ม "+ กลุ่มโปรเจค" ล่างสุด section (ghost link ไป /spaces#pg-manage) */
.pg-add-link {
  display:flex; align-items:center; gap:8px; margin-top:6px; padding:6px 12px;
  border-radius:8px; font-size:13px; color:var(--text-2); text-decoration:none;
}
.pg-add-link i.fi { font-size:13px; }
.pg-add-link:hover { background:var(--surface-2); color:var(--primary-solid); }

/* zone A — จัดการ Project Group (หน้า /spaces) */
.pg-manage { margin:12px 0 20px; }
.pg-manage-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:12px; }
.pg-manage-title { display:flex; align-items:center; gap:8px; font-size:16px; font-weight:600; margin:0; }
.pg-manage-title i.fi { color:var(--primary-solid); font-size:15px; }
.pg-manage-sub { margin:4px 0 0; font-size:13px; color:var(--text-2); max-width:52ch; }
.pg-add { display:flex; align-items:center; gap:8px; flex-shrink:0; }
.pg-add .input { width:200px; }
.pg-tree-title { display:flex; align-items:center; gap:8px; font-size:16px; font-weight:600; margin:20px 0 8px; }
.pg-tree-title i.fi { color:var(--primary-solid); font-size:15px; }
.pg-list { display:flex; flex-direction:column; gap:6px; }
.pg-row {
  display:flex; align-items:center; gap:10px; padding:10px 12px;
  border:1px solid var(--border); border-radius:10px; background:var(--surface);
}
.pg-row-icon { flex-shrink:0; width:24px; height:24px; display:flex; align-items:center; justify-content:center; color:var(--text-2); font-size:14px; }
.pg-row-name { flex:1; min-width:0; font-size:14px; font-weight:500; overflow-wrap:anywhere; }
.pg-row-count { flex-shrink:0; font-size:12px; color:var(--text-2); }
.pg-row-sys { flex-shrink:0; font-size:12px; color:var(--text-2); font-style:italic; }
.pg-row-act { display:flex; align-items:center; gap:2px; flex-shrink:0; }

/* ---------- FLOW-4 (DEC-105 §10): group card + project chip + drop-zone (drag ย้ายกลุ่ม) ---------- */
/* การ์ดกลุ่ม = drop container (หัว card เหมือน pg-row เดิม + แถว chip โปรเจคใต้หัว) · สี/เส้นจาก token (light+dark) */
.pg-card {
  border:1px solid var(--border); border-radius:10px; background:var(--surface);
  transition:border-color .12s, background-color .12s;
}
.pg-card-head { display:flex; align-items:center; gap:10px; padding:10px 12px; }
.pg-chips { display:flex; flex-wrap:wrap; gap:6px; padding:0 12px 10px 12px; }
.pg-chip {
  display:inline-flex; align-items:center; gap:2px; max-width:100%;
  padding:3px 10px 3px 4px; border:1px solid var(--border-control); border-radius:999px;
  background:var(--surface-2); font-size:13px;
}
.pg-chip-name { min-width:0; overflow-wrap:anywhere; }
/* handle = ปุ่ม (mirror .dragh) — draggable เมาส์ + กดเปิดเมนู (keyboard/touch) */
.pg-chip-drag { cursor:grab; flex:none; }
.pg-chip-drag:active { cursor:grabbing; }
.pg-chip--drag { opacity:.5; }
.pg-chips-empty { font-size:12px; color:var(--text-2); font-style:italic; padding:2px 0; }
/* drop-zone ปลายทาง (❌ ไม่ใช้ left-accent) — border primary + tint จาก token (derive ไม่ hardcode) */
.pg-card--dropzone {
  border-color:var(--primary-solid);
  background:color-mix(in srgb, var(--primary-solid) 6%, var(--surface));
}

/* ---------- ติ่งพับ/กาง (desktop) — แขวนขอบขวา divider เห็น 2 สถานะ ---------- */
/* left:var(--drawer-w) + translateY เท่านั้น → pill แขวน "ขวา" ของเส้นแบ่ง ไม่คร่อมโซนลาก resize
   (แก้ #3: กลางจอลาก resize ได้ ไม่โดนพับ) · collapsed → left:0 โผล่เต็ม 16px (แก้ #7) */
.drawer-handle {
  position:absolute; top:50%; left:var(--drawer-w); transform:translateY(-50%);
  width:16px; height:48px; border-radius:8px;
  background:var(--surface); border:1px solid var(--border);
  box-shadow:0 1px 4px rgba(0,0,0,.08);
  display:flex; align-items:center; justify-content:center;
  color:var(--text-2); cursor:pointer; z-index:20; padding:0;
  transition:left .2s ease, color .15s, border-color .15s;
}
/* ขยาย hit-area ≥44px โดยไม่ขยาย visual (แก้ #5) — pseudo คลิกนับเป็นปุ่ม */
.drawer-handle::after { content:''; position:absolute; inset:-8px -14px -8px 0; }
.drawer-handle:hover { color:var(--primary-solid); border-color:var(--primary-solid); }
.drawer-handle i { transition:transform .2s; font-size:14px; }
html[data-drawer-collapsed="1"] .drawer-handle i { transform:rotate(180deg); }

/* ---------- resizer: ลากปรับกว้าง (Q3 — pattern reuse card detail คลื่น 2) ---------- */
/* left:var(--drawer-w) (นอกขอบ drawer) → ไม่ทับ scrollbar ของ .drawer-body (แก้ #8)
   z สูงกว่า handle → ลากได้ทั้งแนวรวมโซนกลางที่ handle เคยบัง (แก้ #3) */
.drawer-resizer {
  position:absolute; top:0; bottom:0; left:var(--drawer-w);
  width:6px; cursor:col-resize; z-index:25;
  background:var(--primary-solid); opacity:0; transition:opacity .15s;
}
.drawer-resizer:hover, .drawer-resizer.is-dragging { opacity:.25; }
html[data-drawer-collapsed="1"] .drawer-resizer { display:none; }
body.no-select { user-select:none; }
/* ตอนลาก (มี .no-select) ตัด transition ทุกส่วน = ไม่หน่วง rubber-band (แก้ #2 · reuse คลื่น 2) */
body.no-select .drawer,
body.no-select .drawer-handle,
body.no-select .drawer-resizer { transition:none; }

.drawer-backdrop { display:none; }

.dgroup.collapsed > .dchildren { display:none; }
.dgroup.collapsed > .dnode .dcaret i { transform:rotate(-90deg); }

.dnode {
  display:flex; align-items:center; gap:6px;
  padding:6px 8px; border-radius:8px; cursor:default; min-width:0;
}
.dnode:hover { background:var(--surface-2); }
.dcaret {
  position:relative;
  width:20px; height:20px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; color:var(--text-2); font-size:12px; padding:0;
}
.dcaret::after { content:''; position:absolute; inset:-2px; } /* hit-area 24px (แก้ #5) */
.dcaret i { transition:transform .15s; }
.dcaret--ghost { visibility:hidden; }
.dicon {
  width:20px; height:20px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  color:var(--text-2); font-size:14px;
}
.dnode--space .dicon { color:var(--primary-solid); }
.dname {
  flex:1; min-width:0; font-size:14px; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.dnode--space .dname { font-weight:500; }
.dcount {
  flex-shrink:0; font-size:11px; font-weight:500; color:var(--text-2);
  background:var(--surface-2); border-radius:999px; padding:2px 8px; min-width:22px; text-align:center;
}

/* ไฮไลต์ list ที่กำลังเปิด (tint แดง ไม่ใช้แถบซ้าย — DS #9) — คลื่น 1 */
.dnode--active { background:color-mix(in srgb, var(--primary-solid) 8%, transparent); }
.dnode--active .dname { color:var(--text); font-weight:600; }
.dnode--active .dicon { color:var(--primary-solid); }

/* focus-visible กลาง (แก้ #4) — keyboard เห็น ring แบรนด์ ไม่ใช่ ring น้ำเงิน browser */
.dnav:focus-visible,
.drawer-handle:focus-visible,
.topbar-menu:focus-visible,
.dcaret:focus-visible,
.theme-toggle:focus-visible,
.btn-ghost:focus-visible,
.btn-tertiary:focus-visible,
.dd-trigger:focus-visible,
.seg-link:focus-visible,
.pv-chip-x:focus-visible,
.pv-aggbtn:focus-visible,
.pv-addbtn:focus-visible,
.pv-tog:focus-visible,
.pv-actbtn:focus-visible,
.grp-add:focus-visible,
.bd-col-add:focus-visible,
.dname--link:focus-visible,
.iconbtn:focus-visible,
.btn-danger:focus-visible,
.dragh:focus-visible,
.chip--link:focus-visible,
.btn-primary:focus-visible {
  outline:2px solid var(--primary-solid); outline-offset:2px;
}

/* ---------- Main area ---------- */
.app-main { flex:1; overflow-y:auto; display:flex; align-items:center; justify-content:center; padding:32px; }

/* ============ MANAGE SPACES PAGE ============ */
/* B1: เอา max-width:960px + margin:0 auto ออก → content ยืดเต็มพื้นที่ที่เหลือจาก sidebar (พีสั่ง)
   หน้าที่ต้อง cap เพื่อ readability = opt-in ผ่าน modifier เฉพาะหน้า (.page--wide/.page--card) ไม่ใช่ global
   padding:32px คงไว้ให้ content หายใจ ไม่ชิดขอบ · scrollbar-gutter กันเนื้อกระตุกตอน scrollbar โผล่/หาย */
.page { flex:1; overflow-y:auto; padding:32px; width:100%; scrollbar-gutter:stable; }

/* Custom scrollbar (B2) — บาง มน สีจาง ใช้ token (light+dark) ไม่เทอะทะ/ไม่เด่นชิดขอบ
   border โปร่งใส + background-clip:padding-box = thumb ผอมลอยกลางราง 8px ดูสะอาดแบบ dashboard */
.page, .drawer-scroll { scrollbar-width:thin; scrollbar-color:var(--scrollbar-thumb) transparent; }
.page::-webkit-scrollbar,
.drawer-scroll::-webkit-scrollbar { width:8px; height:8px; }
.page::-webkit-scrollbar-track,
.drawer-scroll::-webkit-scrollbar-track { background:transparent; }
.page::-webkit-scrollbar-thumb,
.drawer-scroll::-webkit-scrollbar-thumb {
  background:var(--scrollbar-thumb); border-radius:999px;
  border:2px solid transparent; background-clip:padding-box;
}
.page::-webkit-scrollbar-thumb:hover,
.drawer-scroll::-webkit-scrollbar-thumb:hover {
  background:var(--scrollbar-thumb-hover); background-clip:padding-box;
}
.page-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:16px; }
.page-head h1 { font-size:24px; font-weight:600; }
.page-sub { font-size:var(--fs-sm); color:var(--text-2); margin-top:4px; }

.callout {
  display:flex; align-items:flex-start; gap:8px;
  background:var(--surface-2); border-radius:12px; padding:12px 16px;
  font-size:var(--fs-sm); color:var(--text-2); margin-bottom:16px;
}
.callout > i { color:var(--primary-solid); margin-top:2px; flex-shrink:0; }
.callout-role { margin-top:4px; }

.tree {
  background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:8px; box-shadow:var(--shadow-card);
}
.tree-empty { padding:32px; text-align:center; color:var(--text-2); font-size:14px; display:flex; align-items:center; justify-content:center; gap:8px; }

.tnode {
  display:flex; align-items:center; gap:8px;
  padding:8px 12px; border-radius:10px; min-width:0;
}
.tnode:hover { background:var(--surface-2); }
.tnode:hover .tact { opacity:1; }
.ticon {
  width:24px; height:24px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center; color:var(--text-2); font-size:14px;
}
.tnode--space .ticon { color:var(--primary-solid); }
.tbadge {
  flex-shrink:0; font-size:var(--fs-micro); font-weight:600; text-transform:uppercase; letter-spacing:.03em; /* S27 Batch2: 10px ต่ำกว่า Micro floor (DS §3) → 11px */
  border-radius:6px; padding:2px 8px; color:var(--text-2); background:var(--surface-2);
}
.tbadge--space { color:color-mix(in srgb, var(--primary-solid) 55%, var(--text)); background:color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.tname { flex:1; min-width:0; font-size:14px; overflow-wrap:anywhere; }
.tnode--space .tname { font-weight:500; }
.tmeta { flex-shrink:0; font-size:12px; color:var(--text-2); }

.tact { display:flex; align-items:center; gap:2px; flex-shrink:0; opacity:0; transition:opacity .15s; }
.iconbtn {
  position:relative;
  width:30px; height:30px; aspect-ratio:1/1; flex-shrink:0;
  display:inline-flex; align-items:center; justify-content:center;
  background:none; border:none; color:var(--text-2);
  border-radius:8px; font-size:14px;
}
/* S27 P0 (audit-css #3 / audit-a11y M6-adjacent): 30px visual แต่ hit-area ต้อง ≥44px (WCAG 2.5.5)
   — ::after ขยาย invisible tap area โดยไม่ขยาย icon จริง (pattern เดียวกับ .drawer-handle/.dcaret) */
.iconbtn::after { content:''; position:absolute; inset:-7px; }
.iconbtn:hover { background:var(--surface); color:var(--primary-solid); box-shadow:0 0 0 1px var(--border); }
.iconbtn--danger:hover { color:var(--alert); }
.iconbtn .mini { position:absolute; right:2px; bottom:2px; font-size:8px; }

/* ============ MODAL (centered desktop / bottom-sheet mobile) ============ */
.modal-backdrop {
  position:fixed; inset:0; z-index:2000;
  background:rgba(0,0,0,.4);
  display:flex; align-items:center; justify-content:center; padding:24px;
}
.modal-box {
  width:100%; max-width:440px;
  background:var(--surface); border:1px solid var(--border);
  border-radius:20px; overflow:hidden;
  display:flex; flex-direction:column; max-height:90vh;
  box-shadow:0 8px 32px rgba(0,0,0,.24);
}
.modal-header {
  flex-shrink:0; display:flex; align-items:center; justify-content:space-between;
  gap:16px; padding:20px 20px 12px;
}
.modal-header h2 { font-size:18px; font-weight:600; }
.modal-body { overflow-y:auto; flex:1; padding:4px 20px 20px; display:flex; flex-direction:column; gap:16px; }
.modal-footer {
  flex-shrink:0; display:flex; align-items:center; justify-content:flex-end; gap:8px;
  padding:16px 20px; border-top:1px solid var(--border);
}
/* S56 N4: tertiary action ใน modal-footer — น้ำหนักต่ำกว่า ghost (ไม่มีกรอบ/พื้น) + ชิดซ้ายแยกกลุ่ม
   จาก ยกเลิก/ยืนยัน (ปุ่มทางออก "ไปฟอร์มเต็ม" ไม่ควรดูเท่ากับปุ่มยกเลิก) · hit area ยัง ≥44px */
.btn-tertiary {
  display:inline-flex; align-items:center; gap:8px;
  background:none; border:none; color:var(--primary-aa);
  padding:8px 8px; min-height:44px; font-size:14px; font-weight:500;
  border-radius:12px; text-decoration:none;
}
.btn-tertiary:hover { background:var(--surface-2); text-decoration:underline; }
.btn-tertiary i.fi { color:inherit; }
.modal-footer .btn-tertiary:first-child { margin-right:auto; }
.confirm-text { font-size:14px; }
.confirm-note { font-size:12px; color:var(--text-2); }

/* S27 Batch3 (DEC-068#1 §6.6): #dialog slot = native <dialog> host (focus trap/Esc/คืน focus).
   host โปร่งใส/ไร้กรอบ — reset UA default ของ <dialog>; ลูก .modal-backdrop ทำ dim+center เหมือนเดิม
   → ตัด ::backdrop ซ้ำ (dim อยู่บน .modal-backdrop). closed+empty = display:none (UA) ไม่กินพื้นที่ */
dialog#dialog.modal-host {
  padding:0; border:none; margin:0; background:transparent; color:inherit;
  max-width:none; max-height:none; width:auto; height:auto; overflow:visible;
}
dialog#dialog.modal-host::backdrop { background:transparent; }

/* Archive round 2 — section header ในหน้ากรุ (container + การ์ด) */
.arch-sec-head { display:flex; align-items:center; gap:8px; font-weight:600; color:var(--text-2);
  font-size:14px; margin:24px 0 8px; padding-bottom:8px; border-bottom:1px solid var(--border); }
.arch-sec-head:first-child { margin-top:0; }

/* .dy-row layout — list-row มาตรฐาน (ใช้ร่วม trash/risk/archive; B1 รุมเทส เต๋ — เดิมไม่มี CSS) */
.dy-rows { display:flex; flex-direction:column; gap:8px; }
.dy-row { display:flex; justify-content:space-between; align-items:flex-start; gap:16px;
  padding:16px; border:1px solid var(--border); border-radius:var(--r); background:var(--surface); }
.dy-main { flex:1; min-width:0; }
.dy-act { flex-shrink:0; display:flex; align-items:center; gap:8px; }

.btn-danger {
  display:inline-flex; align-items:center; gap:8px;
  /* S27 P0 (audit B4): ขาวบน --alert (#FF453A) = 3.41:1 fail AA — เดิมลองใช้ --alert-text ตามโจทย์
     แต่ verify browser จริงพบ dark variant (#FF6961) ขาวทับ = 2.82:1 ยัง fail (--alert-text ออกแบบมา
     เป็น text-on-surface ไม่ใช่ bg-for-white-text) → ใช้ --alert-aa แทน (ค่าคงที่ไม่ผันตาม theme,
     pattern เดียวกับ --primary-aa) ผ่าน AA จริงทั้ง 2 mode (5.65:1) */
  background:var(--alert-aa); color:#fff; border:none;
  border-radius:12px; padding:12px 24px; font-size:14px; font-weight:600;
  text-decoration:none; /* S56 poka-yoke: ครบทุก .btn-* เผื่อถูกใช้เป็น <a> (เหมือน primary/ghost) */
  transition:filter .15s;
}
.btn-danger:hover { filter:brightness(1.06); }
/* disabled (S25 M1) — decision 2: ปุ่มลบ release ที่ยังมีการ์ดผูก ต้องเห็นชัดว่ากดไม่ได้ */
.btn-danger:disabled { opacity:.5; cursor:not-allowed; filter:none; }

/* ============ nav compaction ≤1439px (Tablet + "desktop เล็ก" ทั่วไป (1280/1366 laptop จริง) + Mobile
   <768) — S42 followup ============
   Bug เดิมที่รายงาน: top nav เต็ม label ไม่ยุบจนกว่าจะถึง 768px เดิม → ช่วง 768-1023 ไม่มีการยุบเลย
   ตกบรรทัด (พบจริงที่ ~800-830px)
   ⚠️ วัดจริงด้วย CDP (getBoundingClientRect, ไม่เดาจากตา) พบว่า breakpoint 1023 ตาม DS §6 desktop≥1024
   ไม่พอ: full-text nav (5 item + "จัดการ") ต้องการพื้นที่จริง ~513px นับจากขอบซ้าย nav ซึ่งไปทับ/ซ่อนอยู่
   หลัง search box จริงจนถึง viewport 1366px (จอ laptop ทั่วไป!) — 1280 ยังเหลื่อมทับ 41px (ตัวอักษร
   "จัดการ" ถูกซ่อนบางส่วน), 1366 ยังเหลื่อม 19px, พึ่งพอดีไม่ทับที่ 1400 (margin แคบมาก เสี่ยง font
   render ต่างเครื่อง) → เลือก breakpoint ที่ verify แล้วปลอดภัยจริง 1439px (เผื่อ margin, ตรงกับค่า
   breakpoint 1440 ที่ design system หลายเจ้าใช้เป็น "small desktop" cutoff อยู่แล้ว) แทนการยึด 1023
   ตรงตัว — ตาม DS หลักการ "ปรับ breakpoint ตามหลักฐานจริง ไม่ใช่ยึดเลข" (Wegital DS §0)
   ครอบคลุม tablet 768-1023 + "desktop เล็ก" 1024-1439 + mobile ≤768 ในบล็อกเดียว กัน duplicate/conflict
   กับ block มือถือด้านล่างที่เติมเฉพาะส่วนเสริม (overflow-x/scroll fallback + brand compaction เพิ่ม) */
@media (max-width:1439px) {
  .topbar-nav { gap:2px; }
  /* padding 14px (ไม่ใช่ 8px ของ mobile เดิม) — ให้ hit-area ถึง 44×44px จริง (DS §0.10) */
  .topnav-link { padding:14px; }
  .topnav-link > span { display:none; }
  .topnav-menu .dnav span { display:inline; }
  /* คืนพื้นที่ฝั่ง brand/right — ซ่อน label ที่ไม่จำเป็นตอนจอแคบลง (scope เฉพาะ topbar กัน
     กระทบ .btn-ghost/label อื่นทั้งแอป) */
  .module-switcher .ms-label { display:none; }
  .topbar-right .user-meta { display:none; }
  .topbar-right .btn-ghost span { display:none; }
}

/* mobile */
@media (max-width:768px) {
  .topbar { padding:12px 16px; gap:8px; }
  .topbar-menu { display:inline-flex; }
  .user-meta { display:none; }
  .btn-ghost span { display:none; }
  .tl-zoom-level { display:none; }  /* G1 (S34): จอแคบ เหลือ [−][+] ติดกัน (ป้าย level ซ่อน · fit label ซ่อนตาม .btn-ghost span) */
  /* REQ-S12 ข้อ 2: top nav บนมือถือ = icon-only (ประหยัดพื้นที่) + เลื่อนแนวนอนได้ · เมนูใน dropdown คงป้าย
     (padding/span-hide สืบมาจาก block ≤1023 ด้านบนแล้ว — เหลือแค่ overflow-x:auto ที่มือถือต้องการเพิ่ม
     เพราะ 5 pill ยังกว้างกว่าที่เหลือจริงที่จอ ≤768; ไม่ซ้ำ .topnav-link/span ที่นี่ กัน override 14px padding) */
  .topbar-nav { gap:2px; overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .page { padding:16px; }
  .dnav { padding-top:12px; padding-bottom:12px; } /* touch target ~44px (แก้ #5) */
  .tact { opacity:1; } /* มือถือไม่มี hover — โชว์ปุ่มเสมอ */
  /* S42 followup — pre-existing bug พบระหว่าง verify: topbar-right (search) เดิมไม่หด (flex-shrink:0)
     บีบ .topbar-nav จน width:0 ที่ ~375px (nav หายทั้งแถบ ไม่ใช่แค่ wrap) → คืนพื้นที่ฝั่ง brand เพิ่ม
     (ซ่อนชื่อ WeTeam/WeFin ตัวอักษร เหลือแค่ไอคอน — ยังคง product identity ผ่านไอคอน) */
  .brand-link strong { display:none; }
  .ms-divider { display:none; }
  /* กันไม่ให้ nav ถูกบีบเหลือ 0 เด็ดขาด — มี floor ให้เห็น/เลื่อนถึงอย่างน้อย ~2 ไอคอน */
  .topbar-nav { min-width:100px; }
  /* search บนมือถือ: ให้หดตามจริง (topbar-right หดได้แล้ว) แต่ตั้ง min-width 44px กัน touch target
     ต่ำกว่ามาตรฐาน DS §0.10 (ทดสอบแล้วยังพอดี ไม่ล้นที่ 375px — ดูรายงาน verify) */
  .gs-field { min-width:44px; }

  /* drawer = overlay สไลด์ซ้าย (DS §6.3) — ติ่ง/resizer ปิด, ใช้ hamburger+backdrop */
  .drawer-handle, .drawer-resizer { display:none; }
  .app-layout { --drawer-w:0px; }
  .drawer {
    position:fixed; top:0; left:0; bottom:0; width:min(86vw,320px);
    border-right:1px solid var(--border);
    transform:translateX(-100%); transition:transform .25s ease; z-index:1500;
  }
  body.drawer-open .drawer { transform:none; }
  .drawer-backdrop {
    display:block; position:fixed; inset:0; background:rgba(0,0,0,.4);
    z-index:1400; opacity:0; visibility:hidden; transition:opacity .2s;
  }
  body.drawer-open .drawer-backdrop { opacity:1; visibility:visible; }

  /* modal → bottom sheet */
  .modal-backdrop { align-items:flex-end; padding:0; }
  .modal-box { max-width:100%; border-radius:20px 20px 0 0; }
  .cf-grid { grid-template-columns:1fr; }
  .fc-act { opacity:1; }
}

/* ============ S41 Wave4a: mobile nav hamburger — จอแคบมาก ≤320px ============
   จอ ≥768 = nav เต็ม (เดิมเป๊ะ) · 320<จอ≤768 = nav icon-only เลื่อนแนวนอน (เดิมเป๊ะ, block 768 ด้านบน)
   ≤320px = nav ยุบเป็น hamburger dropdown (block นี้อยู่ "หลัง" block 768 → override .topbar-nav ได้ specificity เท่ากัน) */
@media (max-width:320px) {
  .topbar { position:relative; flex-wrap:wrap; }
  .topnav-toggle {
    display:inline-flex; align-items:center; justify-content:center; flex-shrink:0;
    width:40px; height:40px; padding:8px; border-radius:10px;
    background:none; border:none; cursor:pointer; color:var(--text); font-family:inherit;
  }
  .topnav-toggle i.fi { font-size:18px; color:inherit; }
  .topnav-toggle:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
  /* nav ยุบ = dropdown เต็มความกว้างใต้ topbar · default ซ่อน เปิดด้วย .is-open (toggleTopNav) */
  .topbar-nav {
    display:none; position:absolute; top:100%; left:0; right:0; z-index:1300;
    flex-direction:column; align-items:stretch; gap:2px; margin:0; padding:8px;
    max-height:72vh; overflow-y:auto;
    background:var(--surface); border-bottom:1px solid var(--border);
    box-shadow:0 6px 16px rgba(0,0,0,.12);
  }
  .topbar-nav.is-open { display:flex; }
  /* ใน dropdown: label เต็ม (override icon-only ของ block 768) + hit-area 44px */
  .topbar-nav .topnav-link { width:100%; padding:12px; }
  .topbar-nav .topnav-link > span { display:inline; }
  .topbar-nav .topnav-manage { width:100%; }
  .topbar-nav .topnav-mbtn { width:100%; justify-content:flex-start; padding:12px; }
}

/* ============ CUSTOM FIELD (เฟส 2.2) ============ */
.fields-wrap { display:flex; flex-direction:column; gap:24px; }
.cf-section { display:flex; flex-direction:column; gap:12px; }
.cf-section-head { display:flex; align-items:center; gap:8px; }
.cf-section-head h2 { font-size:16px; font-weight:600; }
.cf-section-head > i.fi {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px;
}
.cf-section-hint { font-size:12px; color:var(--text-2); font-weight:400; }
.cf-spacefilter { display:flex; align-items:center; flex-wrap:wrap; gap:8px; }
.cf-filter-label { font-size:12px; font-weight:500; color:var(--text-2); }
.chip-nav {
  font-size:var(--fs-sm); font-weight:500; text-decoration:none;
  color:var(--text-2); background:var(--surface-2);
  border:1px solid var(--border); border-radius:999px; padding:6px 14px;
  transition:color .15s, background .15s;
}
.chip-nav:hover { color:var(--text); }
.chip-nav--on { color:#fff; background:var(--primary); border-color:transparent; }

.cf-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:12px; }
.cf-empty, .cf-noopt { font-size:var(--fs-sm); color:var(--text-2); padding:12px; }

.field-card {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:16px; display:flex; flex-direction:column; gap:12px;
  box-shadow:var(--shadow-card);
}
.fc-top { display:flex; align-items:flex-start; gap:12px; }
.fc-icon {
  width:36px; height:36px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface-2); color:var(--text-2); border-radius:10px; font-size:16px;
}
.fc-title { flex:1; min-width:0; }
.fc-name { font-size:var(--fs-md); font-weight:600; display:flex; align-items:center; gap:6px; overflow-wrap:anywhere; }
.fc-lock { color:var(--text-2); font-size:12px; display:inline-flex; }
.fc-badges { display:flex; flex-wrap:wrap; gap:6px; margin-top:6px; }
.fc-act { display:flex; align-items:center; gap:2px; flex-shrink:0; opacity:0; transition:opacity .15s; }
.field-card:hover .fc-act { opacity:1; }
/* ---- option summary strip บนการ์ด (redesign ยู — แทน chip cloud) ---- */
.fc-optsum { display:flex; align-items:center; gap:10px; padding-top:12px; border-top:1px solid var(--border); }
.fc-optcount { font-size:12px; font-weight:500; color:var(--text-2); flex-shrink:0; }
.fc-dots { display:flex; align-items:center; gap:4px; flex:1; min-width:0; overflow:hidden; }
.fc-dot { width:12px; height:12px; border-radius:999px; background:var(--chip-c); flex-shrink:0; }
.fc-dot-more { font-size:11px; color:var(--text-2); flex-shrink:0; }
.fc-manage { flex-shrink:0; }
.fc-noopt {
  font-size:12px; color:var(--text-2); padding-top:12px; border-top:1px solid var(--border);
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
}
.fc-manage-empty { flex-shrink:0; }

.badge { font-size:11px; font-weight:500; border-radius:999px; padding:2px 10px; }
.badge--type { color:var(--text-2); background:var(--surface-2); }
.badge--cascade { color:var(--alert-aa); background:color-mix(in srgb, var(--primary-solid) 10%, transparent); } /* S27-a11y (เอ): badge bg เป็น primary-solid 10% tint ทับ --surface (ไม่ใช่ขาวล้วน) -> primary-solid text 4.43:1 ตกจริงเหลือ ~3.9-4.4:1 ขึ้นกับ theme, primary-aa เองก็ยังเฉียด/ตก (~4.30-4.42:1) บน bg tint นี้ -> ใช้ alert-aa (~4.77-4.91:1 บน bg tint, คำนวณจริง) ผ่านชัวร์ */
[data-theme="dark"] .badge--cascade { color:var(--primary-light); } /* เข้ม alert-aa บนพื้นมืด fail -> primary-light สว่าง 5.8:1+ ผ่าน */
/* --warm ดิบ (#FFB800) เป็นตัวอักษรบนพื้นเหลืองอ่อน = fail AA (1.60:1 light). ใช้ token text-safe
   --warm-text/--warn-bg (pattern เดียวกับ .hp-badge--yellow) → light 5.41:1 / dark 7.42:1 ผ่าน AA (S25 B1) */
.badge--req { color:var(--warm-text); background:var(--warn-bg); }

/* ---- colored chip (DEC-055) — bg/fg จาก color-mix → auto light/dark ---- */
/* S52 (ซี — bug vertical-text, instance ที่ 3 พบระหว่าง verify .tk-title/.tk-list fix บน L1294/1323): เจอ
   pattern เดียวกันเป๊ะใน .lb-chip (inline-flex + overflow-wrap:anywhere + raw text หลายคำ เช่น "Team Meeting")
   ที่ container แคบมาก (.tk-meta ยุบ width:0 ที่ 800px แถวเดียวกับที่ card-title เคยพัง) → ยุบตัวอักษรทีละ
   บรรทัดเหมือนกัน · .lb-chip reuse กว้างมาก (status/priority/custom-field chip ทั้งระบบ) จึงแก้แค่ anywhere→
   break-word (ไม่ใช่ ellipsis — เพราะ .lb-chip ไม่ได้ตั้งใจให้บรรทัดเดียวเสมอ หลายที่ยอม wrap 2 บรรทัดอยู่แล้ว
   เป็นปกติ) กัน min-content ยุบเกินจริงแบบเดียวกับ .tk-title โดยไม่เปลี่ยนพฤติกรรม wrap ปกติที่มีอยู่แล้วเลย
   (ปลอดภัยกว่า ellipsis ที่จะซ่อนข้อความ — เห็นว่าเป็น fix ระดับเดียวกับ .tk-title ไม่ใช่ design decision ต้องผ่าน
   เอส เพราะไม่เปลี่ยน spacing/token แค่แก้ค่า overflow-wrap ให้ตรง spec ที่ตั้งใจจริง) */
.lb-chip {
  display:inline-flex; align-items:center; font-size:12px; font-weight:500;
  border-radius:999px; padding:3px 10px; overflow-wrap:break-word;
  background:color-mix(in srgb, var(--chip-c) 16%, var(--surface));
  /* text เข้มขึ้น (chip-c 55%) → ผ่าน AA ทุกสีทั้ง light/dark (เดิม 70% amber/gray ตก ~3.4-4.3) — M5 */
  color:color-mix(in srgb, var(--chip-c) 55%, var(--text));
}
/* yellow (#EBC634) เป็นสีสว่างสุดใน palette → fg 55% ตก AA เฉพาะ light (3.90:1); อีก 11 สีผ่านหมด
   ลดสัดส่วน chip-c ลง (45% = ผสม --text เข้มขึ้น) เฉพาะ yellow → light 5.07:1 / dark 9.16:1 ผ่าน AA.
   คง base --lb-yellow ไว้ให้ dot/swatch ยังเหลืองสด + ไม่แตะสีอื่น (ไม่ regress) — S25 B5.
   หมายเหตุ: เพิ่ม % (60-65%) จะยิ่ง fail light เพราะ chip-c ยิ่งสว่าง — ต้อง "ลด" % เท่านั้น */
.lb-chip[style*="--lb-yellow"] { color:color-mix(in srgb, var(--chip-c) 45%, var(--text)); }

/* ---- segmented control (custom radio — ❌ ห้าม native select) ---- */
.segmented { display:flex; gap:6px; background:var(--surface-2); border-radius:12px; padding:4px; }
.segmented--wrap { flex-wrap:wrap; }
.seg { flex:1; cursor:pointer; }
.seg input { position:absolute; opacity:0; pointer-events:none; }
.seg > span {
  display:flex; align-items:center; justify-content:center; gap:6px;
  font-size:var(--fs-sm); font-weight:500; color:var(--text-2);
  border-radius:8px; padding:8px 12px; transition:color .15s, background .15s;
}
.seg input:checked + span { background:var(--surface); color:var(--primary-aa); box-shadow:var(--shadow-tab); } /* S27-a11y (เอ): profile theme/density toggle (#pf-theme-seg/#pf-density-seg) 4.43→5.09:1 AA */
[data-theme="dark"] .seg input:checked + span { color:var(--primary-light); }
/* S30 audit P0 (เอ a11y): .seg input ไม่มี focus indicator เลย (role/team/field-type/theme/density picker)
   — parity gap เทียบ .cbx ที่แก้แล้ว S27 (2.4.7 AA). pattern เดียวกับ .cbx input:focus-visible + .cbx-box */
.seg input:focus-visible + span { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* ---- custom checkbox (❌ ห้าม native สีฟ้า) ---- */
.cbx { display:flex; align-items:center; gap:10px; font-size:14px; cursor:pointer; }
.cbx input { position:absolute; opacity:0; pointer-events:none; }
.cbx-box {
  width:20px; height:20px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border-control); border-radius:6px; background:var(--surface); /* S27 P0: border-control (AA 1.4.11) แทน --border hairline */
  color:#fff; font-size:11px; transition:background .15s, border-color .15s;
}
.cbx-box > i.fi { opacity:0; }
/* S27 Batch2 (B — contrast): checked box = white check icon ทับพื้น → ใช้ --primary-aa solid
   (เดิม --primary gradient ฝั่งอ่อน #FF7864 กับขาว = 2.59:1 fail — pattern เดียวกับ .btn-primary) */
.cbx input:checked + .cbx-box { background:var(--primary-aa); border-color:transparent; }
.cbx input:checked + .cbx-box > i.fi { opacity:1; }
/* S27 P0 (audit B6): .cbx input ไม่มี focus indicator เลย — เพิ่ม focus-visible ring บน .cbx-box ที่จับคู่ */
.cbx input:focus-visible + .cbx-box { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* ---- toggle switch (role=switch) — Global Wegital DS primitive (S33, เอส)
   เพิ่มจาก gap ที่ ยู flag ตอนออกแบบ View Setting panel (§8-Q3 view-setting-panel-spec-s33.md)
   ❌ ไม่ใช่ native checkbox แต่งหน้า — ต้องใช้คู่กับ role="switch" + aria-checked บน .toggle-switch เสมอ
   สถานะ CSS อ่านจาก attribute [aria-checked] ไม่ใช่ class → server-render (htmx) render attribute ตรงจาก
   DB ได้เลย ไม่ต้องมี JS state คู่ขนาน (ดู SKILL.md §4 Toggle Switch สำหรับ markup contract เต็ม + verify
   contrast). Global primitive — ห้าม fork ชื่อ class ใหม่ต่อโปรดักต์ (เพิ่ม modifier แทน เช่น --sm) */
.toggle-switch {
  display:flex; align-items:center; justify-content:center; flex-shrink:0;
  width:44px; height:44px;                 /* hit-area = DS §0.10 (pattern เดียวกับ .icon-btn) */
  background:none; border:none; padding:0; margin:0; cursor:pointer; border-radius:12px;
}
.toggle-track {
  position:relative; width:36px; height:20px; border-radius:999px; padding:2px; box-sizing:border-box;
  background:var(--surface-2); border:1px solid var(--border-control);   /* OFF: border-control ไม่ใช่ --border (1.4.11) */
  transition:background var(--ease-standard) .2s, border-color var(--ease-standard) .2s;
}
.toggle-knob {
  display:block; width:16px; height:16px; border-radius:999px;
  background:var(--surface); box-shadow:0 1px 2px rgba(0,0,0,.2);
  transform:translateX(0); transition:transform var(--ease-standard) .2s;
}
.toggle-switch[aria-checked="true"] .toggle-track { background:var(--primary-aa); border-color:var(--primary-aa); }
.toggle-switch[aria-checked="true"] .toggle-knob { transform:translateX(16px); }
.toggle-switch:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.toggle-switch:disabled { opacity:.45; cursor:not-allowed; }  /* WCAG 1.4.11 ยกเว้น inactive component — ไม่ต้องคุม contrast */

/* ---- toggle row — Global DS companion pattern (SKILL.md §4 "ในแถวการตั้งค่า") ----
   label+help ครอบซ้าย (ครอง flex เต็ม), .toggle-switch ชิดขวา · min-height 44 = hit-area ทั้งแถวเผื่อ label ครอบ
   (S33 View Setting panel เป็นผู้ใช้รายแรก — ตั้งชื่อ class ตาม Global contract ไม่ fork .vs-row เอง ให้ product อื่น reuse ได้) */
.toggle-row { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; min-height:44px; padding:8px 0; }
.toggle-row-txt { display:flex; flex-direction:column; gap:2px; min-width:0; padding-top:10px; } /* 10px จัด label บรรทัดแรกให้กึ่งกลาง track 44px */
.toggle-row-label { font-size:var(--fs-sm); color:var(--text); overflow-wrap:anywhere; } /* ไทยตัดบรรทัด DS §0.6 */
.toggle-row-help { font-size:11px; color:var(--text-2); overflow-wrap:anywhere; }

/* ---- color picker (swatch popover) ---- */
.cpick { position:relative; flex-shrink:0; }
.cpick-trigger {
  width:36px; height:36px; aspect-ratio:1/1; display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--border); border-radius:10px;
}
.cpick-trigger:hover { background:var(--surface-2); }
.cpick-dot { width:16px; height:16px; border-radius:999px; background:var(--chip-c); }
.cpick-dot--none { background:repeating-linear-gradient(45deg,var(--surface-2),var(--surface-2) 3px,var(--border) 3px,var(--border) 6px); }
.cpick-pop {
  display:none; position:absolute; top:calc(100% + 6px); right:0; z-index:10;
  grid-template-columns:repeat(5,1fr); gap:6px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:8px; box-shadow:var(--shadow-dropdown); width:180px;
}
.cpick-pop.open { display:grid; }
/* flip ขึ้นบน (คลื่น 3): แถวท้าย (N/A/Reject) พื้นที่ล่างไม่พอ — JS เติม class ตอนเปิด */
.cpick-pop--up { top:auto; bottom:calc(100% + 6px); }
.cpick-sw {
  width:26px; height:26px; aspect-ratio:1/1; border-radius:999px;
  border:2px solid transparent; background:var(--chip-c);
  display:flex; align-items:center; justify-content:center;
}
.cpick-sw:hover { border-color:var(--text-2); }
.cpick-sw--none {
  color:var(--text-2); font-size:12px;
  background:var(--surface-2); border-color:var(--border);
}

/* ---- option manager: grid table (redesign ยู — 4 คอลัมน์ตกแกนเดียว) ---- */
.modal-box--lg { max-width:640px; }
/* ❌ ไม่ใส่ overflow:hidden — จะ clip popover เลือกสีในแถว (บทเรียนเดียวกับ .status-manage คลื่น 3) */
.opt-table { display:flex; flex-direction:column; border:1px solid var(--border); border-radius:12px; }
/* สี 40 | ชื่อ 1fr | การ์ดที่ใช้ 96 | ลบ 40 — thead + ทุกแถวใช้ grid เดียวกัน */
.opt-thead, .opt-line {
  display:grid; grid-template-columns:40px 1fr 96px 40px; align-items:center; gap:16px; padding:8px 16px; margin:0;
}
.opt-thead { background:var(--surface-2); position:sticky; top:0; z-index:1; border-radius:12px 12px 0 0; }
.opt-th { font-size:11px; font-weight:500; color:var(--text-2); text-transform:uppercase; letter-spacing:.03em; }
.opt-th--c { text-align:center; }
.opt-line { border-top:1px solid var(--border); }
.opt-line:first-child { border-top:none; }
.opt-line:hover { background:var(--surface-2); }
.opt-empty { padding:16px; text-align:center; font-size:var(--fs-sm); color:var(--text-2); }

/* col1 สี — swatch (trigger color picker) */
.opt-swatch-wrap { position:relative; }
.opt-swatch {
  width:28px; height:28px; aspect-ratio:1/1; display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--border); border-radius:8px; cursor:pointer; padding:0;
}
.opt-swatch:hover { background:var(--surface-2); }
.opt-swatch .cpick-dot { width:16px; height:16px; }
.opt-swatch--ro { cursor:default; }
/* popover สีอยู่คอลัมน์ซ้ายสุด → anchor ซ้าย (default right:0 จะล้นขอบซ้าย modal) */
.opt-swatch-wrap .cpick-pop { left:0; right:auto; }

/* col2 ชื่อ (inline editable) + ปุ่ม save (โผล่เมื่อแก้ = save-before-edit) */
.opt-namecell { display:flex; align-items:center; gap:8px; min-width:0; }
.opt-name {
  flex:1; min-width:0; width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:transparent; border:1px solid transparent; border-radius:8px; padding:6px 10px; overflow-wrap:anywhere;
}
.opt-name:hover { background:var(--surface); border-color:var(--border); }
.opt-name:focus { outline:none; background:var(--surface); border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.opt-name--ro { border:none; padding:6px 0; }
.opt-save { display:none; flex-shrink:0; padding:6px 10px; }        /* ซ่อนจนกว่าจะมีการแก้ */
.opt-line--dirty .opt-save { display:inline-flex; }
.opt-line--dirty { background:color-mix(in srgb, var(--primary-solid) 5%, transparent); }               /* ไฮไลต์ pending */

/* col3 การ์ดที่ใช้ (เลขสี --warm สื่อว่าลบไม่ได้ถ้ามีใช้) */
.opt-usage { font-size:12px; color:var(--text-2); text-align:center; }
.opt-usage b { font-weight:600; color:var(--warm-text); } /* S27 P0: warm-text (AA) */
.opt-usage--zero { color:var(--text-2); opacity:.6; }

/* col4 ลบ */
.opt-line .iconbtn { width:28px; height:28px; font-size:var(--fs-sm); }
.opt-line .iconbtn::after { inset:-8px; } /* S27 P0: 28px context ต้อง -8px (28+16=44) แทน -7px ของ 30px default */

/* ---- status color manager (คลื่น 3) — reuse .cpick / .lb-chip / .iconbtn ---- */
/* ❌ ไม่ใส่ overflow:hidden — จะ clip popover เลือกสีของแถวท้าย (N/A/Reject) กดไม่ได้ (blocker คลื่น 3)
   row bg โปร่งใส → border-radius มุมการ์ดยังโชว์ถูกโดยไม่ต้อง clip */
.status-manage { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); }
.status-row { display:flex; align-items:center; gap:12px; flex-wrap:wrap; padding:12px 16px; }
.status-row + .status-row { border-top:1px solid var(--border); }
.status-row .lb-chip { min-width:96px; justify-content:center; }
.status-name { flex:1; min-width:120px; display:flex; align-items:baseline; gap:8px; overflow-wrap:anywhere; word-break:break-word; }
.status-name b { font-weight:500; color:var(--text); font-size:14px; }
.status-hint { font-size:11px; color:var(--text-2); }
/* save-before-edit: ปุ่มบันทึกโผล่เมื่อเลือกสีใหม่ (pending) */
.status-edit { display:flex; align-items:center; gap:8px; }
.status-save { display:none; flex-shrink:0; }
.status-row--dirty .status-save { display:inline-flex; }
.status-row--dirty { background:color-mix(in srgb, var(--primary-solid) 5%, transparent); border-radius:8px; }
/* mobile: row wrap → trigger ชิดซ้าย → popover ต้อง anchor ซ้าย (right:0 เดิมล้นขอบซ้าย — MAJOR คลื่น 3) */
@media (max-width:768px) {
  .cpick--status .cpick-pop { right:auto; left:0; }
}
/* ---- quick-add option (footer modal — Enter = เพิ่มทันที + คง focus) ---- */
.modal-footer--stack { flex-direction:column; align-items:stretch; gap:8px; }
.qadd { display:flex; flex-direction:column; align-items:stretch; gap:8px; width:100%; }
.qadd-row { display:flex; align-items:center; gap:8px; width:100%; }
/* S10 — chip ตัวเลือกที่ "พัก" ไว้ (ยังไม่บันทึก) */
.qadd-pending { display:flex; flex-wrap:wrap; gap:6px; }
.qadd-pending:empty { display:none; }
.qadd-chip {
  display:inline-flex; align-items:center; gap:4px; font-size:12px;
  background:var(--warn-bg); color:var(--text); border:1px dashed color-mix(in srgb, var(--warm) 50%, transparent);
  border-radius:999px; padding:3px 6px 3px 10px;
}
.qadd-chip-x { border:none; background:none; cursor:pointer; color:var(--text-2); display:flex; padding:2px; border-radius:50%; }
.qadd-chip-x:hover { color:var(--alert); background:var(--surface); }
.qadd-input { flex:1; min-width:0; position:relative; }
.qadd-input > i {
  position:absolute; left:12px; top:50%; transform:translateY(-50%);
  color:var(--text-2); font-size:14px; pointer-events:none; display:inline-flex; align-items:center; line-height:1;
}
.qadd-input input {
  width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:10px 12px 10px 38px;
}
.qadd-input input:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.qadd .btn-primary { flex-shrink:0; }
.qadd-msg:empty { display:none; }
.qadd-err { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--alert-text); background:var(--alert-bg); border-radius:8px; padding:6px 10px; overflow-wrap:anywhere; } /* S27 P0: alert-text (AA) */
.qadd-hint { font-size:11px; color:var(--text-2); display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.kbd { font-size:var(--fs-micro); font-weight:600; color:var(--text-2); background:var(--surface-2); border:1px solid var(--border); border-radius:6px; padding:2px 6px; } /* S27 Batch2: 10px ต่ำกว่า Micro floor (DS §3) → 11px */
.opt-modal-done { display:flex; justify-content:flex-end; width:100%; margin-top:4px; padding-top:12px; border-top:1px solid var(--border); }

.opt-add {
  display:flex; align-items:center; gap:8px; margin-top:12px;
  padding-top:12px; border-top:1px solid var(--border); flex-wrap:wrap;
}
.optrows { display:flex; flex-direction:column; gap:8px; margin-bottom:8px; }
.optrow { display:flex; align-items:center; gap:8px; }
.input-wrap--flex { flex:1; min-width:0; }
.input-wrap--flex input { width:100%; }
/* read-only display (Total actual — DEC-063): ดูเป็นค่าอ่านอย่างเดียว ไม่ใช่ช่องกรอก */
.input-wrap--ro input { background:var(--surface-2); color:var(--text); cursor:default; font-weight:600; }
.input-wrap--ro input:focus { border-color:var(--border); box-shadow:none; }
.btn-sm { padding:8px 14px; font-size:var(--fs-sm); }
.cf-meta { font-size:12px; color:var(--text-2); display:flex; align-items:center; gap:6px; overflow-wrap:anywhere; }
.cf-scopehint, .cf-noopt { font-weight:400; color:var(--text-2); }

/* ============ TASK/CARD (เฟส 2.3) ============ */
/* opt-in cap (readability) — margin:0 auto ย้ายมาที่นี่จาก .page (B1) ให้หน้าที่ cap ยัง center */
.page--wide { max-width:1100px; margin-inline:auto; }
.crumb { margin-bottom:4px; }
.crumb-link {
  display:inline-flex; align-items:center; gap:6px;
  font-size:12px; font-weight:500; color:var(--text-2); text-decoration:none;
}
.crumb-link:hover { color:var(--primary-solid); }

/* ---- list การ์ด (List Item Card — ไม่มี left-accent) ---- */
/* S52 (ซี — bug vertical-text ต่อ, ส่วนที่ 2): container-type ให้ .tk-row วัด "ความกว้างจริงที่มี" แทน viewport —
   pattern เดียวกับ .bd-card/.hq-panel/.bullet-list ที่มีอยู่แล้วในไฟล์นี้ (container-type:inline-size บน
   ancestor + @container query). เหตุผลที่ต้องใช้ container query ไม่ใช่ media query: verify จริงพบว่า
   .tk-rowmain ยุบเหลือ width:0 ที่ viewport 800px (drawer เปิดกิน 280px + page padding 64px เหลือ .tk-list
   แค่ 456px) ทั้งที่ viewport 750px (ต่ำกว่า mobile breakpoint 768px นิดเดียว — ตอนนั้น drawer ซ่อนเอง) กลับ
   เหลือพื้นที่ 718px สบาย ๆ — "ช่องว่างระหว่างเบรกพอยต์" นี้ (768px ปิด mobile declutter ทันที แต่ drawer ยัง
   กินพื้นที่จริงต่อ จนกว่า viewport จะกว้างพอเกิน ~1000px) คือ root cause ตัวจริงของ "การ์ดยุบแนวตั้งที่ 800px" ที่
   แดงเจอ (ไม่ใช่แค่ overflow-wrap อย่างเดียว — .tk-title fix บน L1313 ช่วยกันยุบเกินจริงจาก min-content แต่ยัง
   ไม่พอเมื่อพื้นที่จริงเหลือ ~0-30px) → container query แก้ตรงเหตุ เพราะวัดความกว้างจริงของ .tk-list/.grp-body
   เอง ไม่ขึ้นกับ viewport/drawer state (ทนต่อ drawer เปิด-ปิดในอนาคตด้วย ไม่ต้องเดา breakpoint ผูก px คงที่) */
.tk-list { display:flex; flex-direction:column; gap:8px; container-type:inline-size; container-name:tk-row-wrap; }
.tk-row {
  display:flex; align-items:center; gap:12px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:12px 16px; text-decoration:none; color:var(--text);
  box-shadow:var(--shadow-card); transition:border-color .15s;
}
.tk-row:hover { border-color:var(--primary-light); }
/* S30 audit P1 (เต๋ #1): focus indicator หายทั้ง List view (614 จุด) — pattern .risk-row/.seg */
.tk-row:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.tk-main { flex:1; min-width:0; }
/* S52 (ซี — bug vertical-text, root เดียวกับ .qv-field .dd-label บน L2036): ชื่อการ์ด (raw text node) ใน
   .tk-title (display:flex) กลายเป็น anonymous flex item เดียวกัน — เดิม overflow-wrap:anywhere นับ
   breaking-opportunity เข้า automatic min-content ด้วย (ต่างจาก break-word) → คอลัมน์ .tk-main ที่ถูกบีบแคบจาก
   sibling flex-shrink:0 (.tk-assignee/.tk-due/.tk-est) ที่ 800px ทำให้ min-content ยุบเหลือ ~1 ตัวอักษร =
   ชื่อการ์ดยุบแนวตั้ง (พบจริงใน List/Cross view) · ต่างจากปุ่ม dd-label (บรรทัดเดียว ตัด ellipsis ได้) ชื่อการ์ด
   ต้อง "ยังขึ้นหลายบรรทัดได้ปกติ" (ห้าม ellipsis ซ่อนชื่อ) → แก้ที่ root: overflow-wrap:anywhere → break-word
   (ยังตัดคำยาวไม่มีที่เว้นวรรค/ไทยได้เหมือนเดิมตอน layout จริง แค่ "ไม่นับ" breaking-opportunity เข้า
   min-content คำนวณ — กัน flex item ยุบเกินจริงโดยไม่กระทบการ wrap ปกติ, verify: css-tricks.com/almanac/properties/o/overflow-wrap) */
.tk-title {
  font-size:14px; font-weight:500; overflow-wrap:break-word;
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
}
.tk-meta { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:4px; }
.tk-id {
  font-size:11px; font-weight:500; letter-spacing:.04em; color:var(--text-2);
  background:var(--surface-2); border-radius:6px; padding:2px 6px;
}
.tk-chk { display:inline-flex; align-items:center; gap:4px; font-size:11px; color:var(--text-2); }
.tk-assignee { display:flex; align-items:center; gap:8px; flex-shrink:0; min-width:0; }
.tk-assignee-name { font-size:12px; color:var(--text-2); }
.tk-due, .tk-est {
  display:inline-flex; align-items:center; gap:4px; flex-shrink:0;
  font-size:12px; color:var(--text-2); min-width:96px;
}
/* S52 (ซี — bug vertical-text ต่อ .tk-list container-type บน L1294): "ช่องว่างระหว่างเบรกพอยต์" — mobile
   declutter เดิม (@media max-width:768px ท้ายไฟล์) ปิดพอดีตอน drawer (280px) ยังกินพื้นที่จริงต่อจนกว่า
   container จะกว้างพอ (~1000px+ viewport ถึงจะสบาย) → reuse มาตรการ declutter ชุดเดียวกัน (ซ่อนชื่อ assignee
   + ปล่อย due/est หดได้) ผ่าน container query แทนรอ viewport แคบเท่า mobile จริง — เผื่อพื้นที่ให้ .tk-rowmain/
   .tk-title พอมีที่ขึ้นข้อความอ่านได้ ไม่ยุบเหลือ ~0px (verify จริง: container 456-606px คือช่วงที่พังก่อนแก้) */
@container tk-row-wrap (max-width: 640px) {
  .tk-assignee-name { display:none; }
  .tk-due, .tk-est { min-width:0; }
}
.tk-muted { color:var(--text-2); opacity:.6; }
.tk-empty {
  padding:48px 16px; text-align:center; color:var(--text-2); font-size:14px;
  display:flex; align-items:center; justify-content:center; gap:8px;
  background:var(--surface); border:1px dashed var(--border); border-radius:var(--r);
}
.tk-empty-link { color:var(--primary-solid); font-weight:500; text-decoration:none; }
.tk-empty-link:hover { text-decoration:underline; }
.tk-empty-link:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-radius:4px; }
.prio { display:inline-flex; align-items:center; flex-shrink:0; font-size:14px; }
.avatar--sm { width:28px; height:28px; font-size:12px; }

/* ---- หน้าการ์ด ---- */
.card-form { display:flex; flex-direction:column; gap:16px; }
.card-fs { border:none; padding:0; margin:0; display:flex; flex-direction:column; gap:16px; min-width:0; }
.panel {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; display:flex; flex-direction:column; gap:16px;
  box-shadow:var(--shadow-card); margin-top:16px;
}
.card-form .panel, .card-fs .panel { margin-top:0; }
.panel-head { display:flex; align-items:center; gap:8px; }
.panel-head h2 { font-size:16px; font-weight:600; }
.panel-head > i.fi {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px;
}
.card-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:16px; }
.card-grid--3 { grid-template-columns:repeat(3,1fr); }
.card-actions { display:flex; align-items:center; justify-content:flex-end; gap:8px; }
/* S44 E0 รุมเทส m3: sibling .field-error มี overflow-wrap/word-break แล้ว แต่ .field-hint ไม่มี —
   E0 hint ยาวสุดในแอป (เช่น field-hint ของ working-day picker) เสี่ยงล้นกล่องมือถือ (DS §3 ภาษาไทย) */
.field-hint { font-size:11px; color:var(--text-2); overflow-wrap:anywhere; word-break:break-word; }
/* S27 B6 (DEC-068#4): client-side validation error — inline ใต้ field · icon+ข้อความ (ไม่พึ่งขอบแดงอย่างเดียว) */
.field-error {
  display:flex; align-items:flex-start; gap:6px; font-size:12px; color:var(--alert-text);
  overflow-wrap:anywhere; word-break:break-word;
}
.field-error i.fi { font-size:12px; margin-top:1px; flex:0 0 auto; }
.input-wrap input.input-invalid,
.input-wrap input[aria-invalid="true"] { border-color:var(--alert-text); }
.input-wrap input.input-invalid:focus,
.input-wrap input[aria-invalid="true"]:focus {
  border-color:var(--alert-text);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--alert-text) 12%, transparent);
}
.field-hint--block {
  display:flex; align-items:center; gap:6px; font-size:12px;
  background:var(--surface-2); border-radius:10px; padding:8px 12px;
}
.field-scope {
  float:right; font-size:var(--fs-micro); font-weight:500; color:var(--text-2); /* S27 Batch2: 10px ต่ำกว่า Micro floor (DS §3) → 11px */
  background:var(--surface-2); border-radius:999px; padding:2px 8px;
}
.ta {
  width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border-control); border-radius:12px; /* S27 Batch2: border-control (AA 1.4.11) แทน --border hairline */
  padding:12px; resize:vertical; min-height:72px;
}
.ta:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.ta--tall { min-height:132px; }  /* note ที่ต้องการพื้นที่พิมพ์เยอะ (daily) — resize:vertical ยืดเพิ่มเองได้ */
.btn-ghost--danger { color:var(--alert-text); } /* S27 P0: text ปุ่ม "ลบ" — alert-text (AA), เจอเพิ่มนอก audit list เดิม */
.alert-ok {
  display:flex; align-items:center; gap:8px;
  background:color-mix(in srgb, var(--green) 12%, transparent); color:var(--green-text); /* S27 P0: green-text (AA) */
  border-radius:var(--r); padding:12px 16px; font-size:14px; margin-bottom:16px;
}
#form-errors .alert-box { margin-bottom:4px; }

/* S10 (#7/#8) — การ์ดฟอร์มหน้า วันลา/วันหยุด */
.card-box {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:16px; margin-bottom:16px;
}
.hol-formt { font-size:var(--fs-md); font-weight:600; display:flex; align-items:center; gap:8px; margin-bottom:12px; }
.hol-formt i.fi { color:var(--primary-solid); }
.hol-forms { display:flex; flex-direction:column; gap:12px; }
.hol-add-row { display:flex; gap:12px; align-items:flex-end; flex-wrap:wrap; }
.hol-namef { flex:1; min-width:180px; }
/* S44 E0 — work_week card (วันทำงานมาตรฐาน) reuse .hol-add-row pattern (dropdown + ปุ่มบันทึก) */
.hol-ww { margin-bottom:16px; }
.hol-ww-row { display:flex; gap:12px; align-items:flex-end; flex-wrap:wrap; margin-top:12px; }
.hol-ww-row .field { flex:1 1 240px; min-width:0; }
.ww-current { display:inline-flex; align-items:center; gap:8px; font-weight:600; margin-top:8px; }
.ww-current i.fi { color:var(--primary-solid); }
.leave-grid { display:grid; grid-template-columns:repeat(4, minmax(160px, 1fr)); gap:12px; margin-bottom:12px; }
/* S32 polish: sticky save footer — ฟอร์มยาว (full-page) ปุ่มสร้างใบลาตรึงล่าง viewport กดได้ไม่ต้อง scroll สุด
   (modal ตรึง footer ผ่าน flex layout อยู่แล้ว — จุดนี้คือฟอร์มเต็มหน้า). negative margin = หัก padding card-box (16px) */
.leave-form-foot { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
  position:sticky; bottom:0; z-index:2; margin:12px -16px -16px; padding:12px 16px;
  background:var(--surface); border-top:1px solid var(--border);
  border-radius:0 0 var(--r) var(--r); box-shadow:0 -4px 12px rgba(0,0,0,.06); }
.leave-form-foot .field-hint { display:flex; align-items:center; gap:6px; }
@media (max-width:768px) { .leave-grid { grid-template-columns:1fr 1fr; } .hol-add-row, .hol-ww-row { flex-direction:column; align-items:stretch; } }

/* ---- custom dropdown trigger (❌ ห้าม native select — DS §4) ---- */
.dd { position:relative; min-width:0; }
.dd-trigger {
  width:100%; display:flex; align-items:center; gap:8px;
  background:var(--surface); border:1px solid var(--border-control); border-radius:12px; /* S27 P0: border-control (AA 1.4.11) แทน --border hairline */
  padding:10px 12px; min-height:44px; font-size:14px; color:var(--text); text-align:left; /* touch ≥44 */
  transition:border-color .15s;
}
.dd-trigger:hover { border-color:var(--primary-light); }
.dd-trigger:disabled { opacity:.6; }
.dd-trigger > i.fi:last-child { margin-left:auto; color:var(--text-2); }
.dd-label {
  display:inline-flex; align-items:center; gap:8px; min-width:0;
  overflow-wrap:anywhere; flex:1;
}
.dd-muted { color:var(--text-2); display:inline-flex; align-items:center; gap:6px; }

/* ---- Bottom Sheet (DS §4 — dropdown/date เปิดเป็น sheet เสมอ) ---- */
.sheet-backdrop {
  position:fixed; inset:0; z-index:4000; background:rgba(0,0,0,.4);
  display:flex; align-items:stretch; justify-content:flex-end; /* S10 (#5): drawer ชิดขวา */
}
/* S10 (#5, พีสั่ง): dropdown/picker เปิดเป็น "drawer ทางขวา" (desktop) แทน bottom sheet
   — มือถือคง bottom sheet (จับถนัดกว่า) ผ่าน media query ด้านล่าง */
.sheet {
  background:var(--surface); width:380px; max-width:88vw; height:100%; max-height:100%;
  border-radius:var(--r) 0 0 var(--r); box-shadow:-8px 0 24px rgba(0,0,0,.14);
  overflow:hidden; display:flex; flex-direction:column;
  transform:translateX(100%); transition:transform .25s;
}
.sheet.open { transform:translateX(0); }
.sheet-head { flex-shrink:0; padding:16px 20px 8px; display:flex; flex-direction:column; align-items:flex-start; gap:8px; }
.sheet-handle { display:none; } /* drawer ขวาไม่ต้องมี handle ลาก (เดิมของ bottom sheet) */
@media (max-width:768px) {
  .sheet-backdrop { align-items:flex-end; justify-content:center; }
  .sheet {
    width:100%; max-width:460px; height:auto; max-height:72vh;
    border-radius:20px 20px 0 0; box-shadow:none;
    transform:translateY(100%);
  }
  .sheet.open { transform:translateY(0); }
  .sheet-head { padding:8px 20px 8px; align-items:center; }
  .sheet-handle { display:block; width:36px; height:4px; border-radius:999px; background:var(--border); }
}
.sheet-title { font-size:14px; font-weight:600; align-self:flex-start; }
.sheet-body { overflow-y:auto; flex:1; min-height:0; padding:4px 12px 16px; display:flex; flex-direction:column; gap:2px; }
.sheet-search { padding:4px 8px 8px; }
.sheet-search input {
  width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface-2); border:1px solid var(--border); border-radius:12px; padding:10px 12px;
}
/* search input + icon ซ้าย (DS §4) — reuse: sheet search, member, views */
.search-input-wrap { position:relative; }
.search-input-wrap > i.fi {
  position:absolute; left:12px; top:50%; transform:translateY(-50%);
  color:var(--text-2); font-size:14px; pointer-events:none;
  display:inline-flex; align-items:center; line-height:1;
}
.search-input-wrap input { padding-left:36px; }
.sheet-opt {
  display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  font-size:14px; color:var(--text); border-radius:10px; padding:10px 12px; min-height:44px; /* touch ≥44 */
  background:transparent; flex-wrap:wrap;
}
.sheet-opt:hover { background:var(--surface-2); }
/* selected: bg tint + bold + icon แดง (สื่อชัดโดยไม่พึ่งสีตัวอักษรล้วน — text=--text ผ่าน AA) */
.sheet-opt--sel { background:color-mix(in srgb, var(--primary-solid) 10%, transparent); color:var(--text); font-weight:600; }
.sheet-opt--sel > i.fi { color:var(--primary-solid); }
.sheet-opt--muted { color:var(--text-2); }
.sheet-opt-path { font-size:11px; color:var(--text-2); width:100%; overflow-wrap:anywhere; }
.sheet-empty { padding:16px; font-size:var(--fs-sm); color:var(--text-2); text-align:center; }
/* PB2 (S35): trash ต่อ option ในกล่องเลือก (scratch custom field) — โผล่เฉพาะ dropdown ที่ data-can-del=1 */
.sheet-opt--deletable { justify-content:space-between; }
.sheet-opt-del { display:inline-flex; align-items:center; justify-content:center; width:32px; min-height:32px;
  border-radius:8px; color:var(--text-2); cursor:pointer; flex-shrink:0; }
.sheet-opt-del:hover { background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-aa); }
[data-theme="dark"] .sheet-opt-del:hover { color:var(--primary-light); }
/* ปุ่ม "+ เพิ่ม xxx" inline (dropdown self-service 2026-07-30) — Coral AA-safe text, touch ≥44 (จาก .sheet-opt) */
.sheet-opt--add { color:var(--primary-aa); font-weight:600; border-top:1px solid var(--border); margin-top:4px; }
.sheet-opt--add > i.fi { color:var(--primary-aa); margin-inline-end:6px; }
/* dark: coral เข้ม (--primary-aa #CE3329) บนพื้นมืดตก AA (~3.7:1) → ใช้ coral สว่าง --primary-light (~6.4:1) */
[data-theme="dark"] .sheet-opt--add,
[data-theme="dark"] .sheet-opt--add > i.fi { color:var(--primary-light); }
/* N3 (เต๋ รุมเทส E4-4A) + B1: header คั่นกลุ่ม option ใน sheet/popover (เช่น scope picker ต่อโปรเจค —
   report.html, clone เข้า sheet ผ่าน ddOpen ใน shared.js) — ไม่ interactive (plain <div>, ไม่เข้า tab-order)
   ไม่ uppercase (ต่างจาก .dh-title/.opt-th ที่เป็น label ระบบคงที่ — ตรงนี้โชว์ชื่อโปรเจคที่ user ตั้งเอง
   บังคับตัวใหญ่จะทำลาย identity ที่ user ตั้งใจสะกดไว้) */
.dd-group-label { padding:8px 12px 4px; font-size:11px; font-weight:600; color:var(--text-2); }
/* pill บอกประเภท scope (Quoted/Ad-hoc) ต่อท้าย option label ให้ scan เร็ว — ค่าตรงกับ .badge base (§ ค้นหา
   ".badge {") ย่อ padding ให้พอดีพื้นที่แคบของ .sheet-opt (touch target 44px เดิมไม่กระทบ — badge เป็น
   decoration ปลาย option ไม่ใช่ target แยก) flex-shrink:0 กันบี้เมื่อชื่อ scope ยาว + wrap */
.dd-opt-badge { margin-left:auto; flex-shrink:0; padding:2px 8px; border-radius:999px;
  font-size:11px; font-weight:500; background:var(--surface-2); color:var(--text-2); }

/* ---- custom calendar (❌ ห้าม native date — ปี พ.ศ.) ---- */
.cal { display:flex; flex-direction:column; gap:8px; padding:4px 8px; }
.cal-head { display:flex; align-items:center; justify-content:space-between; }
.cal-title { font-size:14px; font-weight:600; }
.cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; }
.cal-wd {
  font-size:11px; font-weight:500; color:var(--text-2); text-align:center; padding:4px 0;
}
.cal-day {
  /* S31: border/appearance/-webkit-appearance/cursor มาจาก global button reset (§150) แล้ว
     — เหลือแค่ width/padding/สไตล์เฉพาะของปฏิทิน ไม่ต้อง reset ซ้ำ (กัน redundant) */
  width:100%; aspect-ratio:1/1; display:flex; align-items:center; justify-content:center;
  font-size:var(--fs-sm); color:var(--text); border-radius:10px; background:transparent; padding:0;
}
.cal-day:hover { background:var(--surface-2); }
/* S56 (เต๋): เดิมสื่อ "วันนี้" ด้วยเส้นขอบ 1px สี --primary-light อย่างเดียว = 2.60:1 ตก WCAG 1.4.11
   (non-text contrast ต้อง ≥3:1) และเป็น "สีอย่างเดียว" (1.4.1) → เปลี่ยนเป็น --primary-solid (ผ่าน 3:1)
   + ตัวหนา = สัญญาณที่ไม่พึ่งสี คนตาบอดสี/จอสว่างจัดยังแยกออก */
.cal-day--today { border:1px solid var(--primary-solid); font-weight:600; }
.cal-day--sel { background:var(--primary); color:#fff; font-weight:600; }
/* m5: วันอนาคตบน field ห้ามอนาคต (data-nofuture เช่น work_date) — จาง+ กดไม่ได้ */
.cal-day--disabled { color:var(--text-3, var(--text-2)); opacity:.35; cursor:not-allowed; }
.cal-day--disabled:hover { background:transparent; }
.cal-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; padding-top:4px; }

/* ===== S27 B4 (DEC-068#1 §6.6): Anchored popover (desktop) สำหรับ custom dropdown/date/time =====
   native Popover API → top-layer จริง (ลอยเหนือ <dialog> modal ไม่ต้องคุม z-index เอง)
   ตำแหน่ง top/left set จาก JS (ovPosition) · reset UA popover default (inset:0/margin:auto/border/bg)
   mobile ไม่ผ่าน .ov-pop (fallback ไป .sheet เดิม) — บล็อกนี้จึงไม่มี media query */
.ov-pop { position:fixed; margin:0; inset:auto; padding:0; border:none; background:transparent; }
.ov-pop::backdrop { background:transparent; } /* anchored dropdown ไม่ dim ฉากหลัง */
.ov-pop--anchored {
  width:320px; max-width:calc(100vw - 16px); max-height:min(60vh, 440px);
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:var(--shadow-dropdown); overflow:hidden;
  display:flex; flex-direction:column; z-index:5000; /* z-index สำรองกรณี fallback ไม่ใช้ top-layer */
}
.ov-pop--anchored .sheet-body { padding:6px; } /* กระชับกว่า sheet (ไม่มี title/handle บน desktop) */
/* S56 (เต๋): ช่องวันบน desktop ได้ 39.7px < 44 ตาม DS/WCAG 2.5.5 — 2 สาเหตุซ้อนกัน (วัดจากจอจริง)
   (1) กว้าง 320 − ขอบ 2 − .sheet-body 12 − .cal 16 − gap 2x6 = 278/7 = 39.71
   (2) ปฏิทินสูง ~453px > max-height 440 → .sheet-body มี scrollbar กินอีก 15px
   → ปฏิทินใช้ 350px (350−42)/7 = 44.0 พอดี + ยก cap ความสูงให้พ้น scrollbar (pattern เดียวกับ
   .ov-pop--range ที่ยก cap ไปแล้ว) · scope ด้วย :has(.cal) — dropdown/เมนูอื่นยัง 320px เท่าเดิม */
.ov-pop--anchored:has(.cal) { width:350px; max-height:min(88vh, 520px); }
/* S31 range picker: เนื้อหา (field row + hint + quick + ปฏิทิน + foot) สูงกว่า dropdown/date เดี่ยวมาก
   ให้ cap สูงขึ้น (ยังจำกัดกัน popover ยาวเกิน viewport เตี้ย) — ดู dpRangeOpen (shared.js) ที่เติม class นี้ */
.ov-pop--range { max-height:min(75vh, 560px); }

/* ===== S49 (ซี): Tooltip primitive (.tip-bubble, trigger = [data-tip]) — DS §6.6 "Tooltip" row =====
   ใช้แทน native title="…" (ช้า/มือถือไม่ขึ้น/keyboard focus ไม่ได้) — JS: shared.js tipShow/tipHide/tipPosition
   top-layer จริงผ่าน popover="manual" (Baseline 2024 — Chrome114+/Firefox125+/Safari17+; DS doc เขียน
   popover="hint" ไว้แต่ Safari ยังไม่รองรับจริง ก.ย. 2026 → ใช้ manual แทน ดูเหตุผลเต็มใน shared.js)
   .tip-bubble--fixed = fallback browser ที่ไม่มี Popover API เลย (rare) — position:fixed ล้วนเหมือนกัน
   ต่างแค่กลไก visibility (ไม่ผ่าน top-layer) → เขียนสไตล์ร่วมกันได้เกือบหมด แยกแค่ transition/entrance state */
.tip-bubble {
  position:fixed; margin:0; border:1px solid var(--border); z-index:6000;
  padding:6px 10px; max-width:240px; border-radius:8px;
  background:var(--surface); color:var(--text); box-shadow:var(--shadow-dropdown);
  font-family:inherit; font-size:12px; font-weight:500; line-height:1.4;
  pointer-events:none;                       /* เนื้อหาไม่ interactive (APG tooltip pattern) — เมาส์ทะลุได้ กัน mouseleave loop */
  overflow-wrap:anywhere; word-break:break-word; /* ข้อความไทยยาวไม่มีวรรค */
}
/* ---- tier 1: Popover API (top-layer จริง) — @starting-style ทำ entrance fade (มาตรฐาน Chrome/MDN
   สำหรับ animate popover จาก display:none) · exit fade ทำงานได้แม้ browser ไม่รองรับ @starting-style
   (แค่เสีย entrance fade เฉย ๆ — เข้าทันทีไม่กระพริบ ไม่ใช่ bug) */
[popover].tip-bubble {
  inset:auto;                                /* reset UA popover default (inset:0 + margin:auto = จัดกลางจอ) */
  opacity:0; transform:scale(.94);
  transition:opacity .12s var(--ease-standard), transform .12s var(--ease-standard),
             display .12s allow-discrete, overlay .12s allow-discrete;
}
[popover].tip-bubble:popover-open { opacity:1; transform:scale(1); }
@starting-style { [popover].tip-bubble:popover-open { opacity:0; transform:scale(.94); } }
/* ---- tier 2: fallback (ไม่มี Popover API) — visibility-delay trick คลาสสิก ไม่พึ่ง CSS feature ใหม่เลย ---- */
.tip-bubble--fixed {
  opacity:0; visibility:hidden; transform:scale(.94);
  transition:opacity .12s var(--ease-standard), transform .12s var(--ease-standard), visibility 0s linear .12s;
}
.tip-bubble--fixed.is-open {
  opacity:1; visibility:visible; transform:scale(1);
  transition:opacity .12s var(--ease-standard), transform .12s var(--ease-standard), visibility 0s linear 0s;
}
@media (prefers-reduced-motion:reduce) {
  [popover].tip-bubble, .tip-bubble--fixed { transition:none; transform:none; }
}
/* trigger focus ring (SC 2.4.7) — reusable ให้ทุก [data-tip] ในอนาคต ไม่ต้องเขียนซ้ำต่อ component */
[data-tip]:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* ---- ปุ่มลัดเลือกวัน (ClickUp-style) — shared ทุก date picker (dpOpen) ---- */
.cal-quick { display:flex; flex-wrap:wrap; gap:6px; padding-bottom:4px; border-bottom:1px solid var(--border); }
.cal-qbtn {
  display:inline-flex; align-items:center; gap:6px; min-height:44px; padding:8px 12px; /* touch target ≥44px */
  border:1px solid var(--border); border-radius:12px; background:var(--surface);
  font-size:var(--fs-sm); font-weight:500; color:var(--text); cursor:pointer;
  overflow-wrap:anywhere; transition:border-color .15s, background .15s;
}
.cal-qbtn i.fi { color:var(--primary-solid); font-size:14px; }
.cal-qbtn:hover { border-color:var(--primary-light); background:var(--surface-2); }
.cal-qbtn:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* ---- Date RANGE picker (S31, พีเคาะ) — Pattern B sequential + click-to-edit 2 field, popover เดียว ----
   .dr-group ครอบ trigger เดิม 2 อัน (Start/Due, ไม่แตะ name/hidden input เดิม) — คลิกอันไหนเปิด popover เดียวกัน (dpRangeOpen, shared.js)
   ปฏิทินยังเป็น .cal/.cal-grid/.cal-day เดิมทั้งชุด (dpOpen วันเดี่ยวไม่ถูกแตะ) — เพิ่มแค่ wrapper .cal-day-o
   สำหรับทาสี range band ต่อเนื่อง (เปิด gap:0 เฉพาะ .cal-grid--range) ไม่กระทบ grid ปกติ */
.dr-group { display:flex; gap:12px; flex-wrap:wrap; grid-column:1 / -1; } /* span เต็มแถวถ้าอยู่ใน .card-grid 2 คอลัมน์ */
.dr-group > .field { flex:1 1 160px; min-width:0; }

.dr-pop { display:flex; flex-direction:column; gap:8px; padding:4px 8px; min-width:280px; }
.dr-fields { display:flex; align-items:center; gap:6px; padding-bottom:8px; border-bottom:1px solid var(--border); }
.dr-fsep { color:var(--text-2); flex-shrink:0; font-size:12px; }
.dr-fbtn {
  flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:2px; align-items:flex-start;
  padding:6px 10px; min-height:44px; border:1px solid var(--border); border-radius:10px; /* touch ≥44 */
  background:var(--surface); text-align:left; transition:border-color .15s;
}
.dr-fcap { font-size:11px; font-weight:500; color:var(--text-2); }
.dr-fval { font-size:13px; font-weight:600; color:var(--text); overflow-wrap:anywhere; }
.dr-fbtn--active { border-color:var(--primary-solid); }
.dr-fbtn--active .dr-fcap, .dr-fbtn--active .dr-fval { color:var(--primary-solid); }

/* hint แถบ step (เลือก start / กำลังเลือก due) — reuse ทั้ง 2 step (spec: cal-hint) */
.cal-hint {
  display:flex; align-items:center; gap:8px; font-size:12px; font-weight:600; color:var(--primary-solid);
  background:color-mix(in srgb, var(--primary-solid) 8%, transparent); padding:8px 10px; border-radius:10px;
}
.cal-hint--due { color:var(--text); background:var(--surface-2); }
.dr-dot { flex:0 0 8px; height:8px; border-radius:50%; background:var(--primary-solid); }

/* range band ต่อเนื่อง (ClickUp-style) — สี tint primary ~12% flag เอส (design-systems) ยืนยัน token ทางการ
   (spec date-range-picker-spec-s31.md decision #5 — ใช้ provisional --dr-range-tint ไปก่อน) */
.cal-grid--range { gap:0; }
.cal-day-o { position:relative; display:flex; align-items:center; justify-content:center; }
.cal-day-o--mid { background:var(--dr-range-tint, color-mix(in srgb, var(--primary-solid) 12%, transparent)); }
.cal-day-o--start { background:linear-gradient(to right, transparent 50%, var(--dr-range-tint, color-mix(in srgb, var(--primary-solid) 12%, transparent)) 50%); }
.cal-day-o--end { background:linear-gradient(to right, var(--dr-range-tint, color-mix(in srgb, var(--primary-solid) 12%, transparent)) 50%, transparent 50%); }

@media (max-width:768px) { .dr-fcap { display:none; } } /* มือถือแคบ — เหลือแค่ค่า ไม่ต้องมี caption ซ้ำ label ด้านบน */

/* ---- checklist ---- */
.chk-count { font-size:12px; font-weight:500; color:var(--text-2); }
.chk-bar {
  flex:1; max-width:160px; height:6px; border-radius:999px;
  background:var(--surface-2); overflow:hidden;
}
.chk-bar-fill { display:block; height:100%; border-radius:999px; background:var(--secondary-chart); } /* S41: --secondary-chart (audit §1 — dark contrast fix) */
.chk-item { display:flex; align-items:center; gap:10px; padding:4px 0; flex-wrap:wrap; }
.chk-item--done .chk-label { text-decoration:line-through; color:var(--text-2); }
.chk-box {
  width:20px; height:20px; aspect-ratio:1/1; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--border); border-radius:6px; background:var(--surface);
  color:#fff; font-size:11px; transition:background .15s, border-color .15s;
}
.chk-box > i.fi { opacity:0; }
.chk-box--on { background:var(--primary); border-color:transparent; }
.chk-box--on > i.fi { opacity:1; }
.chk-label { font-size:14px; min-width:0; overflow-wrap:anywhere; }
.chk-dates { display:inline-flex; align-items:center; gap:4px; font-size:11px; color:var(--text-2); }
.chk-act { display:flex; align-items:center; gap:2px; margin-left:auto; opacity:0; transition:opacity .15s; }
.chk-item:hover .chk-act { opacity:1; }
.chk-empty { font-size:var(--fs-sm); color:var(--text-2); }
.chk-add { display:flex; }

/* ---- dependencies (S41 Wave 3) — token-based (light+dark ผ่าน var) ---- */
.dep-group { margin-top:4px; }
.dep-group + .dep-group { margin-top:14px; padding-top:12px; border-top:1px solid var(--border); }
.dep-group-head { display:flex; align-items:center; gap:6px; margin-bottom:6px; color:var(--text-2); font-size:12px; }
.dep-group-head > i.fi { font-size:13px; }
.dep-group-title { font-weight:600; }
.dep-hint { margin-left:4px; }
.dep-item { display:flex; align-items:center; gap:8px; padding:5px 4px; border-radius:8px; }
.dep-item:hover { background:var(--surface-2, color-mix(in srgb, var(--text) 5%, transparent)); }
.dep-item--done .dep-title { text-decoration:line-through; color:var(--text-2); }
.dep-status { flex-shrink:0; }
.dep-title { font-size:14px; color:var(--text); text-decoration:none; min-width:0; overflow-wrap:anywhere; }
.dep-title:hover { color:var(--primary); text-decoration:underline; }
.dep-id { flex-shrink:0; }
.dep-del { margin-left:auto; opacity:0; transition:opacity .15s; }
.dep-item:hover .dep-del { opacity:1; }
.dep-add { position:relative; margin-top:8px; }
.dep-results { display:flex; flex-direction:column; gap:2px; margin-top:4px; }
.dep-result { display:flex; align-items:center; gap:8px; width:100%; text-align:left; padding:6px 8px;
  border:1px solid transparent; border-radius:8px; background:transparent; cursor:pointer; font:inherit; color:var(--text); }
.dep-result:hover, .dep-result:focus-visible { background:var(--surface-2, color-mix(in srgb, var(--text) 6%, transparent)); border-color:var(--border); }
.dep-result-title { min-width:0; overflow-wrap:anywhere; font-size:14px; }
.dep-result-title + .tk-id { margin-left:auto; }
.dep-result-empty { font-size:var(--fs-sm); color:var(--text-2); padding:6px 8px; }

/* ---- tags ---- */
.tag-wrap { display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
.tag-chip {
  display:inline-flex; align-items:center; gap:4px;
  font-size:12px; font-weight:500; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:999px; padding:3px 10px;
}
.tag-chip--sm { font-size:11px; padding:2px 8px; }
.tag-x {
  display:inline-flex; align-items:center; justify-content:center;
  width:16px; height:16px; border-radius:999px; color:var(--text-2); background:transparent; font-size:11px;
}
.tag-x:hover { color:var(--alert); background:var(--surface); }
.tag-add { position:relative; display:flex; flex-direction:column; gap:4px; }
.tag-suggest {
  position:absolute; top:calc(100% + 4px); left:0; z-index:20;
  display:flex; flex-direction:column; min-width:200px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:4px; box-shadow:var(--shadow-dropdown);
}
.tag-suggest-item {
  text-align:left; font-size:var(--fs-sm); color:var(--text); border-radius:8px; padding:8px 10px; background:transparent;
}
.tag-suggest-item:hover { background:var(--surface-2); }

/* ---- drawer list link ---- */
.dname--link { text-decoration:none; color:var(--text); }
.dname--link:hover { color:var(--primary-solid); }

/* mobile (เฟส 2.3) */
@media (max-width:768px) {
  .card-grid, .card-grid--3 { grid-template-columns:1fr; }
  .tk-row { flex-wrap:wrap; }
  .tk-main { flex-basis:100%; }
  .tk-assignee-name { display:none; }
  .tk-due, .tk-est { min-width:0; }
  .chk-act { opacity:1; }
}

/* ============ VIEWS (เฟส 2.4 — Board/Table/Timeline + view switcher) ============ */
.page--full { max-width:none; }

/* ---- view switcher (segmented) ---- */
.view-seg {
  display:inline-flex; gap:4px; background:var(--surface-2);
  border-radius:12px; padding:4px; margin-bottom:8px;
}
/* lean หัวหน้า list (พีสั่ง S10 + REQ-S12 ข้อ 3 — บีบพื้นที่ว่างก่อนตารางให้เห็นการ์ดมากขึ้น) */
.page--full { padding-top:16px; padding-bottom:16px; }
.page--full .page-head { margin-bottom:8px; }
.page--full .page-head h1 { font-size:20px; margin-bottom:2px; }
.page--full .page-head .crumb { margin-bottom:2px; }
.page--full .page-sub { font-size:12px; }
.page--full .view-seg { margin-bottom:6px; }
.page--full .filter-bar { margin-bottom:6px; }

/* REQ-S12 ข้อ 3 แบบ B — ส่วนหัวแถวเดียว: title+count · tabs · filter(ยืด) · ปุ่มเพิ่มการ์ด */
.page-bar { display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin-bottom:8px; }
.page-bar-head { display:flex; align-items:center; gap:8px; min-width:0; flex-shrink:0; }
.page-bar-title { font-size:18px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:280px; }
.page-bar-count { flex-shrink:0; background:var(--surface-2); color:var(--text-2); font-size:12px; padding:2px 10px; border-radius:999px; white-space:nowrap; }
/* R2 (S55): หมายเหตุส่วนต่างจำนวนการ์ดที่ project-level (นับ complete/due ปีอื่น ต่างจากหน้า List)
   — muted ไม่แย่ง hierarchy กับ .page-bar-count · จอแคบตัดทิ้ง (title/tooltip ยังอยู่บน desktop) */
.page-bar-note { flex-shrink:1; min-width:0; display:inline-flex; align-items:center; gap:4px;
  color:var(--text-2); font-size:12px; line-height:1.4; }
.page-bar-note i.fi { font-size:12px; color:inherit; }
@media (max-width:767px) { .page-bar-note { display:none; } }
.page-bar .view-seg { margin-bottom:0; flex-shrink:0; }
/* S34 fix(control-bar-align): เดิม flex:1 1 200px + min-width:0 กะขนาด .filter-bar เป็น 200px คงที่ตอน
   line-breaking (ไม่ได้มองเนื้อหาจริง) → พอ tl-ctrl เต็มชุด (group/sort/unit/zoom/fit) ต้องการ ~600-900px
   จริง กล่องถูกกะไว้แค่ ~200-240px → ปุ่มหักบรรทัด/ล้นทับ .page-bar-add ข้าง ๆ (วัดจริง mobile 375+desktop 1280)
   → เปลี่ยนเป็น flex-basis:auto (กะจากเนื้อหาจริง) + เอา min-width:0 ออก: เนื้อหาน้อย (Board/Table/Calendar
   ~350px) ยังแชร์บรรทัดเดียวกับ title/tabs ได้เหมือนเดิม · เนื้อหาเยอะ (Timeline เต็มชุด) ตกไปทั้งก้อนบรรทัดใหม่
   เต็มความกว้าง .page-bar แทนถูกบีบเป็นเสาแคบ ๆ ซ้อนหลายสิบบรรทัด */
.page-bar .filter-bar { margin-bottom:0; flex:1 1 auto; }
.page-bar-add { flex-shrink:0; padding:8px 16px; font-size:var(--fs-sm); white-space:nowrap; }
.view-tab {
  display:inline-flex; align-items:center; gap:6px;
  font-size:var(--fs-sm); font-weight:500; color:var(--text-2);
  border-radius:8px; padding:8px 14px; text-decoration:none;
  transition:color .15s, background .15s;
}
.view-tab:hover { color:var(--text); }
.view-tab--on { background:var(--surface); color:var(--primary-aa); box-shadow:var(--shadow-tab); } /* S27-a11y (เอ): primary-solid #DC3C32 on white = 4.43:1 fail AA text (WCAG 1.4.3) — primary-aa #CE3329 = 5.09:1 pass */
[data-theme="dark"] .view-tab--on { color:var(--primary-light); } /* primary-aa on dark surface #1E1E1E ≈ 3.3:1 fail — primary-light #FF7864 = 6.45:1 pass (pattern = .sheet-opt--add) */
/* view-tab "เร็ว ๆ นี้" (space-view MVP = list เท่านั้น) — disabled cue ไม่ใช่ลิงก์ */
.view-tab--soon { color:var(--text-2); opacity:.7; cursor:default; font-weight:400; }
#view-alert .alert-box { margin-bottom:16px; }

/* ==== Group by (Space-view + List grouping) S15 — reuse --lb-*/lb-chip/avatar/bd-count ==== */
.h1-icon { display:inline-flex; align-items:center; justify-content:center; aspect-ratio:1/1; flex-shrink:0;
  width:28px; height:28px; border-radius:50%; background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  font-size:14px; margin-right:8px; vertical-align:middle; }
/* group-by trigger active */
.grp-btn--on { color:var(--primary-aa); border-color:var(--primary-light); } /* S34-a11y: primary-solid #DC3C32 on white = 4.43:1 fail AA text (WCAG 1.4.3) — primary-aa #CE3329 = 5.09:1 pass (mirror .view-tab--on) */
[data-theme="dark"] .grp-btn--on { color:var(--primary-light); } /* primary-aa on dark surface fail — primary-light #FF7864 = 6.45:1 pass */
/* 1 กลุ่ม */
.grp-wrap { display:flex; flex-direction:column; gap:8px; }
.grp { display:flex; flex-direction:column; }
/* header กลุ่ม — caret + chip/avatar/icon + count แกน center เดียว (DS §6.2) */
.grp-head { display:flex; align-items:center; gap:8px; width:100%; min-height:44px; /* touch target WCAG 2.5.5 (ตรง .pv-addbtn/.seg-btn) */
  text-align:left; cursor:pointer;
  padding:8px 16px; background:var(--surface-2); border:1px solid var(--border); border-radius:10px;
  color:var(--text); font-size:var(--fs-sm); font-weight:500; }
.grp-head:hover { border-color:var(--primary-light); }
.grp-head:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.grp-caret { display:inline-flex; align-items:center; justify-content:center; aspect-ratio:1/1; flex-shrink:0;
  width:24px; height:24px; color:var(--text-2); transition:transform .15s; }
.grp--collapsed .grp-caret { transform:rotate(-90deg); }
.grp-name { min-width:0; overflow-wrap:anywhere; } /* ชื่อ list/custom ยาว (ไทยไม่เว้นวรรค) */
.grp-licon { color:var(--primary-solid); flex-shrink:0; display:inline-flex; align-items:center; }
.grp-count { margin-left:auto; font-size:11px; font-weight:500; color:var(--text-2);
  background:var(--surface); border-radius:999px; padding:2px 8px; min-width:22px; text-align:center; flex-shrink:0; }
/* body — การ์ด reuse tk-row (list_row) · ขอบซ้ายการ์ด align กับ chip หัวกลุ่ม (§6.2)
   chip เริ่ม 48px (pad16+caret24+gap8) · card content = grp-body pad-left + card pad16 → pad-left 32px ให้ตรง */
/* S52 (ซี — bug vertical-text, container query mirror .tk-list บน L1294 — grp-body = อีกที่ที่ list_row โผล่ (grouped/cross view)
   ต้อง container-name เดียวกันให้ @container tk-row-wrap ครอบทั้ง 2 ที่ที่ list_row render ได้ (ดู comment เต็มที่ .tk-list) */
.grp-body { display:flex; flex-direction:column; gap:8px; padding:8px 0 4px 32px; container-type:inline-size; container-name:tk-row-wrap; }
.grp--collapsed .grp-body { display:none; }
.grp-body:empty { display:none; }
/* per-group +add (เฉพาะ by List — §10 Q6) */
.grp-add { display:inline-flex; align-items:center; gap:6px; align-self:flex-start; min-height:36px;
  font-size:12px; font-weight:500; color:var(--text-2); text-decoration:none;
  border:1px dashed var(--border); border-radius:8px; padding:6px 12px; }
.grp-add:hover { border-color:var(--primary-light); color:var(--primary-solid); }
.grp-add i.fi { color:inherit; }
.board > .alert-box { margin-bottom:16px; }

/* ---- Board (Kanban — 7 คอลัมน์ status DEC-024) ---- */
.board-cols { display:flex; gap:12px; align-items:flex-start; overflow-x:auto; padding-bottom:16px; }
/* S56 H3 (เต๋ real-click จาก space จริง): คอลัมน์ Done สูง 21,642px → ปุ่ม "+ เพิ่มการ์ด" ท้ายคอลัมน์
   อยู่ y=21,726px = ต้องเลื่อน 20+ จอถึงจะกดได้ (บอร์ดจริงกดไม่ถึง).
   แก้แบบเดียวกับ Jira/ClickUp/Trello: **บอร์ดสูงเท่าจอ · คอลัมน์ scroll ในตัวเอง** → หัวคอลัมน์ + ปุ่ม
   ท้ายคอลัมน์เห็นตลอดโดยไม่ต้องเพิ่มปุ่มที่หัว (ปุ่มยังอยู่ "ใต้การ์ดล่างสุด" ตามที่พีสั่ง)
   - --bd-col-max: bdFitCols() (shared.js) วัดพื้นที่จริงที่เหลือใต้ board ให้ = ไม่ใช้เลขเดา ·
     fallback CSS (calc(100dvh - 260px)) ครอบกรณี JS ไม่ทำงาน
   - ปุ่ม sticky bottom:0 = safety net เมื่อคอลัมน์ยังยาวเกินกรอบ (JS ปิด/จอสั้นกว่า 320px+chrome) */
.bd-col {
  width:256px; flex-shrink:0;
  background:var(--surface-2); border-radius:var(--r); padding:8px;
  display:flex; flex-direction:column; gap:8px;
  max-height:var(--bd-col-max, calc(100dvh - 260px));
}
.bd-col-head { display:flex; align-items:center; gap:8px; padding:4px 8px; }
/* S41 Wave4a (B5): quick-add การ์ดต่อคอลัมน์ — full-width dashed, hit-area 44px (§0.10), border-control (1.4.11) */
.bd-col-add { display:flex; align-items:center; justify-content:center; gap:6px;
  width:100%; min-height:44px; font-size:12px; font-weight:500; color:var(--text-2);
  text-decoration:none; border:1px dashed var(--border-control); border-radius:10px;
  padding:8px 12px; box-sizing:border-box;
  /* H3: sticky ท้ายคอลัมน์ — พื้นหลังต้อง "ทึบ" (สีเดียวกับคอลัมน์) ไม่ให้การ์ดทะลุขึ้นมาซ้อนตัวอักษร */
  position:sticky; bottom:0; z-index:1; flex-shrink:0; background:var(--surface-2); }
.bd-col-add:hover { border-color:var(--primary-light); color:var(--primary-solid); background:var(--surface); }
.bd-col-add i.fi { color:inherit; }
.bd-count {
  font-size:11px; font-weight:500; color:var(--text-2);
  background:var(--surface); border-radius:999px; padding:2px 8px; min-width:22px; text-align:center;
}
.bd-col-body {
  display:flex; flex-direction:column; gap:8px; min-height:48px; border-radius:12px;
  /* H3: คอลัมน์สูงเท่ากรอบ → ส่วน "การ์ด" เป็นตัว scroll เอง (หัว+ปุ่มท้ายคอลัมน์คงที่)
     padding-bottom = ระยะกันการ์ดใบล่างสุดถูกปุ่ม sticky บัง */
  flex:1 1 auto; overflow-y:auto; padding-bottom:4px;
  /* Container query (DS §6.5 / DEC-068#2) — .bd-card ปรับตาม "พื้นที่จริงของ column ตัวเอง"
     ไม่ใช่ viewport: ใช้ตัวนี้เป็น container เพราะเป็น element ที่ห่อ card โดยตรง (width = .bd-col
     ลบ padding 8px×2 แล้ว) → รองรับทั้ง board ต่อ list (256px/224px มือถือ) และ cross-view (/everything,
     ปัจจุบัน column กว้างเท่ากันทั้งคู่ — container query จึงพร้อมขยับถ้า column กว้างขึ้นในอนาคตด้วย
     ไม่ต้องแก้ CSS เพิ่ม) */
  container-type:inline-size; container-name:bd-card;
}
.bd-card {
  position:relative; /* A5: anchor ของ stretched-link (.bd-card-open::before) */
  display:flex; flex-direction:column; gap:8px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:12px; text-decoration:none; color:var(--text);
  box-shadow:var(--shadow-card); cursor:grab;
  transition:border-color .15s;
}
.bd-card:hover { border-color:var(--primary-light); }
.bd-card--drag { opacity:.5; }
.bd-card-top { display:flex; align-items:flex-start; gap:8px; }
.bd-title { flex:1; min-width:0; font-size:var(--fs-sm); font-weight:500; overflow-wrap:anywhere; }
.bd-lock { color:var(--text-2); font-size:12px; flex-shrink:0; margin-top:2px; }
/* A5: stretched-link — ทั้งใบการ์ดคลิกเปิด detail (hit-area) แต่ลิงก์จริง = <a class="bd-card-open"> (ไม่ nested-interactive) */
.bd-card-open { color:inherit; text-decoration:none; }
.bd-card-open::before { content:""; position:absolute; inset:0; z-index:1; border-radius:12px; }
.bd-card:hover .bd-card-open { color:var(--text); }
/* keyboard focus บนลิงก์เปิดการ์ด — วาด ring ที่ ::before (คลุมทั้งใบ) วงเดียว ไม่ต้องพึ่ง :has */
.bd-card-open:focus-visible { outline:none; }
.bd-card-open:focus-visible::before { outline:2px solid var(--primary-solid); outline-offset:2px; }
/* A5: ปุ่ม kebab — z-index สูงกว่า stretched-link + touch ≥44px (SC 2.5.8) · icon อยู่นอกลิงก์ */
.bd-card-menu {
  position:relative; z-index:2; flex-shrink:0;
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; margin:-10px -10px -10px 0; /* touch 44 แต่ไม่ดันความสูง card-top */
  border:none; background:transparent; color:var(--text-2); border-radius:8px;
  cursor:pointer; font-size:15px; line-height:0;
}
.bd-card-menu:hover { background:var(--surface-2); color:var(--text); }
.bd-card-menu:focus-visible { outline:2px solid var(--primary-solid); outline-offset:-2px; color:var(--text); }
/* A5: divider ในเมนู kebab (ovOpen popover/sheet) */
.bd-menu-sep { border:none; border-top:1px solid var(--border); margin:4px 6px; }
/* A5: menuitem ที่ปิด (การ์ดหัว/ท้ายคอลัมน์) — reuse .sheet-opt */
.sheet-opt[disabled], .sheet-opt[aria-disabled="true"] { color:var(--text-2); opacity:.45; cursor:not-allowed; }
.sheet-opt[disabled]:hover { background:transparent; }
/* utility: อ่านโดย screen reader เท่านั้น (live-region announce ไม่โชว์ banner) */
.sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
}
.bd-meta, .bd-chips { display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
.bd-foot { display:flex; align-items:center; gap:8px; font-size:11px; color:var(--text-2); }
.bd-mini { display:inline-flex; align-items:center; gap:4px; }

/* container แคบ (มือถือ .bd-col 224px → เหลือ ~208px หลังหัก padding) — footer meta (assignee/estimate/
   due) แน่นเกินไปเสี่ยงล้น/บีบทับกัน → ให้ wrap ได้แทน (ไม่ซ่อนข้อมูลใด ๆ — แค่ตกบรรทัด) + กระชับ padding
   เล็กน้อย ไม่กระทบ layout การ์ดที่ผู้ใช้ชินอยู่ (comfortable ปัจจุบันไม่โดน rule นี้ — 240px > 216px) */
@container bd-card (max-width:216px) {
  .bd-card { padding:10px; }
  .bd-foot { flex-wrap:wrap; row-gap:4px; }
}

/* container กว้างขึ้น (เผื่ออนาคต — column กว้างกว่า 256px ปัจจุบัน เช่น cross-view ที่อาจปรับ width
   ทีหลัง หรือ panel ใหญ่ขึ้น) — เพิ่มช่องหายใจของ meta แถวล่าง ไม่ทำอะไรกับ layout วันนี้ (dormant จนกว่า
   จะมี container กว้างจริง — ปัจจุบันยังไม่มีจุดไหนกว้างถึง 300px) */
@container bd-card (min-width:300px) {
  .bd-card-top { gap:12px; }
  .bd-foot { gap:12px; }
}
.avatar--xs { width:24px; height:24px; font-size:11px; }
.lb-chip--sm { font-size:11px; padding:2px 8px; }

/* ---- Table (การ์ด = แถว · custom field = คอลัมน์ · inline edit) ----
   ┌─ .tbl-scroll = กล่องหุ้มตารางมาตรฐาน "ตัวเดียวของทั้งแอป" ─────────────────────────────
   │ จอแคบ = **เลื่อนแนวนอนในกล่องตัวเอง** (ไม่ลาม body) — ไม่ซ่อนอะไรทั้งนั้น
   │ ลืมใส่/ใส่ผิด อย่างแย่ที่สุดคือผู้ใช้ต้องปัดซ้ายขวา = ยังอ่านข้อมูลได้ครบ
   └─ .tbl-scroll--hascards = เติมเมื่อตารางนั้น **มีการ์ดสำรอง .tbl-mobile คู่กันจริง ๆ**
      → ซ่อนตารางที่ ≤768px แล้วให้การ์ดขึ้นแทน (ดู @media(max-width:768px) ด้านล่าง)
      ลืมเติม = เห็นทั้งตาราง(เลื่อนได้)และการ์ด ซ้ำกันหน่อย แต่ **ข้อมูลไม่หาย**

   ทำไมกลับด้านแบบนี้ (S56 · พีเคาะ): ของเดิม .tbl-scroll เป็นตัวที่ "ซ่อนตัวเองบนมือถือ" แล้วต้อง
   จำไปเติมการ์ดเอง — ลืมเมื่อไหร่ = ตารางหายทั้งก้อน เกิดมาแล้ว 4 รอบ (S36 P&L · S37 WeFin ·
   S56 WeDoc · S56 leave/holidays/projects/cost) จนต้องมี "รายการหนี้" ในเทสถึงจะคุมได้
   = API ที่ผิดโดยดีไซน์ (ค่าตั้งต้นอันตราย) → กลับด้านให้ **ค่าตั้งต้นปลอดภัย ส่วนพิเศษต้องขอ**
   ผลพลอยได้: ยุบ .plan-scroll / .fin-scroll / .wide-scroll / .fin-pl-scroll (4 คลาสที่เกิดมาเพื่อ
   "หนี" การซ่อนของ .tbl-scroll โดยเฉพาะ · สไตล์เหมือนกันเป๊ะทั้ง 4) มารวมเป็น .tbl-scroll ตัวเดียว
   → ได้ keyboard-scroll a11y (wgTblScrollA11y ใน shared.js ผูกกับ .tbl-scroll · WCAG 2.1.1) ฟรีทุกตาราง
   เทสดัก: prototype-go/guard_repeat_defects_test.go §คลาส 3 + ด่าน tools/uiguard กฎ B */
.tbl-scroll {
  overflow-x:auto; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); box-shadow:var(--shadow-card);
}
.tbl { border-collapse:collapse; width:100%; min-width:960px; }
/* C1: Table view ใช้ separate + spacing 0 — border-collapse:collapse ทำให้ sticky cell มี bleed
   ที่ collapsed border (เนื้อคอลัมน์ที่ scroll ผ่านโผล่ลอด) บน Chrome/Safari; scope เฉพาะ #tbl-wrap
   ไม่กระทบตาราง report/pivot/daily ที่ยังใช้ collapse */
#tbl-wrap .tbl { border-collapse:separate; border-spacing:0; }
.tbl-th {
  text-align:left; font-size:12px; font-weight:500; color:var(--text-2);
  padding:var(--tbl-cell-py) var(--tbl-cell-px); border-bottom:1px solid var(--border); white-space:nowrap;
  line-height:1.3;
}
.tbl-sort { display:inline-flex; align-items:center; gap:4px; color:inherit; text-decoration:none; }
.tbl-sort:hover, .tbl-sort--on { color:var(--primary-solid); }
.tbl-sort-i { opacity:.4; font-size:12px; }
/* REQ-S12 ข้อ 10 — Table drag reorder + resize (ClickUp-style) */
.tbl-th[data-col] { position:relative; cursor:grab; }
.tbl-th[data-col]:active { cursor:grabbing; }
.tbl-th.tv-dragging { opacity:.45; }
.tbl-th.tv-over { box-shadow:inset 2px 0 0 var(--primary-solid); }  /* เส้นบอกจุดวาง */
.tv-grip { position:absolute; top:0; right:0; width:8px; height:100%; cursor:col-resize; z-index:2; }
.tv-grip:hover { box-shadow:inset -2px 0 0 var(--primary-solid); }
.tbl-td {
  padding:var(--tbl-cell-py) var(--tbl-cell-px); border-bottom:1px solid var(--border);
  font-size:var(--fs-sm); vertical-align:middle; min-width:112px;
  /* ระดับ 1 quick-fix: cell ไม่ตัดคำ (ตกบรรทัด) — ตัด ellipsis + ให้ตารางเลื่อนแนวนอนแทน */
  max-width:220px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.tbl-row:last-child .tbl-td { border-bottom:none; }
.tbl-row:hover .tbl-td { background:var(--surface-2); }
/* Hybrid alignment (S27x — พีเคาะจาก research docs/design/research/01-tables-data.md P1):
   ตัวเลขเชิงปริมาณ (est/actual/manday/cost/%) ชิดขวาเสมอทุกตาราง ไม่ผูกกับ .tbl--report อีกต่อไป
   (เดิม scope เฉพาะ report → scratch/leave/daily header ชิดขวาแต่ตัว cell ชิดซ้าย = header ลอยไม่ตรงค่า, บั๊ก P1) */
.tbl-td--num { min-width:88px; text-align:right; }
.tbl-td--over { color:var(--alert-text); font-weight:600; } /* Total actual เกิน estimate (DEC-020/063) — S27 P0: alert-text (AA) */

/* sticky คอลัมน์แรก (checkbox + ชื่อการ์ด) — เลื่อนแนวนอนแล้วยังเห็นว่าแถวไหน (ดู pattern .tl-name)
   C1: bg ต้อง "ทึบสนิท" ทุก state ไม่งั้นเนื้อคอลัมน์ที่ scroll ผ่านโผล่ลอดใต้ sticky
   - z-index: cb(3) > title(2) > cell ที่ scroll ผ่าน (static, z auto) → ลำดับทับถูก
   - box-shadow ขอบขวา title = เส้นแบ่ง sticky/scroll (มาสก์ bleed ระดับ sub-pixel แบบ ClickUp) */
.tbl-th--cb, .tbl-td--cb {
  position:sticky; left:0; z-index:3; background:var(--surface);
  min-width:auto; max-width:none; width:44px;
}
.tbl-th--title, .tbl-td--title {
  position:sticky; left:44px; z-index:2; background:var(--surface);
  min-width:220px; max-width:320px; box-shadow:inset -1px 0 0 var(--border);
}
/* hover: sticky cell เปลี่ยนพื้นตามแถว (ทับ background:surface ของตัวเอง) */
.tbl-row:hover .tbl-td--cb, .tbl-row:hover .tbl-td--title { background:var(--surface-2); }
/* selected (checkbox ติ๊ก) — ไฮไลต์ทั้งแถว + sticky cell ทึบตรง state (color-mix = opaque, ปรับ light/dark เอง)
   selector รวม .tbl-td → ครอบ cb/title sticky ด้วย (specificity สูงกว่า base/hover) → ไม่มี bleed */
.tbl-row:has(.bulk-cb:checked) .tbl-td { background:color-mix(in srgb, var(--primary-solid) 9%, var(--surface)); }
.tbl-row:has(.bulk-cb:checked):hover .tbl-td { background:color-mix(in srgb, var(--primary-solid) 14%, var(--surface)); }
/* หน้า list (Table view) — จัดกลางทุก field (พีสั่ง S28 · เห็น Table view จริงแล้วเลือก center)
   scope เฉพาะ #tbl-wrap (specificity id > global rule) → report/pivot/leave/scratch/daily คง hybrid ไม่กระทบ */
#tbl-wrap .tbl-th, #tbl-wrap .tbl-td { text-align:center; }
#tbl-wrap .tbl-td .cell-input { text-align:center; }
/* S33 ข้อ 2 (พีเคาะ explicit): คอลัมน์ชื่อการ์ด (card name) — cell ชิดซ้าย, header คงจัดกลางเหมือนเดิม
   ต่างจาก standard ปกติ (ปกติ align header ตาม cell) แต่พียืนยันเอาแบบนี้ — scope เฉพาะ .tbl-td--title
   ไม่แตะ .tbl-th--title (ยัง inherit center จากกฎด้านบน) */
#tbl-wrap .tbl-td--title { text-align:left; }
.tbl-td--empty { text-align:center; color:var(--text-2); padding:32px 12px; }
.tbl-link {
  display:inline-flex; align-items:center; gap:8px;
  color:var(--text); text-decoration:none; font-weight:500; margin-right:8px;
  overflow-wrap:anywhere; /* B3: ชื่อไทยยาวไม่มีวรรค ตัดบรรทัดได้ (DS §3) */
}
.tbl-link:hover { color:var(--primary-solid); }

/* inline cell controls (custom dd + text input — ❌ ห้าม native select/date) */
.dd--cell { display:inline-flex; max-width:100%; }
.dd-cellbtn {
  display:inline-flex; align-items:center; gap:6px; max-width:100%;
  background:transparent; border:1px solid transparent; border-radius:8px;
  padding:4px 8px; font-size:var(--fs-sm); color:var(--text); text-align:left;
  transition:border-color .15s, background .15s;
}
.dd-cellbtn:hover { border-color:var(--border); background:var(--surface); }
.dd-cellbtn > i.fi:last-child { color:var(--text-2); font-size:12px; }
.dd-cellbtn--ro { cursor:default; }
.dd-cellname { min-width:0; overflow-wrap:anywhere; }
.cell-input {
  width:100%; min-width:72px; font-family:inherit; font-size:var(--fs-sm); color:var(--text);
  background:transparent; border:1px solid transparent; border-radius:8px; padding:4px 8px;
  transition:border-color .15s, box-shadow .15s;
}
.cell-input:hover { border-color:var(--border-control); } /* S27 Batch2: border-control (AA 1.4.11) — hairline เดิมไม่ผ่าน 3:1 */
.cell-input:focus {
  outline:none; border-color:var(--primary-solid);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); background:var(--surface);
}

/* ==== G2 (S34): Quick Edit Card (Gantt bar popup) — โหลดใน .modal-body (modalOpen) ====
   reuse .dd/.dd-cellbtn/.cell-input/.dd-menu จาก Table view · layout = label:value แนวตั้ง
   (mobile modal เต็มจอ = stack เหมือนเดิม) · control ให้ขอบชัด (form context ไม่ใช่ dense table) */
.qv-card { display:flex; flex-direction:column; gap:16px; }
.qv-top { display:flex; align-items:center; justify-content:space-between; gap:8px; }
.qv-chip .lb-chip { font-size:var(--fs-sm); }
.qv-ro-badge { display:inline-flex; align-items:center; gap:4px; font-size:var(--fs-micro); color:var(--text-2); }
.qv-ro-badge i.fi { font-size:12px; }
/* ชื่อการ์ด — textarea auto-grow */
.qv-title {
  width:100%; font-family:inherit; font-size:16px; font-weight:600; color:var(--text);
  line-height:1.4; overflow-wrap:anywhere; word-break:break-word;
  border:1px solid var(--border-control); border-radius:10px; padding:8px 12px; background:var(--surface);
  resize:none; box-sizing:border-box;
}
.qv-title:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.qv-title--ro { border-color:transparent; padding-left:0; padding-right:0; }
/* label:value row — desktop 2 คอลัมน์ (label 76px) · mobile stack */
.qv-field { display:grid; grid-template-columns:76px minmax(0,1fr); align-items:center; gap:8px 12px; }
.qv-label { font-size:var(--fs-sm); font-weight:500; color:var(--text-2); }
.qv-field .dd--cell { width:100%; }
.qv-field .dd-cellbtn { width:100%; justify-content:space-between; border-color:var(--border-control); background:var(--surface); min-height:36px; }
.qv-field .dd-cellbtn--ro { border-color:transparent; background:transparent; cursor:default; }
.qv-field .dd-label { display:inline-flex; align-items:center; gap:6px; min-width:0; overflow-wrap:anywhere; }
/* S52 (ซี — bug vertical-text): raw text node (เช่นชื่อ assignee ต่อจาก avatar span) ใน .dd-label ที่เป็น
   inline-flex + flex:1 (base .dd-label บน L1428) กลายเป็น anonymous flex item — overflow-wrap:anywhere
   เปลี่ยนกติกาคำนวณ automatic min-content ให้ยุบเหลือ "1 ตัวอักษร" ได้ (breaking-opportunity นับเข้า
   min-content ต่างจาก break-word — verify: css-tricks.com/almanac/properties/o/overflow-wrap) พอชื่อยาว
   (เช่น "PAT UX/UI") + คอลัมน์แคบกว่าที่คาด → browser เลือก wrap ทีละตัวอักษรต่อบรรทัด ดูเหมือนเขียนแนวตั้ง
   (ไม่ใช่ writing-mode bug) → mirror ellipsis pattern เดียวกับ .pv-viewbtn .dd-label (L3941, ปุ่มป้ายบรรทัด
   เดียวแคบเหมือนกัน): บังคับบรรทัดเดียว + ตัดด้วย ... แทนปล่อย wrap (white-space:nowrap ทำให้ overflow-wrap
   ไม่มีผลอีกต่อไปเพราะไม่มีโอกาสขึ้นบรรทัดใหม่ให้ break) */
.qv-field .dd-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* Date range (start → due) */
.qv-daterange { display:flex; align-items:center; gap:6px; width:100%; }
.qv-daterange .dd-cellbtn { flex:1; justify-content:center; }
.qv-arrow { color:var(--text-2); font-size:12px; flex-shrink:0; }
.qv-daterange--ro { color:var(--text); }
/* number (estimate) + read-only value */
.qv-num { display:inline-flex; align-items:center; gap:6px; width:100%; }
.qv-num .cell-input { border-color:var(--border-control); background:var(--surface); max-width:96px; text-align:right; }
.qv-unit, .qv-value { font-size:var(--fs-sm); color:var(--text); }
.qv-value--ro { color:var(--text-2); display:inline-flex; align-items:center; gap:6px; }
.qv-value--over { color:var(--alert-text); font-weight:500; }
.qv-ro-ic { font-size:12px; opacity:.7; }
/* footer */
.qv-foot { display:flex; justify-content:flex-end; border-top:1px solid var(--border); padding-top:12px; }
.qv-openfull { display:inline-flex; align-items:center; gap:6px; }
@media (max-width:767px) {
  .qv-field { grid-template-columns:1fr; gap:6px; }
  .qv-label { font-size:var(--fs-micro); }
}

/* ---- Filter bar (ระดับ 2 — เหนือ view, แชร์ทุก view BR-1) ---- */
.filter-bar { display:flex; align-items:center; flex-wrap:wrap; gap:8px; margin-bottom:10px; }

/* S10 (#9) — indicator วันขาด daily */
.daily-miss {
  background:var(--warn-bg); border:1px solid color-mix(in srgb, var(--warm) 35%, transparent); border-radius:12px;
  padding:10px 14px; margin-bottom:12px; display:flex; flex-direction:column; gap:8px;
}
.daily-miss-head { display:flex; align-items:center; gap:8px; font-size:var(--fs-sm); color:var(--warm-text); }
.daily-miss-head i.fi { font-size:16px; }
.daily-miss-days { display:flex; flex-wrap:wrap; gap:6px; }
.daily-miss-chip {
  display:inline-flex; align-items:center; gap:4px; font-size:12px;
  background:var(--surface); color:var(--text); border:1px solid var(--border);
  border-radius:999px; padding:3px 10px;
}
.daily-miss-chip i.fi { font-size:12px; color:var(--warm-text); }
.filter-btn { display:inline-flex; align-items:center; gap:6px; }
/* S34 fix(control-bar-align): root cause — ปุ่ม control (จัดกลุ่ม/เรียง/ตัวกรอง/ตั้งค่ามุมมอง/พอดีจอ ฯลฯ)
   เป็น flex item เดิม (flex-shrink:1 default) ไม่มี white-space:nowrap → เมื่อแถวแคบกว่าผลรวมปุ่ม (วัดจริง
   desktop 1280px ก็เกิด — filter-bar ถูกบีบเหลือ ~222px จาก 5 view-tab + title กิน 700px) label ยาว
   ("จัดกลุ่ม: ผู้รับผิดชอบ") หัก 2-3 บรรทัด ปุ่มสูงไม่เท่ากัน (116/96/57px ปนกับ 44px) = alignment เพี้ยน
   → ล็อก flex-shrink:0 (ปุ่มไม่ยอมบีบ คงความสูง 44px เสมอ) + white-space:nowrap (กันบรรทัดหักสำรอง)
   ปุ่มที่ไม่พอที่ = ตกไปทั้งก้อนบรรทัดใหม่ (parent .filter-bar/.tl-ctrl flex-wrap:wrap) แทนบีบตัวอักษร */
.filter-btn, .qf-btn, .grp-btn, .grp-allbtn, .vs-btn, .col-btn, .tl-zoom-fit {
  flex-shrink:0;
}
.filter-btn > span, .qf-btn > span, .grp-btn > span, .grp-allbtn > span,
.vs-btn > span, .col-btn > span, .tl-zoom-fit > span {
  white-space:nowrap;
}
.filter-chip {
  --chip-c:var(--primary-solid);
  display:inline-flex; align-items:center; gap:6px; text-decoration:none;
  cursor:pointer; max-width:260px;
}
.filter-chip:hover { filter:brightness(.97); }
.filter-chip-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.filter-chip > i.fi { font-size:12px; opacity:.7; }
.filter-clear { font-size:12px; color:var(--text-2); text-decoration:none; padding:0 4px; }
.filter-clear:hover { color:var(--primary-solid); }
.filter-spacer { flex:1; min-width:8px; }
/* search ในตาราง (DS §Search — icon absolute ในกล่อง) · scope เฉพาะ filter-search ไม่แตะ search อื่น */
.filter-search { position:relative; max-width:220px; }
.filter-search > i.fi {
  position:absolute; left:12px; top:50%; transform:translateY(-50%);
  color:var(--text-2); font-size:14px; pointer-events:none;
  display:inline-flex; align-items:center; line-height:1;
}
.filter-search input {
  width:100%; font-family:inherit; font-size:var(--fs-sm); color:var(--text);
  background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:8px 12px 8px 36px;
}
.filter-search input:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.col-btn { display:inline-flex; align-items:center; gap:6px; }
.filter-btn--on { border-color:var(--primary-solid); color:var(--primary-solid); }
.filter-btn--on > i.fi { color:var(--primary-solid); }
.filter-chip--open { --chip-c:var(--text-2); border:none; font-family:inherit; }

/* ---- S49 (compact mobile toolbar — ซี): ปุ่มรวม "ตัวกรอง & จัดเรียง" + ปุ่มค้นหา icon-expand (List) ----
   desktop ซ่อนเสมอ (control เดิม filter-btn/qf-btn/grp-btn/sort/unit/zoom ทำงานครบตามเดิม) ·
   มือถือ (@media ≤768 ด้านล่าง) re-show ทั้งคู่ — ดู docs/design/mobile-toolbar-compaction-spec-s49.md */
.fs-combo, .list-search-btn { display:none; }
.fs-combo { position:relative; }
.fs-badge {
  position:absolute; top:-6px; right:-6px; min-width:18px; height:18px; padding:0 5px;
  border-radius:999px; background:var(--primary-aa); color:#fff; font-size:11px; font-weight:600;
  display:inline-flex; align-items:center; justify-content:center; line-height:1;
}
.fs-badge[hidden] { display:none; }

/* ---- View Setting (ข้อ 4/5, S33) — ปุ่มขวาสุด filter-bar ทุก view (spec §1.2/§5) ----
   --on mirror .filter-btn--on (consistency NN/g#4 — ผู้ใช้คุ้น pattern "ปุ่มเป็นสีแดงเมื่อปรับค่าแล้ว") */
.vs-btn--on { border-color:var(--primary-solid); color:var(--primary-solid); }
.vs-btn--on > i.fi { color:var(--primary-solid); }

/* ---- View Setting panel — เนื้อหาใน overlay (ov-pop desktop / .sheet mobile — ซี เฟส 2)
   panel content ถูกสร้างฝั่ง client (vspanel.js) แทรกเข้า .sheet-body ที่ ovOpen()/sheetOpen() คืนมา
   (เหมือน grpMenu/colOpen เดิม) — .vs-panel เองไม่ต้อง overflow ซ้อน (คอนเทนเนอร์แม่จัดการ scroll ให้แล้ว
   ตาม DS §Bottom Sheet CRITICAL) */
.ov-pop--vs { width:min(360px, 92vw); max-height:min(70vh, 520px); }
.vs-panel { display:flex; flex-direction:column; padding:4px 14px 14px; gap:4px; }
.vs-head { display:flex; flex-direction:column; gap:6px; padding:8px 6px 12px; border-bottom:1px solid var(--border); margin-bottom:8px; }
.vs-head-top { display:flex; align-items:center; justify-content:space-between; gap:12px; }
.vs-title { font-size:15px; font-weight:600; color:var(--text); }
.vs-scope { font-size:11px; color:var(--text-2); }
.vs-body .sheet-section-h:first-child { padding-top:0; }
.vs-restore { border-top:1px solid var(--border); margin-top:8px; padding-top:12px; }
.vs-restore-btn { width:100%; justify-content:center; }
.vs-restore-confirm { display:flex; flex-direction:column; gap:10px; }
.vs-restore-q { font-size:var(--fs-sm); color:var(--text); overflow-wrap:anywhere; }
.vs-restore-actions { display:flex; gap:8px; }
.vs-restore-actions > button { flex:1; justify-content:center; }
/* mobile sheet: panel content กว้างเต็ม .sheet เดิม (460px cap) — padding ตาม spec §3.1 (16px) */
@media (max-width:768px) {
  .vs-panel { padding:4px 2px 4px; }
}

/* ---- Filter builder (ClickUp-style tree — S11) — เปิดใน bottom sheet ---- */
.fb-body { gap:0; }
.fb-tree { display:flex; flex-direction:column; gap:8px; padding:4px 4px 8px; }
.fb-group { display:flex; flex-direction:column; gap:8px; min-width:0; }
.fb-group--nested {
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:12px; padding:12px; gap:8px;
}
.fb-group-head { display:flex; align-items:center; justify-content:space-between; }
.fb-group-tag { font-size:12px; font-weight:600; color:var(--text-2); }
.fb-line { display:flex; align-items:flex-start; gap:8px; min-width:0; }
.fb-line--group { align-items:stretch; }
.fb-line--group > .fb-group { flex:1; min-width:0; }
/* prefix (เมื่อ / connector) — กว้างคงที่ให้ pill ทุกแถวเริ่มแนวเดียว (alignment) */
.fb-conj { flex:0 0 56px; display:flex; align-items:center; justify-content:flex-end; min-height:36px; }
.fb-where { font-size:var(--fs-sm); color:var(--text-2); font-weight:500; }
/* connector = ปุ่ม toggle ตรรกะ (และ/หรือ) — ตัวเต็มสี (white on primary) ให้อ่านชัด + สื่อ active
   (contrast: ก่อนหน้าใช้ tint+text แดง = 3.97:1 หลุด AA · white-on-primary = baseline ปุ่มทั้งแอป) */
.fb-conj-btn {
  font-family:inherit; font-size:12px; font-weight:600; cursor:pointer;
  background:var(--primary); color:#fff;
  border:1px solid transparent; border-radius:8px; padding:6px 10px; min-width:48px;
}
.fb-conj-btn:hover { filter:brightness(1.04); }
.fb-row { display:flex; align-items:center; flex-wrap:wrap; gap:6px; flex:1; min-width:0; padding:2px 0; }
.fb-pill {
  display:inline-flex; align-items:center; gap:4px; max-width:100%;
  font-family:inherit; font-size:var(--fs-sm); color:var(--text); cursor:pointer;
  background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:8px 10px;
}
.fb-pill:hover { border-color:var(--primary-light); }
.fb-pill-t { overflow-wrap:anywhere; word-break:break-word; min-width:0; }
.fb-pill > i.fi { font-size:12px; color:var(--text-2); flex-shrink:0; }
.fb-pill--field { font-weight:500; }
.fb-pill--op { background:var(--surface-2); }
.fb-x {
  display:inline-flex; align-items:center; justify-content:center; aspect-ratio:1/1; flex-shrink:0;
  width:30px; cursor:pointer; background:transparent; border:none; border-radius:8px; color:var(--text-2);
}
.fb-x:hover { background:var(--alert-bg); color:var(--alert); }
.fb-add { display:flex; flex-wrap:wrap; gap:8px; padding-left:56px; }
/* nested: rows ในกลุ่มย่อยก็มี .fb-conj 56px นำ → ปุ่ม add ต้อง indent 56px เท่ากันให้ตรงแนว pill (alignment) */
.fb-add-btn {
  display:inline-flex; align-items:center; gap:6px; font-family:inherit; font-size:var(--fs-sm); font-weight:500;
  color:var(--primary-solid); cursor:pointer; background:transparent; border:1px dashed var(--border);
  border-radius:10px; padding:8px 12px;
}
.fb-add-btn:hover { border-color:var(--primary-solid); background:color-mix(in srgb, var(--primary-solid) 6%, transparent); }
.fb-add-btn > i.fi { font-size:12px; }
/* saved filters */
.fb-saved-wrap { border-top:1px solid var(--border); margin-top:8px; padding:12px 4px 4px; display:flex; flex-direction:column; gap:8px; }
.fb-sec { display:flex; align-items:center; gap:8px; font-size:12px; font-weight:600; color:var(--text-2); }
.fb-saved { display:flex; flex-wrap:wrap; gap:8px; }
.fb-saved-empty { font-size:12px; color:var(--text-2); }
.fb-saved-chip { display:inline-flex; align-items:center; background:var(--surface-2); border:1px solid var(--border); border-radius:999px; padding-right:2px; }
.fb-saved-go { display:inline-flex; align-items:center; gap:6px; max-width:220px; font-family:inherit; font-size:12px; color:var(--text); cursor:pointer; background:transparent; border:none; padding:8px 8px 8px 12px; overflow-wrap:anywhere; word-break:break-word; text-align:left; }
.fb-saved-go > i.fi { font-size:12px; color:var(--text-2); }
.fb-saved-chip .fb-x { width:24px; }
/* footer */
.fb-foot { display:flex; align-items:center; justify-content:space-between; gap:8px; border-top:1px solid var(--border); margin-top:8px; padding:12px 4px 4px; position:sticky; bottom:0; background:var(--surface); }
/* floating menu (field/op/value picker · saved-name input) — z สูงกว่า sheet (4000) */
.fb-menu {
  position:fixed; z-index:4600; min-width:180px; max-width:280px; max-height:320px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:0 8px 28px rgba(0,0,0,.18); padding:6px; display:flex; flex-direction:column; gap:2px;
}
.fb-menu--input { flex-direction:row; gap:6px; padding:8px; min-width:220px; }
.fb-menu-opt {
  display:flex; align-items:center; gap:8px; width:100%; min-height:40px; text-align:left; font-family:inherit; font-size:var(--fs-sm);
  color:var(--text); cursor:pointer; background:transparent; border:none; border-radius:8px; padding:10px;
}
.fb-menu-opt:hover { background:var(--surface-2); }
/* selected: พื้น tint + text เต็ม contrast (--text) + เน้นด้วย weight (ไม่ใช้ text แดงจาง = หลุด AA) */
.fb-menu-opt--sel { background:color-mix(in srgb, var(--primary-solid) 10%, transparent); color:var(--text); font-weight:600; }
.fb-menu-t { overflow-wrap:anywhere; word-break:break-word; }
.fb-menu-opt > i.fi { font-size:var(--fs-sm); color:var(--text-2); flex-shrink:0; }
.fb-menu-empty { font-size:var(--fs-sm); color:var(--text-2); padding:12px; text-align:center; }
.fb-dot { width:12px; height:12px; border-radius:999px; flex-shrink:0; }
.fb-inp { flex:1; min-width:0; font-family:inherit; font-size:14px; color:var(--text); background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:8px 12px; }
.fb-inp:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
/* S32 — ปฏิทิน custom ใน floating menu (date filter) + ป้ายคั่นช่วง between */
.fb-menu--cal { min-width:264px; padding:8px; }
.fb-range-sep { align-self:center; font-size:var(--fs-sm); color:var(--text-2); padding:0 2px; }

/* sheet section header (ใช้ใน filter sheet — คั่นกลุ่ม field) */
.sheet-section {
  display:flex; align-items:center; gap:8px; font-size:12px; font-weight:600; color:var(--text-2);
  padding:12px 8px 4px; margin-top:4px;
}
.sheet-section:first-child { margin-top:0; }
.sheet-opt { text-decoration:none; color:inherit; }
.cbx--sheet { padding:12px 8px; border-radius:10px; min-height:44px; } /* touch target ≥44px (WCAG 2.5.5) */
.cbx--sheet:hover { background:var(--surface-2); }

/* ---- Pagination (S41 · DS synth-2 §Pagination) — Table view 50 แถว/หน้า ----
   จัดขวาใต้ตาราง · ปุ่ม touch ≥44px · current/hover = --primary · focus ring แดง (Non-negotiable §5)
   สีจาก CSS var เท่านั้น (dark mode ผ่าน token) · Even-Pixel */
.pgn {
  display:flex; align-items:center; justify-content:flex-end; flex-wrap:wrap;
  gap:12px 16px; padding:12px 4px 4px; margin-top:8px;
}
.pgn-info { font-size:var(--fs-sm); color:var(--text-2); }
.pgn-list { display:flex; align-items:center; gap:4px; list-style:none; margin:0; padding:0; }
.pgn-list li { display:flex; }
.pgn-btn {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:40px; height:40px; padding:0 10px; box-sizing:border-box;
  border:1px solid var(--border-control); border-radius:10px;
  background:var(--surface); color:var(--text); text-decoration:none;
  font-size:var(--fs-sm); font-variant-numeric:tabular-nums; cursor:pointer;
  position:relative;
}
/* ขยาย hit area เป็น ≥44px โดยไม่ขยายกล่องภาพ (DS §10 — ผ่าน padding/pseudo) */
.pgn-btn::after { content:''; position:absolute; inset:-2px; }
.pgn-btn--nav { min-width:40px; padding:0; font-size:1.125rem; }
.pgn-btn:hover { border-color:var(--primary-solid); color:var(--primary-solid); }
.pgn-btn--cur {
  /* text-bearing บนพื้น primary → --primary-aa (5.08:1 ผ่าน AA) เหมือน .btn-primary ไม่ใช่ --primary-solid (borderline) */
  background:var(--primary-aa); border-color:var(--primary-aa);
  color:#fff; cursor:default; font-weight:600;
}
.pgn-btn--cur:hover { color:#fff; }
.pgn-btn--disabled {
  color:var(--text-2); border-color:var(--border); opacity:.55;
  cursor:not-allowed; pointer-events:none;
}
.pgn-btn:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.pgn-ellipsis {
  display:inline-flex; align-items:center; justify-content:center;
  min-width:32px; height:40px; color:var(--text-2);
}

/* ---- mobile fallback: <768px ตาราง → List-first (DS §6.3 — ไม่ยัด 17 คอลัมน์จอแคบ) ---- */
.tbl-mobile { display:none; flex-direction:column; gap:8px; }
.tbl-mcard {
  display:flex; flex-direction:column; gap:8px; padding:12px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  text-decoration:none; color:var(--text); box-shadow:var(--shadow-card);
}
.tbl-mcard:hover { border-color:var(--primary-light); }
.tbl-mcard-top { display:flex; align-items:flex-start; gap:8px; }
.tbl-mcard-t { flex:1; min-width:0; font-size:14px; font-weight:500; overflow-wrap:anywhere; }
.tbl-mcard-meta { display:flex; align-items:center; flex-wrap:wrap; gap:8px; font-size:12px; color:var(--text-2); }
/* ปุ่มจัดการบนการ์ดมือถือ (ลบ/แก้ไข) — ดันไปชิดขวาของแถว · form ครอบปุ่มก็ใช้คลาสนี้ได้
   (S56: การ์ดสำรองของ /leave /holidays /projects ที่มี action ต่อแถว — .iconbtn เดิมให้ hit-area 44px อยู่แล้ว) */
/* gap:14px ไม่ใช่ 4px — .iconbtn กว้าง 30px + ::after inset:-7px = hit-area 44px ต่อปุ่ม
   ถ้า gap แคบกว่า 14px hit-area ของปุ่มข้าง ๆ จะ "ทับกัน" (แตะขอบดินสอ → โดนถังขยะ = ลบทิ้ง)
   14px ทำให้ 44px ของสองปุ่มชนกันพอดีโดยไม่ทับ (WCAG 2.5.8 target size) */
.tbl-mcard-act { margin-left:auto; display:inline-flex; align-items:center; gap:14px; }

/* ---- Timeline (Gantt ต่อ assignee — DEC-039, read-only positioning) ---- */
.tl-wrap { display:flex; flex-direction:column; gap:24px; }
.tl-scroll {
  overflow-x:auto; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:12px; box-shadow:var(--shadow-card);
  position:relative; /* S45 dep-viz: anchor ให้ SVG connector overlay (absolute) อ้างอิงได้ · scroll/sticky เดิมไม่กระทบ */
}
.tl-row { display:flex; align-items:stretch; min-width:max-content; }
.tl-row--head { border-bottom:1px solid var(--border); }
.tl-name {
  position:sticky; left:0; z-index:2;
  width:180px; flex-shrink:0; display:flex; align-items:center; gap:8px;
  padding:8px 12px; background:var(--surface); font-size:var(--fs-sm);
  border-right:1px solid var(--border);
}
.tl-name--head { font-size:12px; font-weight:500; color:var(--text-2); }
.tl-name-t { min-width:0; overflow-wrap:anywhere; }
/* S32: หัวราง左 แบบ group (chip/avatar/icon + count) + ปุ่มควบคุม จัดกลุ่ม/เรียง */
/* S34 fix(control-bar-align): เดิม nowrap (default) — ปุ่มลูกที่ตอนนี้ flex-shrink:0 แล้วไม่ยอมบีบ ถ้าแถวแคบกว่า
   ผลรวมปุ่มจะล้นออกนอกกล่อง (เห็นจริง mobile 375px: ทับ .page-bar-add) → wrap ให้ปุ่มที่ไม่พอที่ตกบรรทัดใหม่แทน */
.tl-ctrl { display:inline-flex; flex-wrap:wrap; gap:8px; align-items:center; }
/* G1 (S34): กลุ่ม zoom แกนเวลา (−/level/+ + พอดีจอ) — กลุ่มที่ 2 ต่อจาก group/sort · divider คั่น
   reuse .iconbtn (30px visual + ::after -7px = hit 44px) + .btn-ghost.btn-sm · light+dark ผ่าน token · even-pixel */
.tl-ctrl-sep { width:1px; height:20px; background:var(--border); margin:0 4px; flex-shrink:0; align-self:center; }
.tl-zoom-seg {
  display:inline-flex; align-items:center; flex-shrink:0;
  border:1px solid var(--border-control); border-radius:10px;
}
.tl-zoom-seg .iconbtn { border-radius:0; }
.tl-zoom-seg .iconbtn:first-child { border-top-left-radius:8px; border-bottom-left-radius:8px; }
.tl-zoom-seg .iconbtn:last-child  { border-top-right-radius:8px; border-bottom-right-radius:8px; }
.tl-zoom-level {
  min-width:36px; align-self:stretch; padding:0 6px;
  display:inline-flex; align-items:center; justify-content:center; text-align:center;
  font-size:var(--fs-micro); color:var(--text-2); font-variant-numeric:tabular-nums;
  border-left:1px solid var(--border); border-right:1px solid var(--border);
}
.iconbtn:disabled, .btn-ghost:disabled { opacity:.4; cursor:not-allowed; pointer-events:none; }
.tl-name-count { margin-left:auto; flex-shrink:0; font-size:var(--fs-micro); color:var(--text-2);
  background:var(--surface-2); border-radius:999px; padding:1px 7px; min-width:20px; text-align:center; line-height:1.5; }
.tl-name-link { color:var(--text); text-decoration:none; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tl-name-link:hover { color:var(--primary-solid); text-decoration:underline; }
.tl-name-ic { color:var(--text-2); flex-shrink:0; }
.tl-name .lb-chip { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.tl-grid {
  display:grid; position:relative;
  /* G1 (S34): ความกว้างต่อวัน = zoomable ผ่าน --tl-cell-w (ตั้งบน #tl-root ด้วย inline init script + zoom control)
     default 32px = ค่าเดิมเป๊ะ (fallback เมื่อไม่มี var — กัน regression) · cellWidthPx() (timeline.js) อ่าน DOM จริง
     → drag/resize math ถูกทุก zoom level โดยไม่ต้องแก้ JS · .tl-grid--head ใช้ class .tl-grid ร่วม = header align cell */
  grid-template-columns:repeat(var(--days), var(--tl-cell-w, 32px));
  grid-template-rows:repeat(var(--lanes, 1), 36px);
}
.tl-grid--head { grid-template-rows:40px; }
.tl-hd {
  grid-row:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end;
  font-size:11px; color:var(--text-2); padding-bottom:4px; border-left:1px solid var(--border);
}
.tl-hd em { font-style:normal; }
.tl-hd b { font-size:var(--fs-micro); color:var(--primary-solid); } /* S27 Batch2: 10px ต่ำกว่า Micro floor (DS §3) → 11px */
/* S44 E0 รุมเทส F1 (เอ): fill 14%/surface-2 เดิมเพียงอย่างเดียว fail SC 1.4.11 (surface-2 vs surface
   แค่ ~1.09-1.14:1 — a11y debt ก่อน S44, deploy แล้ว) → คง fill เดิม + เพิ่ม edge marker เข้ม border-control
   (คำนวณจริง: vs surface 3.42-3.53:1 / vs surface-2 3.10-3.15:1 — ผ่าน ≥3:1 ทั้ง 2 theme) โทนเทากลาง
   แยกจาก holiday (แดง primary-solid ด้านล่าง) ให้แยก 3 สถานะ (ทำงาน/เสาร์-อาทิตย์/หยุด) ได้ด้วยตา */
.tl-hd--we { background:var(--surface-2); border-left:2px solid var(--border-control); border-right:2px solid var(--border-control); }  /* [S47 เอส] top/bottom → left/right: .tl-cell กิน grid-row:1/-1 (แนวตั้ง) — border แนวนอนเดิมลอยเป็น "เส้นแปลก" ไม่ตรงแถว · แกนคอลัมน์ถูกต้อง (precedent .tl-hd--today border-left) */
.tl-hd--today { color:var(--primary-solid); font-weight:600; }
/* G1 Phase 2 (S34): header 2-tier (สัปดาห์/เดือน) — แถวบน=แถบหน่วยใหญ่ (band) · แถวล่าง=หน่วยย่อย (sub)
   S34 auto-unit: แถวล่างสูงขึ้น (22→28) รับเลขสัปดาห์ "W38" ใต้ช่วงวันในหน่วยสัปดาห์ (เดือน = สูงเกินนิดหน่อย ยอมรับได้) */
.tl-grid--head2 { grid-template-rows:16px 28px; }
.tl-hd-band {
  grid-row:1; display:flex; align-items:center; padding:0 8px;
  font-size:var(--fs-micro); font-weight:600; color:var(--text-2);
  border-left:1px solid var(--border); white-space:nowrap; overflow:hidden;
}
.tl-hd--sub { justify-content:center; padding-bottom:3px; gap:1px; }
.tl-hd--sub em { font-size:11px; white-space:nowrap; }
/* S34: เลขสัปดาห์ ISO ("W38") — de-emphasize ใต้ช่วงวัน (secondary meta) · tabular กันเด้ง · light+dark ผ่าน token */
.tl-hd-wk { font-size:11px; line-height:1; color:var(--text-2); opacity:.72; font-variant-numeric:tabular-nums; white-space:nowrap; }
.tl-hd--today .tl-hd-wk { color:var(--primary-solid); opacity:1; }
/* coarse cell (สัปดาห์/เดือน): เส้นคั่นคอลัมน์เข้มขึ้นเล็กน้อยให้อ่านช่วงออก · ไม่มี weekend shading */
.tl-grid--coarse .tl-cell--col { border-left:1px solid var(--border); }
.tl-cell { grid-row:1/-1; border-left:1px solid var(--border);
  /* S33 (ซี — motion): highlight ตอนลากผ่านมี transition (เดิมสลับ class ทันที = กระพริบ) */
  transition:background-color var(--ease-standard) .12s, box-shadow var(--ease-standard) .12s; }
/* S44 E0 รุมเทส F1 caveat (เอ): mirror .tl-hd--we ด้านบน — border-control top+bottom เต็มความสูงคอลัมน์
   (.tl-cell มี grid-row:1/-1 อยู่แล้ว → ขอบบน/ล่างไปโผล่ที่ขอบนอกสุดของ grid ชนพื้น --surface ของ .tl-scroll
   ตรง target contrast ≥3:1 ที่คำนวณไว้) */
.tl-cell--we { background:var(--surface-2); border-left:2px solid var(--border-control); border-right:2px solid var(--border-control); }          /* weekend แรเงา + edge marker [S47 เอส] top/bottom→left/right: cell แนวตั้ง grid-row:1/-1 — border แนวนอนเดิม = "เส้นแปลก" ลอยไม่ตรงแถว */
/* N1 (รุมเทส เอ, regression): today = box-shadow(inset) แทน border-left — กันชน property กับ weekend
   ที่ S47 axis-flip เปลี่ยนเป็น border-left/right (2231/2254): เดิม today border-left ทับ weekend border-left
   เหลือ border-right เทา = กรอบ 2 สีเบี้ยว · box-shadow คนละ property → today+weekend อยู่ร่วมกันได้
   ปลอดภัย: ไม่มี .tl-cell:hover เปลี่ยน box-shadow → static ไม่กระพริบ (transition 2250 = smooth ตอน dragover เท่านั้น)
   · dragover (.tl-cell--dragover 0,2,0) ชนะ today (0,1,0) → ลากผ่าน today เห็น dragover ring แทนชั่วคราว = ถูกต้อง */
.tl-cell--today { box-shadow:inset 2px 0 0 0 var(--primary-solid); } /* today marker */
.tl-hd--today { box-shadow:inset 2px 0 0 0 var(--primary-solid); }
/* ---- Gantt bar (v2 — solid status fill, S42 — เอส spec §5, promote จาก mockup delay-baseline-defense
   พีอนุมัติ. Scope: เฉพาะ .tl-bar (Timeline/Gantt/Cross/Planning Board/Leave) — .cal-v-bar (Calendar)
   ไม่แตะ ตั้งใจ (คนละความหนาแน่นข้อมูล) · .tl-bar--frac/.tl-bar-badge ไม่แตะ (fractional/leave badge
   มี bg/color ของตัวเองแยกต่างหาก ไม่ inherit จาก .tl-bar) ---- */
.tl-bar {
  display:flex; align-items:center; margin:4px 2px; padding:0 8px; height:28px;
  border-radius:8px; font-size:11px; font-weight:600; text-decoration:none; /* weight 500→600: โปรขึ้น
     ตาม mockup — ไม่กระทบ a11y (11px ต่ำกว่า large-text threshold อยู่แล้ว bold ไม่ช่วย/ไม่ลด SC) */
  overflow:hidden; z-index:1; position:relative; /* [fix S32] default clip ใน bar — label ไม่ยื่นทับบาร์ข้างเคียงอีก
     (gantt standard: ClickUp/Linear/GitHub Projects timeline ตัด label ใน bar เป็น default, เผย full บน hover/tooltip) */
  background:var(--chip-c);                              /* [S42] เดิม color-mix 20% tint → solid เต็มสี */
  color:var(--chip-ink, var(--ink-dark));                 /* [S42] ink-map ตามตาราง เอส §5 — fallback ink-dark ถ้าไม่ override */
  /* [S42] ขอบเขตรูปทรง (1.4.11): border neutral --border-control (ไม่ผูกสีสถานะ) แทน chip-c 55% mix เดิม
     — WCAG ยอมรับ "border contrast ≥3:1 พอ ไม่บังคับ fill ข้างในต้อง contrast ≥3:1 ด้วย" (verify §1.2: 3.26:1
     light / 3.09:1 dark บน --surface) กันสีอ่อน (amber/yellow) solid fill ตกต่ำกว่า 3:1 กับ surface/surface-2 */
  border:1px solid var(--border-control);
  box-shadow:var(--shadow-tab);                            /* [S42] depth เบา ๆ ตาม mockup — reuse token เดิม */
}
/* ink override (white text) — สถานะที่ raw/solid fill เข้มพอสำหรับตัวอักษรขาว (1.4.3 ≥4.5:1)
   [S42 fix — คิด รุมเทส regression] :not(.tl-bar--frac) กัน leak เข้า frac (hatch bg ยังไม่ใช่ solid —
   ดูบล็อก "frac immunity" ท้ายไฟล์ส่วนนี้) */
.tl-bar[style*="--lb-blue"]:not(.tl-bar--frac), .tl-bar[style*="--lb-indigo"]:not(.tl-bar--frac),
.tl-bar[style*="--lb-red"]:not(.tl-bar--frac), .tl-bar[style*="--lb-brown"]:not(.tl-bar--frac), .tl-bar[style*="--lb-emerald"]:not(.tl-bar--frac)
{ --chip-ink:var(--ink-light); }  /* [S47] white-ink statuses = label + bottom-stripe ใช้ ink ขาว (--chip-ink) · [S47 R2] ลบ --prog-c เดิม (เลิก overlay darken → bottom-stripe พึ่ง --chip-ink ตรง — ดู .tl-bar-fill) */
/* fill override (darken 10%) — 3 สถานะที่ raw fail ทั้ง white และ dark ink เป็น solid: red/brown/emerald
   [S42 fix] :not(.tl-bar--frac) กัน solid ทับ hatch ของ frac (latent bug ที่ คิด flag — attribute+class
   (0,2,0) ชนะ .tl-bar--frac (0,1,0) เดิมไม่ว่าจะวางลำดับไฟล์ตรงไหน) */
.tl-bar[style*="--lb-red"]:not(.tl-bar--frac)     { background:var(--lb-red-solid); }
.tl-bar[style*="--lb-brown"]:not(.tl-bar--frac)   { background:var(--lb-brown-solid); }
.tl-bar[style*="--lb-emerald"]:not(.tl-bar--frac) { background:var(--lb-emerald-solid); }

/* [S45 — ซี, พีรีวิว Timeline: "highlight ไม่สวย · ชื่อการ์ดไม่อยากเป็นกล่องลอย ๆ · เอาแบบ roadmap"]
   เดิม S42 มี hover pop-out (.tl-bar:hover .tl-bar-t ป๊อปเป็น position:absolute pill ลอยนอกบาร์ +
   .tl-bar:hover เปิด overflow:visible/z-index:2 รับมัน) — ตัดออกทั้งกลไก ให้ .tl-bar เหลือพฤติกรรมตรงกับ
   .rm-bar (Roadmap) เป๊ะ: ไม่มี :hover override เลยทั้งระดับ container และ label ชื่อเต็มยังอ่านได้จาก
   native title tooltip (มีอยู่แล้วทุก .tl-bar/.tl-bar--frac/dfz-track — ดู templates/views.html,
   report_leave.html, scratch.html, defense.html) เหมือนที่ .rm-bar พึ่ง title เป็นทางเดียวอยู่แล้ว */
/* ชื่อการ์ด — ตัด ellipsis ในบาร์เสมอ (ไม่ป๊อปเผยเต็มบน hover อีกต่อไป — กัน "กล่องลอย" ตาม feedback).
   bar แคบ (1 วัน) เหลือ 1-2 ตัวอักษร ยอมรับได้ — มี title tooltip (native, ดู <a class="tl-bar">) ให้อ่านชื่อเต็ม */
.tl-bar-t {
  min-width:0; flex:1 1 auto; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  color:inherit;   /* [S42] เดิม color:var(--text-2) คงที่ → inherit ตาม ink ของแท่ง (--chip-ink) ให้ label อ่านออกเสมอบน solid fill */
  position:relative; z-index:1;   /* [S47] label z:1 · [S47 R2] .tl-bar-fill เปลี่ยนเป็น bottom-stripe z:1 (3px ล่าง) — label z:1 source หลัง stripe = paint เหนือเมื่อ overlap (ตัวอักษรไม่โดน stripe บัง) */
}
/* S44 E0 — short-est warning icon ในบาร์ Gantt (soft-nudge แบบ TeamGantt/Float, ไม่บล็อก)
   dual-encode: shape triangle-warning + native tooltip (title) — ไม่พึ่งสีอย่างเดียว
   inherit --chip-ink = contrast รับประกันทุกสีสถานะ
   S44 รุมเทส M1 fix (เต๋ major): เดิม icon เป็น flex-item ก่อนหน้า label — บาร์ 1 วัน (32px, เหลือ 16px
   หลังหัก padding 8×2) icon(12px)+gap กินเกือบหมด label เหลือ ~2px = ชื่องานหายทั้งหมด (verify จาก
   grid-column จริง) + มือถือไม่มี hover เห็นแค่สามเหลี่ยมลอยไม่รู้งานไหน → ย้าย icon ออกจาก flex flow
   (position:absolute มุมขวาบน) ให้ label (.tl-bar-t flex:1 1 auto) ได้พื้นที่คืนเต็มไม่ต้องหักให้ icon
   อีก · border cue (.tl-bar--warn ด้านล่าง) เป็นตัวช่วยหลัก "เห็นได้จากขอบ" ทำงานบนมือถือด้วย ไม่พึ่ง hover
   · z-index:3 กันชนกับ .tl-rz resize handle (z-index:2 มุมขวาเดียวกัน) — [S45] เดิมยังกันซ้อนกับ hover-pill
   ของ label ด้วย แต่ hover-pill ถูกตัดออกแล้ว (ดูคอมเมนต์ .tl-bar-t ด้านบน) เหตุผลนั้นตกไป เหลือแค่กัน resize handle */
.tl-bar-warn {
  position:absolute; top:2px; right:2px; z-index:3;
  flex-shrink:0; font-size:12px; color:inherit; line-height:1;
}
/* S44 รุมเทส M1 fix (Minor-4 เดิม: class ว่างไม่มี CSS มาด้วยตั้งแต่ build แรก) — edge cue หลักของบาร์เตือน
   แทนที่การพึ่ง icon กิน label ด้านบน · ใช้ outline (นอก border-box) ตั้งใจ ไม่ใช่ box-shadow inset ที่จะไป
   ทับซ้อนบน --chip-c (สีสถานะ 14+ สี ต้องคำนวณ contrast ทีละสี ไม่ scale) — outline วาดบนพื้นเซลล์ Gantt
   รอบข้างแทน (surface/surface-2/holiday-fill) ซึ่งเป็นชุดพื้นหลังจำกัด verify ครบได้จริง
   [S45 — ซี soften, พีรีวิว "highlight ไม่สวย/ฉูดฉาด"]: เดิม outline:2px solid var(--warm-text) offset:2px
   ตรงสูตร focus-ring ทั้งระบบเป๊ะ แต่บาร์เตือนโผล่พร้อมกันได้หลายสิบแท่งต่อจอ (list ที่ใช้ verify มี 71/181
   บาร์ = warn) ความเข้ม+ช่องลอย 2px แบบ focus-ring เลยดูเหมือนทุกบาร์ "error/selected" พร้อมกัน เกินกว่า
   "soft-nudge" ที่ตั้งใจ (comment ด้านบนสุดของ section) — คง a11y ครบ (icon+tooltip+border cue) ลดความจัด
   2 ทางแทนการเอาออก: (1) offset 2px→0 ให้เส้นแนบชิดขอบบาร์ (อ่านเป็น "ขอบเน้น" ไม่ใช่ "ป้ายลอย")
   (2) เจือสีด้วย color-mix 85% กับ transparent ให้นุ่มลง — คำนวณ WCAG relative-luminance จริงทุกคู่ไม่ปัด:
   worst-case (light vs holiday-fill) 4.32:1 เดิม → 3.36:1 หลังเจือ ยังผ่าน ≥3:1 (1.4.11) สบาย ทุกพื้นหลัง
   ที่เป็นไปได้ (light vs surface 4.31:1, vs surface-2 3.90:1 · dark vs surface/surface-2/holiday
   6.68-7.97:1 เหลือ headroom สูงอยู่แล้ว) offset:0 ยังนับเป็น Even-Pixel (0 = เลขคู่) */
.tl-bar--warn {
  outline:2px solid color-mix(in srgb, var(--warm-text) 85%, transparent);
  outline-offset:0;
}

/* [S42 fix — คิด รุมเทส regression finding, MAJOR] .tl-bar--frac (leave ครึ่งวัน, F7 เดิม) ก็มี class
   .tl-bar ด้วย → v2 solid-fill ink-map/label-inherit ข้างบนรั่วเข้ามา (blue ink-override + .tl-bar-t
   color:inherit) ทำให้ label ลากิจ(น้ำเงิน)ครึ่งวันกลายเป็นตัวขาวบนพื้น hatch จาง ≈1.6:1 fail 1.4.3
   (ก่อน S42 อ่านออกปกติ = regression). frac ยังใช้ hatch เดิม 100% (ไม่ใช่ solid) — "ภูมิคุ้มกัน" ให้ frac
   ไม่โดน v2 logic แตะเลยทั้ง 2 จุด (ink/label), กันสีสถานะไหนก็ตามที่ map มาในอนาคตด้วย (ไม่ใช่
   แค่ blue ที่เจอวันนี้) — เสริมกับ :not(.tl-bar--frac) ที่ใส่ไว้แล้วในตัว ink-override/fill-override ด้านบน
   [S45 — ซี] จุดที่ 3 เดิม (hover-pill) หลุด scope ไปเอง เพราะ hover pop-out ทั้งกลไกของ .tl-bar ถูกตัดออก
   แล้ว (feedback พี "กล่องลอย" — ดูคอมเมนต์ .tl-bar-t ด้านบนสุดของ section) ไม่เหลือ :hover override ให้
   frac ต้อง immune จุดนั้นอีกต่อไป: */
.tl-bar--frac{ --chip-ink:var(--ink-dark); }              /* [S42 fix] เดิม text-2 มืดพอบน hatch จางทุกสีอยู่แล้ว ไม่ต้องมี ink-light */
.tl-bar--frac .tl-bar-t{ color:var(--text-2); }            /* [S42 fix] คืน label สีเดิม (ไม่ inherit ink ของ v2) */

/* ---- Style E "Status Pro+" (S47 — เด): progress-fill (effort %) + avatar-in-bar ---- */
/* progress-fill = bottom-stripe "มิเตอร์ effort" ล่างแท่ง สูง 3px · [S47 R2 · A→B decision: เต๋ เคาะ bottom-stripe แทน left-fill overlay]
   🔴 B2 (รุมเทส เอ SC 1.4.11 + เต๋ Nielsen#4): overlay เดิม (lighten/darken --prog-c) ขอบ filled-vs-unfilled แค่ 1.1-1.4:1 (เอ)
      + ทิศสลับ (dark-ink สว่างขึ้น / white-ink มืดลง = ไม่ยูนิฟอร์ม เต๋ M1) → เปลี่ยนเป็น bottom-stripe:
   · สี stripe = --chip-ink (ink ตัวเดียวกับ label ที่ เอ verify ≥4.5:1 vs พื้นแท่งทุกสถานะ) → ขอบ ≥3:1 อัตโนมัติทุก 13 สี (ไม่ต้อง derive per-color)
   · filled (0→prog) = ink ทึบ · track (prog→100) = ink จาง 18% · hard-stop ที่ prog = ขอบคม อ่านเป็น "มิเตอร์" ทิศเดียวทุกแท่ง (แก้ M1)
   · [S47 R3 · เอ re-verify SC 1.4.11] ขอบ filled-vs-track ≥3:1 ครบ 13 สถานะ×2 theme (worst=blue) · track 22%→18% เผื่อ margin blue 3.16→3.39 (สีอื่นไม่กระทบ)
   · z-index:1 · stripe 3px ล่างสุด ไม่ทับ label กลางแท่ง (label z:1 source หลัง = paint เหนือเมื่อ overlap) · clipped ด้วย overflow:hidden ของ .tl-bar (มุมโค้ง)
   ⚠️ semantic = effort% (actual/est) — ถ้าพีอยากได้ schedule/done% แทน แก้ที่ views.go tlProgressPct (flag) */
.tl-bar-fill {
  position:absolute; left:0; right:0; bottom:0; height:3px; z-index:1; pointer-events:none;
  /* filled 0→prog = ink ทึบ · track prog→100 = ink จาง .18 · hard-stop = ขอบคม */
  background:linear-gradient(to right,
    var(--chip-ink,var(--ink-dark)) var(--prog,0%),
    color-mix(in srgb, var(--chip-ink,var(--ink-dark)) 18%, transparent) var(--prog,0%));
}
/* avatar ต้นแท่ง (Style E) — เฉพาะ track ที่หัวรางซ้าย≠คน (flat/space/list/status/priority) — template gate ด้วย $kind
   18px (Even-Pixel) พอดี bar 28px · พื้น --primary-aa (ตัวขาว 5.08:1 AA — ไม่ใช้ gradient ที่ฝั่งอ่อน fail)
   box-shadow ring = --chip-ink → แยก avatar coral จากพื้นแท่ง (ขาวบนแท่งเข้ม / เข้มบนแท่งอ่อน) กันจมโดยเฉพาะแท่ง red
   flex-shrink:0 = คงอยู่บนแท่งแคบ (label ellipsis หายก่อน = graceful · ชื่อเต็ม+คน อยู่ใน title tooltip ของแท่ง) */
.tl-bar-av {
  position:relative; z-index:1; flex-shrink:0;
  width:18px; height:18px; margin-right:4px; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--primary-aa); color:var(--ink-light); font-size:11px; font-weight:600; line-height:1; /* N4 (รุมเทส เอ): #fff→ink-light token (convention ไฟล์) · 10→11px = DS Micro floor (single-initial บน 18px avatar รับได้ · mirror .avatar--xs 11px) */
  box-shadow:0 0 0 1px var(--chip-ink,var(--ink-dark));
}

/* ============================================================================
   [S48 — เด] Gantt/Timeline "Soften" — mockup เอส (gantt-soften-explore.html) · พีเคาะ 6 จุด
   SCOPE: #tl-root เท่านั้น (views timeline_view + scratch scratch_gantt) — Leave (.tl-wrap.lvr-gantt)
   + Defense (.dfz-track) คงลุค solid เดิม ไม่แตะ · #tl-root .tl-bar (specificity 1,1,0) ชนะ global
   ink-override/fill-override (0,3,0) ด้วย ID → Option C ทับ solid+white-ink อัตโนมัติ ไม่ต้องแก้ global block ด้านบน
   PROTOTYPE-TEMP: ทีม prod rebuild — ค่า chip-tint/geometry จาก mockup DS ที่ เอส verify (bg16%/ink55%,yellow45%) */
/* [1] สีแท่ง Option C — chip-tint pastel + ตัวอักษรเข้ม (เลิก solid saturated S42 + เลิก white-ink override) */
#tl-root .tl-bar {
  background:color-mix(in srgb, var(--chip-c) 16%, var(--surface));  /* solid → tint จาง (reuse สูตร .lb-chip 16%) */
  --chip-ink:color-mix(in srgb, var(--chip-c) 55%, var(--text));      /* label+stripe+ring = สีสถานะเข้ม 55% (เอส worst amber label 4.65:1) */
  overflow:visible;                                                   /* [6] label ยื่นนอกแท่งได้ (fill หุ้มใน .tl-bar-clip แยก) */
  height:24px; margin:8px 2px;                                        /* [5] geo-new: บาง 28→24 · space ข้างละ 8 (lane 40) */
}
#tl-root .tl-bar[style*="--lb-yellow"] {
  --chip-ink:color-mix(in srgb, var(--chip-c) 45%, var(--text));      /* yellow สว่างสุด → ink เข้มขึ้น (.lb-chip[yellow] pattern) */
}
/* [5] lane 36→40 (bar24 + margin8×2) รับ geometry ใหม่ = .rm-lane--task · :not(--head) กันชนหัวตาราง (40px แถวเดียว) */
#tl-root .tl-grid:not(.tl-grid--head) { grid-template-rows:repeat(var(--lanes,1),40px); }

/* [1b] จุดสีสถานะ 8px ต้นแท่ง — สีเต็ม chip-c (ไม่ tint) ช่วยมองไกลบนพื้น pastel · flex คงบนแท่งแคบ · z:1 เหนือ .tl-bar-clip */
#tl-root .tl-bar-dot {
  flex:0 0 8px; width:8px; height:8px; border-radius:999px;
  background:var(--chip-c); margin-right:6px; position:relative; z-index:1;
}

/* [#2 S48-fix] warn icon: absolute→leading inline (เกาะหน้า label ตาม feedback พี) — เหตุผล absolute เดิม (icon กิน label บนแท่งแคบ)
   หมดไปเพราะ #3 ย้าย label ที่ล้นไป trailing หลังแท่ง · flex-shrink:0 จาก base คงอยู่ (icon ไม่หด) · ลำดับ flex = [avatar][dot][warn][label]
   ⚠️ อย่าแก้ base .tl-bar-warn (:2316) — Leave/Defense ยังใช้ absolute
   [S48-refine] v2 (.tl2): เอา dot/avatar ออกจากบาร์แล้ว (template) — .tl-bar-warn (leading-inline นี้) เหลือใช้เฉพาะ
   edge-case บาร์ที่เริ่ม column แรกสุด (template gate `le .Start 1`) ตัวอื่นในกริดใช้ .tl2-bar-warn (นอกแท่ง) ด้านล่างแทน */
#tl-root .tl-bar-warn { position:static; top:auto; right:auto; z-index:auto; margin-right:4px; }

/* [S48-refine — item 2] ⚠ ShortEst หน้าบาร์ (v2 เท่านั้น) — namespace .tl2- ใหม่ ไม่ทับ .tl-bar-warn เดิม (scratch/Leave/Defense)
   absolute child ของ .tl-bar (position:relative + overflow:visible จาก S48 ด้านบน) ดันออกซ้ายด้วย transform
   pointer-events:none (mirror .dep-badge) กันขยายพื้นที่ hover/click ของลิงก์บาร์ไปทับเซลล์วันก่อนหน้า
   ความหมายให้ SR ผ่าน sr-only ที่วางคู่กันใน DOM (ไม่ใช่ icon นี้ที่ aria-hidden) — title บนบาร์มีข้อความเดียวกันสำรอง (hover) */
#tl-root .tl2 .tl2-bar-warn {
  position:absolute; left:0; top:50%; z-index:3;
  transform:translate(calc(-100% - 4px), -50%);
  font-size:12px; line-height:1; color:var(--warm-text); pointer-events:none;
}

/* [#4 S48-fix] freeze column: name เหนือทุก layer ใน .tl-scroll (bar z:1, dep-overlay z:2, dep-badge z:3)
   → bg ทึบ var(--surface) ทับ content ที่ scroll ลอดใต้ column · ❌ ไม่ใช้ isolation:isolate (จะพัง dep-connector layering S45)
   .tl-row/.tl-grid ไม่ใช่ stacking context → name(z5) composite ตรงใน .tl-scroll ชนะทุกตัว (critic confirm 1 บรรทัดพอ) */
#tl-root .tl-name { z-index:5; }

/* [#3 S48-fix] fit-or-overflow label (แทน straddle เดิมที่พีไม่เอา) — วัดจริงด้วย JS (timeline.js tlMeasureLabels)
   fit (พอดีในแท่ง) = default: overflow:hidden + ellipsis (ไม่คร่อมขอบ) · ล้น → JS toggle .tl-bar--label-out ยกทั้งก้อนไปหลังแท่ง
   pattern MS Project rightside / dhtmlx rightside_text · collision lane เดียว: แท่งถัดขวา (DOM หลัง sort by Start · bg tint opaque) paint ทับ label ยื่น */
#tl-root .tl-bar-t { overflow:hidden; text-overflow:ellipsis; }   /* [แก้จากเดิม overflow:visible] fit = ในแท่ง ไม่ straddle */
#tl-root .tl-bar--label-out .tl-bar-t {
  position:absolute; left:100%; top:0; height:100%;
  display:flex; align-items:center;
  margin-left:6px; max-width:none; overflow:visible; text-overflow:clip;
  z-index:1;                 /* คง bar-local (bar = relative z:1 = stacking context) → composite ต่ำกว่า .tl-name(z:5 #4) = scroll ลอดใต้ column ถูกทับ */
  pointer-events:none;       /* คลิกทะลุเปิดการ์ด (label trailing ไม่บังแท่ง) */
}
/* [V1 fix — เอ, SC 1.4.3] amber/yellow ink (55%/45%) ตอน label-out ยื่นตกบนพื้น holiday-fill (light) = 3.83/3.98 <4.5:1
   → เข้ม text เฉพาะ label-out (แก้สีตัวอักษรเท่านั้น ❌ ไม่แตะ --chip-ink หลัก กัน stripe/avatar-ring/dot เปลี่ยนสีตาม)
   specificity (1,4,0) ชนะ base · เอ recompute ผ่าน ≥4.5:1 ทุกพื้น (surface/weekend/holiday) ทั้ง 2 theme margin >0.8 */
#tl-root .tl-bar.tl-bar--label-out[style*="--lb-amber"] .tl-bar-t  { color:color-mix(in srgb, var(--chip-c) 40%, var(--text)); }  /* 3.83→5.37 */
#tl-root .tl-bar.tl-bar--label-out[style*="--lb-yellow"] .tl-bar-t { color:color-mix(in srgb, var(--chip-c) 30%, var(--text)); }  /* 3.98→5.93 */
/* [6] clip layer: หุ้ม bottom-stripe (.tl-bar-fill) ให้ยังตัดมุมโค้งแม้แท่ง overflow:visible · pointer-events:none ไม่ขวาง drag · z:0 ใต้ dot/avatar/label */
#tl-root .tl-bar-clip { position:absolute; inset:0; overflow:hidden; border-radius:inherit; z-index:0; pointer-events:none; }
/* [6] resize handle: แท่ง overflow:visible → มุมนอก handle โผล่พ้นมุมโค้ง → round มุมนอกให้ match inner-radius 7px (8px border 1px) */
#tl-root .tl-rz--l { border-radius:7px 0 0 7px; }
#tl-root .tl-rz--r { border-radius:0 7px 7px 0; }

/* [4] วันหยุด Path B — เอา border-control (edge marker) ออก · เหลือ fill surface-2 (จาง = decorative WCAG 1.4.11)
   dual-encode ย้ายไปตัวอักษรวัน (.tl-hd-dow จ/อ/พ/พฤ/ศ/ส/อา หัวคอลัมน์) → weekend อ่านออกผ่าน text
   ⚠️ today ที่เป็น weekend ด้วย: today marker = box-shadow inset (N1 S47) คนละ property → ยังเห็น */
/* [#1 S48-fix] weekend fill จางกว่านี้ (พีขอ "จางกว่านี้") — surface-2 40% mix surface (knob=40%, ปรับ 30–50% ได้)
   dow text (--text-2) บนพื้นใหม่ยังผ่าน ≥4.5:1 (light 4.98 / dark 5.61 — พื้นสว่างขึ้น = contrast ดีขึ้น) · fill = decorative WCAG 1.4.11
   ⚠️ critic N5: 40% อาจจางไปใน body cell (dual-encode dow อยู่แค่ header) — พีดู prod ตัดสิน knob */
#tl-root .tl-hd--we { border-left:0; border-right:0; background:color-mix(in srgb, var(--surface-2) 40%, var(--surface)); }
#tl-root .tl-cell--we { border-left:0; border-right:0; background:color-mix(in srgb, var(--surface-2) 40%, var(--surface)); }
#tl-root .tl-hd-dow { display:block; margin-top:1px; font-size:9px; line-height:1; color:var(--text-2); } /* [S48] 9px = mockup เอส (ต่ำกว่า DS micro 11px — flag เอ/เอส · secondary cue ในคอลัมน์ 32px + dual-encode กับเลขวัน) */

/* Timeline drag/resize (mirror .cal-v-bar/.cal-v-rz — token/สีเดียวกัน, even-pixel, light+dark ผ่าน CSS var)
   handle 8px ที่ขอบ bar (bar 1 วัน = 32px → handle 8px×2 กินราวครึ่ง ยอมได้: ลากกลางจับจาก label ที่ยื่น
   หรือ mousedown นอก handle) · grip จาง ๆ ถาวร เข้มตอน hover = affordance ยืด/หด */
.tl-bar[draggable="true"] { cursor:grab; }
.tl-bar--drag { opacity:.5; cursor:grabbing; }
/* G1 Phase 2 (S34): หน่วยสัปดาห์/เดือน = read-only positioning (Q4) — bar เป็นลิงก์เปิดการ์ด ไม่ลาก */
.tl-bar--ro { cursor:pointer; }
/* Task5-fix (เต๋ T1): double-class (0,2,0) ให้ชนะ .tl-cell--we/--hol (0,1,0) ทุก source-order
   ไม่งั้นลากผ่านวันหยุด/เสาร์-อาทิตย์ highlight หาย (--hol ประกาศทีหลัง dragover ในไฟล์) */
.tl-cell.tl-cell--dragover {
  background:color-mix(in srgb, var(--primary-solid) 12%, var(--surface-2));
  /* S33: ring คมชัดกว่าพื้นจาง 12% เดิมอย่างเดียว — เห็นชัดว่า "จะลงตรงไหน" (drop-target affordance) */
  box-shadow:inset 0 0 0 2px color-mix(in srgb, var(--primary-solid) 55%, transparent);
}
.tl-rz {
  position:absolute; top:0; bottom:0; width:8px; cursor:ew-resize; z-index:2;
  background:color-mix(in srgb, var(--chip-c) 16%, transparent); transition:background var(--ease-standard) .12s;
}
.tl-rz--l { left:0; }
.tl-rz--r { right:0; }
.tl-rz:hover { background:color-mix(in srgb, var(--chip-c) 45%, transparent); }
@media (max-width:768px) { .tl-rz { display:none; } } /* ไม่มี drag บนมือถือ (mirror calendar) */

/* ============================================================================
   Timeline v2 (S48) — 2-PANE SPLIT · mockup ยู · design-first แล้ว build
   หัวใจ: name pane (.tl2-left overflow-x:hidden) แยก DOM จาก timeline pane (.tl2-right)
     → bar ไม่มีพิกัดในโซนชื่อ = แก้ปัญหา name/label ซ้อน-ลอย "โดยโครงสร้าง" (ไม่ใช่ z-index patch)
   reuse engine เดิม 100%: .tl-grid/.tl-bar/.tl-cell/.tl-hd/tl_head/tl_cells ย้ายเข้า .tl2-right
     .tl2-* = โครง 2-pane + name-pane meta เท่านั้น (namespace แยก · ไม่ทับ scratch/Defense/Leave)
   parity เป๊ะ (deterministic server-กำหนด): head 52 · lane 48 · group-head 44 · ทั้ง 2 pane เท่ากัน
   token DS จริง (light+dark ผ่าน var · ❌ ไม่ hardcode สี) · PROTOTYPE-TEMP: dev-prod rebuild
   ============================================================================ */
/* [S49 — พี feedback "กล่อง Timeline เตี้ยไป เห็นแค่ ~15 แถว"] height เดิม clamp(360px,64vh,640px)
   cap ตายตัวที่ 640px แม้จอใหญ่แค่ไหน → เปลี่ยนเป็น "fill พื้นที่ viewport ที่เหลือจริง" ใต้ toolbar/filter-bar
   ผ่าน --tl2-fill-h (JS วัด getBoundingClientRect จริงของ #tl2 เทียบ .page ที่ครอบ — inline script ท้าย
   timeline_view, ฟังก์ชัน tlFitHeight รันทุก load/htmx-swap/resize) → แม่นกว่าเดา px คงที่ (filter-bar
   ความสูงไม่คงที่ ขึ้นกับจำนวน filter chip ที่ wrap) · fallback calc(100dvh - 300px) ไว้เผื่อก่อน JS รัน
   (เสี้ยววินาทีแรก) · min-height:280px = floor กันเตี้ยไปกรณี viewport เล็กมาก (≈header 52 + 4 แถวเลน) —
   ★ ตั้งใจไม่ใช้ floor สูงกว่านี้: verify จริงบนมือถือ 375×812 พบว่า floor 420 ดันสูงเกิน avail จริง (381px)
   ทำให้ล้นขอบล่างจอโดยไม่จำเป็น (toolbar มือถือ wrap หลายแถวกินพื้นที่เยอะอยู่แล้ว — pre-existing คนละ scope
   งานนี้) → 280 เตี้ยพอให้ avail จริงชนะ floor แทบทุกกรณี ปกติ ยกเว้น worst-case จริง ๆ เท่านั้น */
#tl-root .tl2 {
  --tl2-name-w:340px; --tl2-lane-h:48px; --tl2-head-h:52px; --tl2-grp-h:44px;
  display:flex; height:var(--tl2-fill-h, calc(100dvh - 300px)); min-height:280px; position:relative;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  box-shadow:var(--shadow-card); overflow:hidden;
}
/* ซ้าย: name pane — overflow-x:hidden เด็ดขาด (bar เข้าไม่ได้โดยโครงสร้าง) · scrollbar ซ่อน (เห็นตัวเดียวที่ขวา) */
#tl-root .tl2-left {
  flex:0 0 var(--tl2-name-w); width:var(--tl2-name-w);
  overflow-y:auto; overflow-x:hidden;
  background:var(--surface); border-right:1px solid var(--border);
  box-shadow:6px 0 12px -8px rgba(0,0,0,.16);      /* divider เงานุ่ม (DS §0.9: ❌ ไม่ใช้ left-accent strip) */
  scrollbar-width:none; position:relative; z-index:2;
}
#tl-root .tl2-left::-webkit-scrollbar { display:none; }
html[data-theme="dark"] #tl-root .tl2-left { box-shadow:6px 0 12px -8px rgba(0,0,0,.5); }
/* ขวา: timeline pane — scroll x+y (เจ้าของ scrollbar ที่ user เห็น) · canvas = anchor dep-overlay SVG */
#tl-root .tl2-right { flex:1 1 0; min-width:0; overflow:auto; position:relative; }
#tl-root .tl2-canvas { min-width:max-content; position:relative; }

/* header sticky top ทั้ง 2 pane (52px) — parity บังคับ (ไม่งั้น track แรกเหลื่อม) */
#tl-root .tl2-lhead {
  position:sticky; top:0; z-index:6; height:var(--tl2-head-h);
  display:flex; align-items:center; gap:8px; padding:0 16px;
  background:var(--surface); border-bottom:1px solid var(--border);
  font-weight:600; font-size:12px; color:var(--text-2);
}
#tl-root .tl2-lhead .grow { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#tl-root .tl2-rhead { position:sticky; top:0; z-index:5; background:var(--surface); border-bottom:1px solid var(--border); }
/* override header row height ให้ parity กับ lhead เป๊ะ:
   lhead = single element height 52 (border-box → border-bottom 1px กินใน 52 = box 52)
   rhead = wrapper + grid child · border-bottom อยู่บน wrapper (นอก grid) → grid ต้อง 51 (51+border1=52 ตรง lhead) */
#tl-root .tl2 .tl-grid--head  { grid-template-rows:calc(var(--tl2-head-h) - 1px); }
#tl-root .tl2 .tl-grid--head2 { grid-template-rows:20px calc(var(--tl2-head-h) - 21px); }
/* override lane 40→48 (name pane 2 บรรทัดหายใจ · roadmap-like) + center bar ใน lane (scoped · ไม่แตะ base .tl-bar) */
#tl-root .tl2 .tl-grid:not(.tl-grid--head) { grid-template-rows:repeat(var(--lanes,1), var(--tl2-lane-h)); }
#tl-root .tl2 .tl-grid:not(.tl-grid--head) .tl-bar { margin-top:0; margin-bottom:0; align-self:center; }

/* ---- แถวชื่อ (ซ้าย) = 2 บรรทัด: name + [status · range · slip] ---- */
#tl-root .tl2-lrow {
  height:var(--tl2-lane-h); display:flex; align-items:center; gap:10px; padding:0 14px;
  border-bottom:1px solid var(--border); min-width:0; text-decoration:none; color:var(--text);
}
#tl-root .tl2-lrow:hover { background:var(--surface-2); }
#tl-root .tl2-av {
  flex:0 0 28px; width:28px; height:28px; border-radius:999px;
  display:inline-flex; align-items:center; justify-content:center;
  background:var(--primary-aa); color:var(--ink-light); font-size:12px; font-weight:600; /* ink-light token (convention ไฟล์ N4) */
}
#tl-root .tl2-av--none { background:var(--surface-2); }
#tl-root .tl2-av--grp { flex-basis:24px; width:24px; height:24px; font-size:11px; }
#tl-root .tl2-lbody { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
#tl-root .tl2-lname { font-size:13px; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:1.3; }
#tl-root .tl2-lrow:hover .tl2-lname { color:var(--primary-aa); }
#tl-root .tl2-lmeta { display:flex; align-items:center; gap:6px; min-width:0; }
/* status chip (16% tint + 55% ink — สูตรเดียวกับ soften Option C bar · เอส verify ≥4.5:1) */
#tl-root .tl2-chip {
  display:inline-flex; align-items:center; gap:5px; padding:1px 8px; border-radius:999px;
  font-size:10px; font-weight:600; white-space:nowrap; flex:0 0 auto;
  background:color-mix(in srgb, var(--chip-c) 16%, var(--surface));
  color:color-mix(in srgb, var(--chip-c) 55%, var(--text));
}
#tl-root .tl2-chip[style*="--lb-yellow"] { color:color-mix(in srgb, var(--chip-c) 45%, var(--text)); }
#tl-root .tl2-cd { width:6px; height:6px; border-radius:999px; background:var(--chip-c); flex:0 0 6px; }
/* range start–due (tabular กันเลขเด้ง · --text-2 บน surface ≥4.5:1) */
#tl-root .tl2-mdue { font-size:10px; color:var(--text-2); white-space:nowrap; display:inline-flex; align-items:center; gap:3px; min-width:0; overflow:hidden; text-overflow:ellipsis; }
#tl-root .tl2-mdue i { font-size:11px; flex:0 0 auto; }
/* slip overdue chip อยู่ใน sticky pane = เห็นเสมอ (ยกจาก roadmap rm-slip) · ขวาสุด */
#tl-root .tl2-slip {
  flex:0 0 auto; display:inline-flex; align-items:center; gap:3px; margin-left:auto;
  font-size:10px; font-weight:600; padding:1px 7px; border-radius:999px; white-space:nowrap;
}
#tl-root .tl2-slip i { font-size:11px; }
#tl-root .tl2-slip--over { color:var(--alert-text); background:var(--alert-bg); }
/* PROTOTYPE-TEMP: reserved late-tier (warm) — ยังไม่ใช้ (slip ปัจจุบัน = overdue "over" เท่านั้น)
   ผูกเมื่อทำ baseline-slip (แผนเดิม vs actual · reuse baseline overlay S46) → late = เลื่อนจากแผนแต่ยังไม่เลย due */
#tl-root .tl2-slip--late { color:var(--warm-text); background:var(--warn-bg); }

/* ---- group mode — swimlane header (ซ้าย) + summary rollup (ขวา) ---- */
#tl-root .tl2-lgrp {
  height:var(--tl2-grp-h); display:flex; align-items:center; gap:8px; padding:0 14px;
  background:var(--surface-2); border-bottom:1px solid var(--border);
  font-size:13px; font-weight:600; color:var(--text);
}
#tl-root .tl2-gdot { width:12px; height:12px; border-radius:4px; flex:0 0 12px; }
#tl-root .tl2-gname { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
#tl-root .tl2-gname i { color:var(--text-2); }
#tl-root .tl2-cnt { font-size:10px; font-weight:600; color:var(--text-2); background:var(--surface); border-radius:999px; padding:2px 8px; }
#tl-root .tl2-rgrp {
  height:var(--tl2-grp-h); display:grid;
  grid-template-columns:repeat(var(--days), var(--tl-cell-w, 32px));
  align-items:center; background:var(--surface-2); border-bottom:1px solid var(--border);
  min-width:max-content; position:relative;
}
#tl-root .tl2-sum {
  position:relative; grid-row:1; height:26px; margin:0 2px; border-radius:8px;
  border:1px solid var(--border-control); overflow:hidden; box-shadow:var(--shadow-tab);
  background:color-mix(in srgb, var(--chip-c) 24%, var(--surface)); display:flex; align-items:center;
}
#tl-root .tl2-sum-fill { position:absolute; left:0; top:0; bottom:0; width:var(--pct,0%); background:var(--chip-c); }
#tl-root .tl2-pct {
  position:relative; margin:0 6px 0 auto; padding:2px 6px; font-size:10px; font-weight:600;
  color:var(--text); background:var(--surface); border:1px solid var(--border-control); border-radius:999px; z-index:1;
}

/* ---- holiday / weekend (DS spec เอส · a11y-verified) — scoped .tl2 (ไม่กระทบ Defense/Leave/scratch) ---- */
/* weekend = คง fill เดิม (surface-2 40% · #tl-root .tl-hd/cell--we มีแล้ว) + อักษรวัน dual-encode */
/* holiday = fill primary 4% (จางเท่า weekend · เอาเส้นออก) + จุดกลม 6px primary ต่อท้ายอักษรวัน + เลขวัน alert-text */
#tl-root .tl2 .tl-hd--hol, #tl-root .tl2 .tl-cell--hol {
  background:color-mix(in srgb, var(--primary-solid) 4%, var(--surface)); border-left:0; border-right:0;
}
#tl-root .tl2 .tl-hd--hol em { color:var(--alert-text); }   /* เลขวันแดง (dual-encode เสริม) */
#tl-root .tl2 .tl-hd--hol .tl-hd-dow::after {                /* จุดกลม 6px primary ต่อท้ายอักษรวัน (shape cue · SC 1.4.1) */
  content:''; display:inline-block; width:6px; height:6px; margin-left:3px;
  border-radius:999px; background:var(--primary-solid); vertical-align:middle;
}

/* ---- a11y (เอ รุมเทส) — focus + scroll region ---- */
/* SC 2.4.11 focus-not-obscured: sticky header ไม่บัง element ที่ scroll มา focus · SC 2.4.7 focus ring แดง */
#tl-root .tl2-left, #tl-root .tl2-right { scroll-padding-top:var(--tl2-head-h); }
#tl-root .tl2-left:focus-visible, #tl-root .tl2-right:focus-visible,
#tl-root .tl2 .tl-bar:focus-visible, #tl-root .tl2 .tl2-lrow:focus-visible {
  outline:2px solid var(--primary-solid); outline-offset:2px;
}
/* [minor เอ] slip chip เส้นขอบบางเบา (bg alert-bg จางกลืนพื้น surface) — currentColor จาง ไม่เพิ่มสีใหม่ */
#tl-root .tl2-slip { border:1px solid color-mix(in srgb, currentColor 28%, transparent); }

/* ---- มือถือ (≤640px): name pane แคบ → ซ่อน status label เหลือ dot (range + slip ยังเห็นครบ · parity ไม่กระทบ) ---- */
@media (max-width:640px) {
  #tl-root .tl2 { --tl2-name-w:224px; }
  /* [เอ blocker · SC 1.4.1] sr-only (ไม่ display:none) → ตายังเห็นแค่ dot สี · SR ยังได้ยินชื่อสถานะ (ไม่หลุด a11y tree) */
  #tl-root .tl2-clbl { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
  #tl-root .tl2-chip { padding:2px 5px; }
  #tl-root .tl2-lrow { gap:8px; padding:0 12px; }
  #tl-root .tl2-av { flex-basis:24px; width:24px; height:24px; font-size:11px; }
}

/* S33 (ซี — motion): ลด "drop-flash" ตอน #tl-root swap ทั้งก้อนหลัง drag/resize (เดิม hard-replace
   กระพริบทันที ไม่มี transition) — crossfade สั้น ๆ ผ่าน htmx swap/settle class มาตรฐาน (htmx docs: Request
   Order/animations) คู่กับ timeline.js tlSwapMode() ที่แนบ `swap:100ms settle:80ms` เฉพาะ drag/resize post
   (endpoint อื่นที่ swap #tl-root แบบ default ไม่ได้แนบ delay → ไม่ trigger, ไม่กระทบ) */
#tl-root.htmx-swapping { opacity:0; transition:opacity var(--ease-standard) .1s; }
#tl-root.htmx-added { opacity:0; }
#tl-root.htmx-settling { opacity:1; transition:opacity var(--ease-standard) .08s; }
@media (prefers-reduced-motion:reduce) {
  .tl-cell { transition:none; }
  #tl-root.htmx-swapping, #tl-root.htmx-settling { transition:none; opacity:1; }
}

/* โซน "ยังไม่จัดวัน" (DEC-039) */
.tl-unsched {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; display:flex; flex-direction:column; gap:12px;
  box-shadow:var(--shadow-card);
}
.tl-unsched-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.tl-unsched-head h2 { font-size:16px; font-weight:600; }
.tl-unsched-head > i.fi {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px;
}
.tl-unsched-hint { font-size:11px; color:var(--text-2); }
.tl-unsched-list { display:flex; flex-direction:column; gap:8px; }
.tl-uncard {
  display:flex; align-items:center; gap:8px; padding:8px 12px;
  border:1px solid var(--border); border-radius:12px;
  text-decoration:none; color:var(--text); font-size:var(--fs-sm);
  transition:border-color .15s;
}
.tl-uncard:hover { border-color:var(--primary-light); }
.tl-uncard-t { flex:1; min-width:0; overflow-wrap:anywhere; }

/* list view: แถวเป็น div (ปุ่ม quick-status อยู่นอกลิงก์) */
.tk-rowmain {
  flex:1; min-width:0; display:flex; align-items:center; gap:12px;
  text-decoration:none; color:var(--text);
}
/* S30 audit P1 (เต๋ #1): focus indicator หายทั้ง List view — pattern .risk-row/.seg */
.tk-rowmain:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* mobile (เฟส 2.4) */
@media (max-width:768px) {
  .view-tab span { display:none; }
  .bd-col { width:224px; }
  .tl-name { width:120px; }
  .tk-rowmain { flex-basis:100%; }
  /* ระดับ 3: ตาราง → List-first (ซ่อน table + bulk/column/search, โชว์การ์ด)
     🔴 บรรทัด .tbl-hascards ข้างล่าง = **จุดเดียวในทั้งไฟล์** ที่ซ่อนตารางบนมือถือ
        ตารางจะหายได้ก็ต่อเมื่อ "ประกาศตัวเอง" ว่ามีการ์ดสำรองเท่านั้น — ไม่ประกาศ = เลื่อนแนวนอน
        (ที่มา/เหตุผลการกลับด้าน: ดูหัวข้อ .tbl-scroll ~L1989) */
  .tbl-hascards { display:none; }
  .tbl-mobile { display:flex; }
  .filter-search, .col-btn { display:none; }
  #bulk-bar { display:none !important; }
  /* View Setting (ข้อ 4/5, S33 spec §1.3): ❌ ห้าม copy .col-btn{display:none} — vs-btn ต้องเห็นทุกจอ
     ยุบเหลือ icon-only (padding คง hit-area 44px แนวตั้งจาก .btn-sm เดิม) */
  .vs-btn > span { display:none; }
  .vs-btn { padding:10px; }

  /* ===== S49 (compact mobile toolbar — ซี): 3 แถว [หัวเรื่อง+เพิ่ม] / [view-switcher เลื่อน] / [ตัวกรอง+…+⚙]
     spec: docs/design/mobile-toolbar-compaction-spec-s49.md · ทุกอย่าง additive ในบล็อกนี้ — control เดิม
     ยังอยู่ DOM (ให้ fscombo.js อ่าน data-*-opts ต่อ) แค่ display:none บนมือถือ → desktop ≥769px = 0 diff */
  .page-bar { gap:8px; row-gap:8px; }
  .page-bar-head { order:0; flex:1 1 auto; min-width:0; }
  .add-card-group { order:1; margin-left:auto; flex-shrink:0; }
  .page-bar .view-seg { order:2; flex:1 0 100%; overflow-x:auto; flex-wrap:nowrap;
    -webkit-overflow-scrolling:touch; scrollbar-width:none; justify-content:flex-start; }
  .page-bar .view-seg::-webkit-scrollbar { display:none; }
  .view-tab { flex-shrink:0; }
  .page-bar .filter-bar { order:3; flex:1 0 100%; flex-wrap:nowrap; gap:8px; }

  /* [root-cause fix] List/Space view (Scope≠"space" ปกติ) ใช้โครง .page-head + .view-seg/.filter-bar
     เป็น "direct child ของ .page" ตรง ๆ (ไม่ผ่าน .page-bar wrapper แบบ Board/Table/Timeline/Calendar/Roadmap
     — ดู tasklist.html branch {{else}}) → กฎ .page-bar .view-seg/.filter-bar ด้านบนไม่ครอบคลุม ต้องเติมแยก
     (.page > ... กันชนกับ .page-bar .view-seg ที่เป็นหลาน ไม่ใช่ลูกตรง ของ .page) */
  .page > .view-seg { overflow-x:auto; flex-wrap:nowrap; -webkit-overflow-scrolling:touch;
    scrollbar-width:none; justify-content:flex-start; }
  .page > .view-seg::-webkit-scrollbar { display:none; }
  .page > .filter-bar { flex-wrap:nowrap; gap:8px; }

  /* เพิ่มการ์ด = icon "+" เท่านั้น (ค่าเริ่มต้น; FAB = body.fab-mode override ท้ายไฟล์ §7)
     .add-card-group > .btn-primary ครอบทั้ง 2 branch (page-bar-add ใน $full · plain btn-primary ใน List) —
     ปุ่ม "กรอกวันลา" (isLeaveList, อยู่นอก .add-card-group เสมอ) ตั้งใจไม่ครอบ — คงเป็นปุ่มปกติในแถว ไม่ทำ FAB */
  .page-bar-add > span, .add-card-group > .btn-primary > span { display:none; }
  .page-bar-add, .add-card-group > .btn-primary { padding:0; width:44px; height:44px; min-width:44px; justify-content:center; }

  /* no-JS safety: ซ่อน control เดิมเฉพาะตอน JS รันจริง (html.js — ตั้งใน layout.html ก่อน paint ผ่าน
     document.documentElement.classList.add('js')) — กัน control หายพร้อมกันหมดตอน JS ไม่โหลด/fscombo.js พัง
     (ไม่มี html.js → fallback = ควบคุมเก่าเต็มชุดเหมือนก่อน S49 คับแคบแต่ใช้งานได้ครบ ดีกว่าใช้ไม่ได้เลย) */
  html.js .filter-bar > .filter-btn, html.js .filter-bar > .qf-btn, html.js .filter-bar > .filter-chip,
  html.js .filter-bar > .filter-clear, html.js .filter-bar > .filter-spacer,
  html.js .filter-bar .grp-allbtn, html.js .filter-bar #tl-unit-btn,
  html.js .filter-bar .tl-zoom-seg, html.js .filter-bar .tl-ctrl-sep { display:none !important; }
  /* .grp-btn: เว้น Roadmap (S45 rmMenu — คนละ handler จาก grpMenu/tlGrpMenu, ไม่อยู่ scope S49) กัน
     regression swimlane-group/หน่วยเวลาของ roadmap มือถือหาย (fs-combo ไม่ได้สร้าง section ให้ roadmap) */
  html.js .filter-bar .grp-btn:not([onclick^="rmMenu"]) { display:none !important; }
  /* .filter-search {display:none} มือถือ = rule เดิม (L2700 บล็อกนี้ — ก่อน S49) ไม่ gate เพิ่ม: ค้นหา
     List/Table บนมือถือถูกซ่อนอยู่แล้วก่อน S49 (ไม่มีทางเปิด) → ไม่ใช่ regression ที่ S49 สร้างใหม่ ·
     .is-open (ด้านล่าง) = ทางเข้าใหม่ผ่าน JS (list-search-btn) เท่านั้น ไม่มี no-JS ต้อง restore */

  /* แสดงปุ่มใหม่ (ไม่ต้องรอ html.js — ปุ่มเปล่าไม่พังอะไรถ้า JS ไม่โหลด แค่กดไม่ทำงาน) */
  .fs-combo { display:inline-flex; position:relative; }
  .fs-combo > span { display:inline; }  /* override .btn-ghost span{display:none} มือถือ (L846) */
  .list-search-btn { display:inline-flex; margin-left:auto; }
  .filter-bar .tl-zoom-fit { display:inline-flex; margin-left:auto; }
  .filter-bar .vs-btn { display:inline-flex; }
  /* Table/Board (ไม่มี tl-zoom-fit/list-search-btn) → vs-btn เองดันขวาแทน (Timeline/List มีตัวอื่นดันแล้ว —
     กัน margin-left:auto ซ้อน 2 จุดในแถวเดียวจะเกิดช่องว่างแทรกกลางเกิน) */
  .filter-bar:not(:has(.tl-zoom-fit, .list-search-btn)) .vs-btn { margin-left:auto; }

  /* touch ≥44px (บังคับ — WCAG 2.5.5) */
  .filter-bar .fs-combo, .filter-bar .list-search-btn,
  .filter-bar .tl-zoom-fit, .filter-bar .vs-btn { min-height:44px; }
  .filter-bar .list-search-btn, .filter-bar .tl-zoom-fit,
  .filter-bar .vs-btn { min-width:44px; justify-content:center; padding:10px; }

  .filter-search.is-open { display:block; flex:1 0 100%; max-width:none; margin-top:6px; }

  /* ---- FAB (พีเคาะ = เปิด — §7) — body.fab-mode ตั้งจาก layout.html (ทุกจอ, effect เฉพาะ ≤768) ---- */
  body.fab-mode .add-card-group {
    position:fixed; right:16px; bottom:calc(16px + env(safe-area-inset-bottom));
    z-index:1500; margin-left:0;
  }
  body.fab-mode .page-bar-add, body.fab-mode .add-card-group > .btn-primary {
    width:56px; height:56px; border-radius:999px;
    box-shadow:0 6px 16px rgba(220,60,50,.35);
  }
  body.fab-mode .iconbtn--qa { display:none; } /* quick-add (template) ซ่อนคู่ FAB — เพิ่มการ์ดเปล่าเป็น action หลัก */
  /* [S49 MINOR-4 · ที review] FAB z-index:1500 ชนของอื่นที่ z สูงกว่า/เท่ากัน — ซ่อน FAB ชั่วคราวตอนนั้นแทน
     แก้ z-index (กัน FAB ลอยทับฉากมืด drawer หรือทับ bulk-bar ที่ต้องกดใช้งานจริง) */
  body.fab-mode.drawer-open .add-card-group { display:none; } /* nav drawer เปิด (z-index 1500 เท่ากัน ซ้อนกันพอดี) */
  body.fab-mode:has(#bulk-bar:not([hidden])) .add-card-group { display:none; } /* Table เลือกการ์ด → bulk-bar โผล่ล่างกลาง กัน FAB ทับปุ่ม action */
  /* กัน FAB บังแถวล่างสุด (regression watch #8 — verify จริง: FAB ทับบาร์/การ์ดแถวสุดท้ายตอน scroll ถึงล่าง)
     เผื่อที่ว่างท้าย scroll container เท่าขนาด FAB+margin (56+16+16≈88px) · :has(.add-card-group) กันกระทบ
     หน้าอื่นที่ใช้ .page เหมือนกันแต่ไม่มี FAB (export/import/everything ไม่มีการ์ดให้เพิ่ม)
     [S49 MINOR-1 · ที review] :not(:has(#tl-root)) กัน Timeline ซ้ำซ้อน — Timeline ไม่ใช้ .page scroll
     (fixed viewport height) เคลียร์ FAB ผ่าน .tl2-left/right padding ด้านล่างอยู่แล้ว · ถ้าไม่กัน .page เอง
     ก็ยังโดน padding-bottom 88px ไปด้วย (แม้ไม่ scroll) = dead space ท้ายหน้าซ้อนสอง */
  body.fab-mode .page:has(.add-card-group):not(:has(#tl-root)) { padding-bottom:88px; }
  /* Timeline 2-pane (S48) ไม่ใช้ .page scroll (fixed viewport height, scroll ในตัวเอง) — เติมทั้งคู่ปาน
     ให้เท่ากัน (name pane ซ้าย + bar pane ขวา) กัน scrollHeight ไม่เท่ากัน = แถวเพี้ยนแนวตั้งหลัง scroll สุด */
  body.fab-mode #tl-root .tl2-left, body.fab-mode #tl-root .tl2-right { padding-bottom:88px; }
}

/* ============================================================ ROADMAP (S45 E3) — swimlane view
   namespace .rm-* ล้วน (❌ ห้ามทับ .tl-bar/.tl-grid/.tl-cell — Timeline/Calendar/Defense-Gantt ใช้ร่วม · regression watch §5)
   reuse token DS ทั้งหมด (Coral · light/dark ผ่าน CSS var · Even-Pixel) · สี swimlane = categorical ต่อ scope (--gc)
   geometry แยกจาก timeline zoom (--rm-col-w) → roadmap คุมความกว้าง col เอง ไม่ผูก --tl-cell-w */
.rm-wrap { --rm-axis-w:248px; --rm-col-w:116px; }
.rm-card { background:var(--surface); border:1px solid var(--border); border-radius:16px; overflow:hidden; box-shadow:var(--shadow-tab); }
.rm-scroll { overflow-x:auto; overflow-y:visible; position:relative; } /* S45 dep-viz: anchor SVG connector overlay */
.rm { min-width:max-content; }

/* ทุกแถว = sticky axis (ซ้าย) + lane (ขวา) */
.rm-row { display:flex; align-items:stretch; min-width:max-content; border-bottom:1px solid var(--border); }
.rm-row:last-child { border-bottom:none; }
.rm-axis { position:sticky; left:0; z-index:3; flex:0 0 var(--rm-axis-w); width:var(--rm-axis-w);
  background:var(--surface); border-right:1px solid var(--border); display:flex; align-items:center; gap:8px; padding:0 12px; min-width:0; }
.rm-lane { position:relative; display:grid; grid-template-columns:repeat(var(--cols,6), var(--rm-col-w)); min-height:44px; }

/* header 2-tier (ไตรมาส / เดือน) */
.rm-row--head { position:sticky; top:0; z-index:4; background:var(--surface); }
.rm-axis--head { background:var(--surface); font-size:12px; font-weight:600; color:var(--text-2); z-index:5; }
.rm-lane--head { grid-template-rows:26px 28px; min-height:auto; }
.rm-q { grid-row:1; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:600; color:var(--text);
  border-left:1px solid var(--border); background:var(--surface-2); }
.rm-m { grid-row:2; display:flex; align-items:center; justify-content:center; font-size:12px; color:var(--text-2); border-left:1px solid var(--border); }
/* lost-intent L2 (mockup .rm-cell--now::after "วันนี้"): แก้เป็น flex-column ภายในกรอบเดิม (ไม่ absolute/float
   ออกนอกกรอบแบบ mockup) เพราะ .rm-card มี overflow:hidden + header เป็น sticky top:0 ชิดขอบบนพอดี — float ลอย
   ขึ้นแบบ mockup จะโดน clip. ใส่ที่ header (ครั้งเดียว) แทนที่จะเป็น .rm-cell--now (ซ้ำทุกแถว data — รกกว่าเดิม) */
.rm-m--now { color:var(--primary-solid); font-weight:600; flex-direction:column; line-height:1.15; gap:0; }
.rm-m--now::after { content:"วันนี้"; font-size:8px; font-weight:600; }
.rm-m em { font-style:normal; }

/* gridlines (absolute overlay หลังบาร์) */
.rm-cell { position:absolute; top:0; bottom:0; width:var(--rm-col-w); border-left:1px solid var(--border); pointer-events:none; }
.rm-cell--now { border-left:2px solid var(--primary-solid); }

/* group row (swimlane header) — a11y S45 audit fix A3/A4: onclick ทั้งแถว(role=row เดิม, ไม่มี cell ลูกสมบูรณ์
   → AT ทิ้ง role ทั้งชุด) เปลี่ยนเป็น semantic <div> + disclosure <button> (APG Disclosure) ครอบเฉพาะ .rm-tw
   (ตัว trigger จริง: chevron+dot+name+count) แทน — คลิก/keyboard ยังทำงานเหมือนเดิมทุกอย่าง (native button
   Enter/Space = click อัตโนมัติ ไม่ต้องมี onkeydown เอง) แค่ขอบเขตที่ "คลิกได้จริง" ชัดเจนขึ้นตรงกับที่ AT ประกาศ */
.rm-grp { user-select:none; }
.rm-grp:hover { background:var(--surface-2); }
.rm-grp:hover .rm-axis { background:var(--surface-2); }
.rm-tw { flex:1 1 auto; display:flex; align-items:center; gap:8px; min-width:0; }
/* disclosure trigger button — reset native <button> ให้เหมือน div เดิม 100% (visual ไม่เปลี่ยน) · padding ย้าย
   จาก .rm-axis มาไว้ที่นี่แทน (hit-area ปุ่มครอบคลุมเต็มพื้นที่คลิกเดิม, align-self:stretch = เต็มความสูงแถว ≥44px) */
.rm-grp .rm-axis { padding:0; }
.rm-grptrigger {
  appearance:none; -webkit-appearance:none; background:none; border:none; margin:0;
  font:inherit; color:inherit; text-align:left; cursor:pointer; box-sizing:border-box;
  /* S46: ตัด width:100% (flex:1/min-width:0/display:flex มาจาก .rm-tw) → รองรับ 2-line layout ตอน baseline ON
     (rm-grp--bl axis เป็น column · trigger = บรรทัด 1) · OFF = trigger เต็ม axis เหมือนเดิม (byte-identical) */
  align-self:stretch; padding:0 12px;
}
.rm-grptrigger:focus-visible { outline:2px solid var(--primary-solid); outline-offset:-2px; }
.rm-chev { flex:0 0 auto; color:var(--text-2); transition:transform .18s; display:inline-flex; }
.rm-grp[data-collapsed="false"] .rm-chev { transform:rotate(90deg); }
.rm-dot { flex:0 0 12px; width:12px; height:12px; border-radius:4px; background:var(--gc,var(--lb-gray)); }
.rm-name { font-weight:600; font-size:13.5px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
.rm-count { flex:0 0 auto; font-size:11px; font-weight:600; color:var(--text-2); background:var(--surface-2); border-radius:999px; padding:2px 8px; margin-left:2px; }
.rm-grp:hover .rm-count { background:var(--surface); }

/* summary rollup bar — ยุบ=เด่น 26px / กาง=บาง 14px (Jira parent-bar · decision B) */
.rm-sumwrap { grid-row:1; align-self:center; position:relative; height:26px; display:flex; align-items:center; z-index:1; }
.rm-sum { position:relative; height:26px; width:100%; border-radius:8px; border:1px solid var(--border-control); overflow:hidden;
  background:color-mix(in srgb, var(--gc,var(--lb-gray)) 24%, var(--surface)); box-shadow:var(--shadow-tab); display:flex; align-items:center; }
.rm-sum-fill { position:absolute; left:0; top:0; bottom:0; width:var(--pct,0%); background:var(--gc,var(--lb-gray)); }
/* a11y S45 audit fix A1 (SC 1.4.3): ที่ 100% ตัวเลขตกบน --gc solid fill เต็มพื้น — color:var(--text) คงที่ fail
   ~5/13 สี(light)·11/13(dark) เทียบ 13 --lb-* จริง (คำนวณยืนยัน). แก้ด้วย opaque pill ของตัวเอง (ไม่พึ่งพื้นข้างใต้เลย
   ไม่ว่า --pct เท่าไหร่/สี track ไหน) แทน ink-switch ตาม --gc เพราะที่ pct<100% พื้นเป็น tint 24% (จาง) ไม่ใช่ solid
   เต็ม — ink-map ของ solid fill จะ contrast ผิดทิศ (เช่น ink-light บน tint สว่างจะ fail) reuse คู่ text/surface
   มาตรฐานที่ verify แล้วทั่วระบบ (17.01:1 light/14.65:1 dark) + border-control (3.26:1/3.09:1, 1.4.11 ขอบเขต pill) */
.rm-pct { position:relative; margin:0 4px 0 auto; padding:2px 6px; font-size:11px; font-weight:600;
  color:var(--text); background:var(--surface); border:1px solid var(--border-control); border-radius:999px;
  z-index:1; font-variant-numeric:tabular-nums; }
.rm-grp[data-collapsed="false"] .rm-sumwrap { height:14px; }
.rm-grp[data-collapsed="false"] .rm-sum { height:14px; border-radius:6px; }
.rm-grp[data-collapsed="false"] .rm-pct { display:none; }

/* ── Baseline overlay (S46 · Scenario-Roadmap increment A) ─────────────────────
   ghost bar = DS "Baseline Ghost Bar" (skill §S42): แถบบางเทาจาง + เส้นประ วางซ้อน "หลัง" summary
   (z ต่ำกว่า) ในเลนเดียว → เห็นแผนเดิมเทียบของจริงพร้อมกัน · dual-encode ผ่าน texture (เส้นประ) ไม่ใช่แค่สี
   ส่วนที่ยื่นพ้น summary = ช่วงที่แผนเดิมต่างจากปัจจุบัน (เริ่มก่อน/จบต่าง) · aria-hidden (พูดผ่าน sr-only)
   เอา B3 fix (SC 1.4.11 ≥3:1): border เป็น "identifying element" → 2px dashed var(--border-control)
   (เอ verify: border-control ผ่าน 3:1 ทั้ง 2 theme) · fill จางไม่ต้องผ่าน 3:1 เพราะ border ระบุขอบเขตแล้ว */
.rm-ghost { grid-row:1; align-self:center; height:12px; border-radius:6px; z-index:0; pointer-events:none;
  background:color-mix(in srgb, var(--text-2) 24%, var(--surface)); border:2px dashed var(--border-control); }
.rm-grp[data-collapsed="false"] .rm-ghost { height:10px; }

/* slip badge ต่อ swimlane — chip semantic (warm=ช้า / green=เร็ว / neutral=ตรงแผน) + icon + ข้อความ
   ไม่พึ่งสีอย่างเดียว (SC 1.4.1) · อยู่ใน rm-axis sticky → เห็นเสมอ ไม่ถูก horizontal-scroll ตัด */
.rm-slip { flex:0 0 auto; display:inline-flex; align-items:center; gap:3px;
  font-size:11px; font-weight:600; padding:2px 8px; border-radius:999px; white-space:nowrap; font-variant-numeric:tabular-nums; }
.rm-slip > i.fi { font-size:11px; }
.rm-slip--late   { color:var(--warm-text);  background:var(--warn-bg); }
.rm-slip--early  { color:var(--green-text); background:color-mix(in srgb, var(--green) 14%, transparent); }
.rm-slip--ontime { color:var(--text-2);     background:var(--surface-2); }

/* B4 (เต๋ B1/M2): baseline ON → axis 2 บรรทัด — ชื่อ Scope เต็มบรรทัด 1 · count+slip บรรทัด 2 (indent เลย chevron)
   กัน slip badge บีบชื่อจนอ่านไม่ออก (mobile 150px/desktop 248px) · OFF/ไม่มี slip = 1 บรรทัดเดิมเป๊ะ (ไม่แตะ) */
.rm-grp--bl .rm-axis { flex-direction:column; align-items:stretch; justify-content:center; gap:2px; }
.rm-grp--bl .rm-grptrigger { flex:0 0 auto; }
.rm-grp--bl .rm-name { flex:1 1 auto; }
.rm-axis-l2 { display:flex; align-items:center; gap:6px; padding-left:34px; min-height:20px; }
.rm-axis-l2 .rm-count { margin-left:0; }

/* legend swatch ของ ghost (เส้นประ) — match .rm-ghost (2px dashed) · reuse ขนาด .lg-prog */
.lg-ghost { flex:0 0 auto; width:26px; height:10px; border-radius:4px;
  background:color-mix(in srgb, var(--text-2) 24%, var(--surface)); border:2px dashed var(--border-control); }
.lg-ghost--older { border-style:dotted; } /* multi: เก่าสุด = dotted (dual-encode texture) */

/* toggle avail=false: aria-disabled (คง focusable ตาม เอ B2) → ต้อง style ให้ดู disabled เอง (ไม่มี attr disabled) */
.btn-ghost[aria-disabled="true"] { opacity:.55; cursor:not-allowed; }

/* ── Multi-baseline (S46 · Scenario-Roadmap increment B) — เทียบ 2 version ซ้อนแถว ─────────────
   dual-encode 4 ทาง (ไม่พึ่งสี · SC 1.4.1): (1) ตำแหน่ง เก่าบน→ใหม่ล่าง→Actual (2) tag "v1/v2" บนแท่ง
   (3) texture dotted(เก่า)/dashed(ใหม่)/solid(Actual) (4) sr-only enumerate ต่อ swimlane
   lane = grid หลายแถว (reuse column tracks เดิม) · ghost แถวบน · summary แถวล่าง (grid-row inline) */
.rm-lane--multi { align-content:center; row-gap:5px; padding:8px 0; }
.rm-lane--multi .rm-ghost { align-self:center; position:relative; overflow:visible; height:12px; }
.rm-ghost--older { border-style:dotted; }
.rm-ghost--newer { border-style:dashed; }
/* tag version บนหัวแท่ง ghost = ตัวแยกหลัก (อ่านตรง ๆ · ลอก MS Project baseline label)
   เต๋/เอ enhance#4: bg pill (surface + ring) ให้ข้อความล้น (แท่งสั้น +120px) มีขอบเขต ไม่ลอยทับ grid เปล่า
   + ยก contrast: --text บน --surface (opaque) = ~15:1 ทั้ง 2 theme (ดีกว่า --text บน ghost-fill เทา) */
.rm-ghost-tag { position:absolute; left:4px; top:50%; transform:translateY(-50%); z-index:1; pointer-events:none;
  font-size:10px; font-weight:600; color:var(--text); white-space:nowrap; line-height:1; letter-spacing:.01em;
  background:var(--surface); padding:1px 5px; border-radius:5px; box-shadow:0 0 0 1px var(--border);
  max-width:calc(100% + 116px); overflow:hidden; text-overflow:ellipsis; }
/* 2 slip ป้ายซ้อนแนวตั้ง (multi) — เทียบ Actual ต่อ version · prefix "vs {ชื่อแผน}" อ่านตรง (label polish)
   blocker#1 (เอ/เต๋ SC 1.4.3): ตัด opacity:.8 (ทำ prefix light theme ตก 3.38–3.63:1) → color:inherit สีเต็ม
   = เท่า .rm-slip-t (ผ่าน 4.85–5.41) · hierarchy เบา ๆ ผ่าน font-weight (500 < value 600) แทน opacity */
.rm-slips { flex-direction:column; align-items:flex-start; gap:4px; }
/* label polish (mockup scenario-roadmap-b-mockup-s46.html §B — พีอนุมัติ): โชว์ "vs {Label}" แทน "v1"/"v2" ดิบ
   Label = ชื่อ baseline จริง (free text ไม่จำกัดยาว) → ต้อง ellipsis กันล้นแกนแคบ (desktop 248px/mobile 150px)
   ตัวเลข slip (.rm-slip-t "+26 วัน") ไม่ตัด — คือข้อมูล defend สำคัญสุด ต้องเห็นเต็มเสมอ · full text เต็ม =
   title tooltip + sr-only (เดิม) + legend ล่างสุด (ไม่แตะ) — ตัดเฉพาะ prefix ป้ายนี้ */
.rm-slip-pre {
  color:inherit; font-weight:500; margin-right:1px;
  display:inline-block; max-width:108px; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; vertical-align:bottom;
}

/* version selector rows (bottom-sheet mobile / popover desktop ผ่าน ovOpen) — custom checkbox DS §4 (❌ ห้าม native)
   Actual = locked reference (บนสุด · ปิดไม่ได้) · cap 2 → is-dis (aria-disabled คง focusable) + hint */
.rm-bl-sheet { min-width:280px; }
.rm-bl-row { display:flex; gap:10px; align-items:flex-start; width:100%; text-align:left; box-sizing:border-box;
  padding:10px 14px; background:none; border:none; border-bottom:1px solid var(--border); font:inherit; color:var(--text); cursor:pointer; }
.rm-bl-row:last-child { border-bottom:none; }
.rm-bl-row.is-lock { background:var(--surface-2); cursor:default; }
.rm-bl-row.is-dis  { opacity:.5; cursor:not-allowed; }
.rm-bl-row:not(.is-lock):not(.is-dis):hover { background:var(--surface-2); }
.rm-bl-row:focus-visible { outline:2px solid var(--primary-solid); outline-offset:-2px; }
.rm-bl-cbx { flex:0 0 20px; width:20px; height:20px; border-radius:6px; border:1px solid var(--border-control);
  display:flex; align-items:center; justify-content:center; margin-top:1px; }
.rm-bl-row.is-on .rm-bl-cbx { background:var(--primary-aa); border-color:var(--primary-aa); }
.rm-bl-row.is-on .rm-bl-cbx i.fi { color:#fff; font-size:12px; }
/* Actual = checked+locked: คง coral fill ให้เห็นชัดว่าเปิดอยู่ (locked cue = row bg เทา + "ปิดไม่ได้" ในข้อความ) */
.rm-bl-t { min-width:0; flex:1 1 auto; }
.rm-bl-t b { font-weight:600; display:block; }
.rm-bl-t small { display:block; color:var(--text-2); font-size:11.5px; overflow-wrap:anywhere; }
.rm-bl-meta { flex:0 0 auto; margin-left:auto; text-align:right; color:var(--text-2); font-size:11px; white-space:nowrap; }

/* milestone เพชร (Release target) */
.rm-ms { position:absolute; top:50%; transform:translate(-50%,-50%); z-index:5; display:flex; align-items:center; gap:5px; }
/* a11y S45 audit fix A2 (SC 1.4.11 non-text ≥3:1): เพชรซ้อนบน --gc summary-fill (13 สี track ที่เป็นไปได้)
   — border เดิม var(--surface) เดี่ยวไม่พอ (คำนวณจริง: fail กับ amber/yellow/pink ที่สว่างใกล้ white มาก).
   แก้ด้วย "double-ring halo" คงที่ไม่สลับ theme (ink-light วงใน + ink-dark วงนอก, คนละสูตรกับ ink-switch ของ
   .rm-bar เพราะสีเพชร(text/green-text/alert-text)ต้องคงความหมายสถานะ ไม่ใช่สลับตามพื้น) — verify: อย่างน้อย
   1 ใน 2 ring ผ่าน ≥3:1 ทุกกรณีจาก 13 lb + 3 solid-darken variant (worst case 4.14:1 @ emerald) เพราะพื้นหลัง
   สว่างจะชนกับ ring เข้ม(ink-dark)ชัด / พื้นหลังเข้มจะชนกับ ring อ่อน(ink-light)ชัด เสมอ */
.rm-dia { width:14px; height:14px; transform:rotate(45deg); border-radius:3px; flex:0 0 14px;
  display:flex; align-items:center; justify-content:center; background:var(--text);
  border:1px solid var(--ink-light,#fff); box-shadow:0 0 0 1px var(--ink-dark,#1C1C1E); }
.rm-dia--done { background:var(--green-text); }
.rm-dia--over { background:var(--alert-text); }
.rm-dia > i.fi { transform:rotate(-45deg); font-size:8px; color:var(--surface); line-height:1; }
.rm-ms-tag { font-size:10px; font-weight:600; color:var(--text-2); background:var(--surface); border:1px solid var(--border);
  border-radius:999px; padding:1px 7px; white-space:nowrap; box-shadow:var(--shadow-tab); }

/* task rows (แสดงเมื่อกาง — adjacent sibling) */
.rm-tasks { display:none; }
.rm-grp[data-collapsed="false"] + .rm-tasks { display:block; }
.rm-trow { display:flex; align-items:stretch; min-width:max-content; border-bottom:1px solid var(--border); }
.rm-trow:last-child { border-bottom:none; }
.rm-trow .rm-axis { padding-left:44px; }
.rm-tname { font-size:13px; color:var(--text); display:flex; align-items:center; gap:8px; min-width:0; }
.rm-tname-t { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0; }
.rm-tname .av { flex:0 0 20px; width:20px; height:20px; border-radius:50%; background:var(--primary,var(--primary-solid)); color:#fff;
  font-size:10px; font-weight:600; display:flex; align-items:center; justify-content:center; }
.rm-lane--task { min-height:40px; }

/* task bar (DS v2 solid status fill) */
.rm-bar { grid-row:1; align-self:center; height:24px; display:flex; align-items:center; padding:0 8px; border-radius:8px;
  font-size:11px; font-weight:600; overflow:hidden; text-decoration:none; box-shadow:var(--shadow-tab);
  background:var(--bc,var(--lb-gray)); color:var(--ink-dark,#1C1C1E); border:1px solid var(--border-control); white-space:nowrap; z-index:1; }
.rm-bar > span { overflow:hidden; text-overflow:ellipsis; }
.rm-bar[style*="--lb-blue"], .rm-bar[style*="--lb-indigo"], .rm-bar[style*="--lb-red"],
.rm-bar[style*="--lb-brown"], .rm-bar[style*="--lb-emerald"], .rm-bar[style*="--lb-violet"] { color:var(--ink-light,#fff); }

/* "ยังไม่จัดวัน" bucket (DEC-102) */
.rm-nosched { border-top:1px solid var(--border); background:var(--surface); }
.rm-nosched-h { display:flex; align-items:center; gap:8px; padding:12px 16px; flex-wrap:wrap; }
.rm-nosched-h > i.fi { color:var(--text-2); }
.rm-nosched-h h2 { font-size:14px; font-weight:600; margin:0; }
.rm-nosched-h .hint { font-size:11px; color:var(--text-2); font-weight:400; }
.rm-chips { display:flex; flex-wrap:wrap; gap:8px; padding:0 16px 14px; }
.rm-uncard { display:flex; align-items:center; gap:8px; background:var(--surface-2); border:1px solid var(--border);
  border-radius:10px; padding:8px 12px; font-size:12.5px; color:var(--text); text-decoration:none; max-width:100%; }
.rm-uncard:hover { border-color:var(--border-control); }
.rm-uncard .sdot { flex:0 0 8px; width:8px; height:8px; border-radius:50%; background:var(--lb-gray); }

/* ============================================================================
   Dependency Visualization (S45 · dependency-viz-design-s45.md) — namespace .dep-* ล้วน
   ห้ามทับ .tl-*/.rm-* เดิม · Layer A badge (server) + Layer B connector (SVG overlay client)
   ============================================================================ */

/* Layer A — Badge (chip เล็กคร่อมขอบบาร์ · sibling ในกริด · pointer-events:none คลิกทะลุ)
   3 ระดับ dual-encode (สี+icon): neutral(blocking) · warm(blocked) · alert(conflict)
   [DEVIATE จาก spec §4: spec ให้ blocked=alert(แดง) — ปรับเป็น warm สงวนแดงไว้ให้ conflict เท่านั้น
    กัน roadmap ที่มี dependency เยอะกลายเป็นแดงทั้งจอ (noise) + แยก 2 ระดับ blocked/conflict ชัด — flag พี] */
.dep-badge {
  align-self:center; justify-self:start; z-index:3; pointer-events:none;
  display:inline-flex; align-items:center; gap:2px;
  min-width:16px; height:16px; padding:0 4px; border-radius:6px;
  font-size:11px; font-weight:700; line-height:1; white-space:nowrap;
  border:1px solid var(--border-control); box-shadow:var(--shadow-tab);
}
.dep-badge > i.fi { font-size:9px; line-height:1; display:inline-flex; align-items:center; }
/* ซ้าย = blocked-by (hourglass) · warm tint (attention "รอ prerequisite") · คร่อมขอบซ้ายบาร์ */
.dep-badge--in {
  justify-self:start; transform:translateX(-42%);
  background:color-mix(in srgb, var(--warm-text) 16%, var(--surface));
  color:var(--warm-text); border-color:color-mix(in srgb, var(--warm-text) 40%, transparent);
}
/* ขวา = blocking (share) · neutral (informational "มีคนรอเรา") · คร่อมขอบขวาบาร์ */
.dep-badge--out {
  justify-self:end; transform:translateX(42%);
  background:var(--surface-2); color:var(--text-2); border-color:var(--border-control);
}
/* conflict (timing ผิด) · alert แดง + icon triangle-warning (dual-encode สี+รูป) — override in */
.dep-badge--conflict {
  background:color-mix(in srgb, var(--alert-text) 16%, var(--surface));
  color:var(--alert-text); border-color:color-mix(in srgb, var(--alert-text) 45%, transparent);
}

/* Layer B — Connector SVG overlay (absolute คลุม content เต็ม · aria-hidden decorative)
   วางเหนือ bar (เห็นเส้นพาดผ่าน) · pointer-events:none (คลิก bar ทะลุได้) */
.dep-overlay { position:absolute; top:0; left:0; pointer-events:none; z-index:2; overflow:visible; }
/* casing/halo (DS §5 Data Date Line technique) — เส้นรอง --surface หนากว่า วางใต้ กันจมสีแท่ง 13 สี */
.dep-line-casing { fill:none; stroke:var(--surface); stroke-width:4; stroke-linecap:round; }
.dep-line { fill:none; stroke:var(--border-control); stroke-width:2; stroke-linecap:round; } /* 1.4.11 3.26:1/3.09:1 ✅ */
.dep-arrow { fill:var(--border-control); }
/* conflict = alert + dashed (ไม่พึ่งสีล้วน — dual-encode รูปแบบเส้น) */
.dep-line--conflict { stroke:var(--alert-text); stroke-dasharray:5 4; } /* 1.4.11 5.65:1/5.91:1 ✅ */
.dep-arrow--conflict { fill:var(--alert-text); }
@media (prefers-reduced-motion: no-preference) {
  .dep-line, .dep-line-casing, .dep-arrow { transition:opacity var(--ease-standard) .15s; }
}

/* empty state */
.rm-empty { padding:48px 20px; text-align:center; color:var(--text-2); }
.rm-empty > i.fi { font-size:34px; color:var(--border-control); display:block; margin-bottom:10px; }
.rm-empty p { margin:0 0 4px; font-size:14px; color:var(--text); }
.rm-empty small { font-size:12px; }

/* lost-intent L1 (legend — mockup roadmap-e3-mockup-s44.html มี, build ตัดหาย): อธิบาย pattern หลัก —
   ไม่ hardcode สีสถานะงาน (13 --lb-* เป็น user-config ต่อ List, ไม่ fix ความหมายเหมือน mockup demo 3 สี) ·
   milestone status (ปกติ/done/over) fix จริงใน buildRoadmapView เลยพูดสีตรง ๆ ได้ · reuse double-ring
   ของ .rm-dia (A2) ให้ swatch demo ตรงกับของจริง (DRY) */
.rm-legend { display:flex; flex-wrap:wrap; gap:16px; align-items:center; margin-top:14px; padding:12px 14px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px; font-size:12px; color:var(--text-2); }
.rm-legend .lg { display:flex; align-items:center; gap:7px; }
.rm-legend .lg > i.fi { color:var(--text-2); font-size:13px; }
.rm-legend .lg-end { margin-left:auto; }
.lg-prog { flex:0 0 auto; width:26px; height:12px; border-radius:4px; border:1px solid var(--border-control);
  overflow:hidden; position:relative; background:color-mix(in srgb, var(--text-2) 24%, var(--surface)); }
.lg-prog i { position:absolute; left:0; top:0; bottom:0; width:60%; background:var(--text-2); }
.lg-dia { flex:0 0 auto; width:12px; height:12px; transform:rotate(45deg); border-radius:2px; background:var(--text);
  border:1px solid var(--ink-light,#fff); box-shadow:0 0 0 1px var(--ink-dark,#1C1C1E); }

@media (max-width:640px) {
  .rm-legend .lg-end { margin-left:0; }
  .rm-wrap { --rm-axis-w:150px; --rm-col-w:96px; }
  .rm-name { font-size:12.5px; }
  /* S46: axis 150px แคบ — ลด indent บรรทัด 2 ให้ count+slip มีที่พอ (ยอมไม่ align ใต้ชื่อเป๊ะ) */
  .rm-axis-l2 { padding-left:18px; gap:4px; }
  /* label polish: มือถือแกนแคบกว่าอีก (150px) — ย่อ max-width ป้าย "vs {Label}" ลง กันล้น 2 badge/แถว
     (เต๋ M2 บทเรียน) · ตัวเลข slip เต็มเสมอ (ไม่แตะ) · full text ยังอยู่ที่ title/sr-only/legend */
  .rm-slip-pre { max-width:56px; }
}

/* ============ กระดาษทด (Scratchpad) — S23 ============ */
/* C1: ปุ่มสลับ Table ⇄ Gantt (even-pixel 4/8/16 · touch height 40px) */
.view-toggle { display:inline-flex; gap:4px; background:var(--surface-2); border-radius:12px; padding:4px; }
.vt-btn {
  display:inline-flex; align-items:center; gap:6px; padding:8px 16px; border-radius:8px;
  color:var(--text-2); font-size:var(--fs-sm); text-decoration:none; white-space:nowrap;
}
.vt-btn--on { background:var(--surface); color:var(--text); box-shadow:0 1px 2px rgba(0,0,0,.08); }
/* C2: wrapper ตาราง scratch — กล่อง/เส้นขอบ/overflow ใช้ .tbl-scroll ร่วมกับทุกตารางแล้ว
   (S56: เดิม copy สไตล์มาทั้งชุดเพื่อหนี @media ซ่อนมือถือของ .tbl-scroll — พอกลับด้านแล้วไม่ต้องหนีอีก)
   เหลือเฉพาะ fix ที่เป็นของ scratch จริง ๆ: บังคับความกว้าง + ปลด sticky คอลัมน์ชื่อ */
.scratch-tbl-wrap {
  width:100%; max-width:100%; /* fix (พีบ่น): บาง engine (Safari/WebKit) ปล่อยให้ div ที่ลูกเป็น <table>
    ขยายตาม min-content ของตารางแทนที่จะ bound ตาม parent เมื่อ width เป็น auto ล้วน — ผลคือ wrapper
    กว้างเกิน .page ตัดขวา (ปุ่มลบ/โน้ตหลุด) และ overflow-x:auto ทำงานไม่ได้เพราะ wrapper เองไม่ล้น (เนื้อในไม่ scroll
    เพราะ container โตตามไปด้วย) → ล็อก width/max-width:100% ให้ wrapper เท่า .page เสมอ บังคับให้ .tbl (ลูก) เป็นฝ่าย
    ล้นแทน แล้ว scroll แนวนอนของ wrapper ทำงานจริงทุก engine (Chrome ปกติอยู่แล้วเพราะ block กิน available width
    เป็น default — เพิ่มไว้กัน regression + คุม cross-browser ชัดเจน ไม่พึ่ง default behavior) */
}
.scratch-tbl-wrap .tbl { min-width:720px; } /* กัน 6 คอลัมน์บีบจนพัง → scroll แทน */
/* fix (พีบ่น "หัวเบี้ยว" ตอน scroll): scratch_row ยืม .tbl-td--title (min-width/box-shadow) จาก main
   Task Table (#tbl-wrap) แต่ไม่ได้ยืม position:sticky ตั้งใจ — ปัญหาคือ .tbl-th--title/.tbl-td--title
   (app.css ~L2001) ตั้ง sticky ให้ "ทั้งคู่" เป็น global rule (ไม่ scope #tbl-wrap) ส่วน scratch header
   <th> ไม่มี class .tbl-th--title คู่กัน (ใช้ .tbl-th เฉย ๆ) → body sticky ค้างซ้าย แต่ header เลื่อนตามปกติ
   ตอน scroll เกินระยะหนึ่งเลยเห็นคอลัมน์ชื่อ header/body เหลื่อมกัน (เบี้ยว) — ปลด sticky เฉพาะ scope scratch
   (ไม่แตะ #tbl-wrap เดิม) คง min-width/box-shadow ที่เหลือไว้ (ไม่ใช่ sticky feature ที่ scratch ขอ) */
.scratch-tbl-wrap .tbl-td--title { position:static; left:auto; z-index:auto; }
.scratch-add { margin-top:12px; }
/* C5: label optional "— ไม่ระบุ —" ที่ต้องอ่านจริง (ไม่ decorative) → contrast AA (--text-2 เต็ม ไม่ลด opacity) */
.scratch-tbl-wrap .dd-label .tk-muted, .cell-input::placeholder { opacity:1; color:var(--text-2); }
/* C6: คอลัมน์ปุ่มลบ — แคบ กึ่งกลาง (เดิม fallback ~112px กินที่) */
.tbl-th--act, .tbl-td--act { width:48px; min-width:48px; max-width:48px; text-align:center; }

/* ---- activity timeline (เฟส 2.6 — DEC-048) ---- */
.act-count { font-size:12px; color:var(--text-2); margin-left:auto; }
.act-list { display:flex; flex-direction:column; gap:12px; }
.act-item { display:flex; align-items:flex-start; gap:12px; min-width:0; }
.act-ic {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%; aspect-ratio:1/1;
  background:var(--surface-2); color:var(--text-2);
  display:inline-flex; align-items:center; justify-content:center; font-size:12px;
}
.act-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
.act-line { font-size:var(--fs-sm); overflow-wrap:anywhere; word-break:break-word; }
.act-change {
  display:flex; align-items:center; flex-wrap:wrap; gap:8px;
  font-size:12px; color:var(--text-2); overflow-wrap:anywhere; word-break:break-word;
}
.act-change > i.fi { font-size:10px; flex-shrink:0; } /* S27 Batch2: icon inline ตาม font (DEC-068#3) — ไม่ใช่ข้อความอ่าน ไม่บังคับ Micro floor */
.act-field { font-weight:500; }
.act-new { color:var(--text); font-weight:500; }
.act-time { font-size:11px; color:var(--text-2); }

/* ============================================================ คลื่น 2 — Card Detail split-pane */
/* .page--card = modifier เฉพาะหน้าการ์ด (ไม่แตะ .page กลาง — list/table/daily/home ไม่กระทบ)
   ยกเลิก scroll ระดับ page → แต่ละเพนเลื่อนเอง + composer ปักล่างเสมอ */
.page--card { max-width:none; padding:0; overflow:hidden; display:flex; flex-direction:column; }

.card-topbar {
  flex-shrink:0; display:flex; align-items:flex-start; justify-content:space-between; gap:16px;
  padding:20px 24px; border-bottom:1px solid var(--border);
}
.card-topbar h1 { font-size:22px; font-weight:600; }
.card-head-actions { display:flex; align-items:center; gap:8px; flex-shrink:0; }

/* segmented tab (โชว์เฉพาะ <1024) */
.card-tabs { display:none; gap:4px; background:var(--surface-2); border-radius:12px; padding:4px; }
.seg-btn {
  display:inline-flex; align-items:center; justify-content:center; gap:6px; cursor:pointer;
  min-height:44px; /* touch target (DS §6.3 / WCAG 2.5.5) — tabs โชว์เฉพาะ <1024 = ทัชสกรีน */
  font-size:var(--fs-sm); font-weight:500; color:var(--text-2);
  border:none; background:none; border-radius:8px; padding:8px 14px;
  transition:color .15s, background .15s;
}
.seg-btn:hover { color:var(--text); }
.seg-btn.is-active { background:var(--surface); color:var(--primary-aa); box-shadow:var(--shadow-tab); } /* S27-a11y (เอ): 4.43→5.09:1 AA */
[data-theme="dark"] .seg-btn.is-active { color:var(--primary-light); }
.seg-count { font-size:11px; font-weight:600; color:var(--primary-solid); }

/* 2 เพน: ซ้ายเลื่อนเอง · divider · ขวา feed+composer */
.card-split { flex:1; min-height:0; display:flex; --activity-w:340px; }
.card-main {
  flex:1; min-width:0; overflow-y:auto;
  padding:24px; display:flex; flex-direction:column; gap:16px;
}
.card-divider {
  flex:0 0 6px; cursor:col-resize;
  background:var(--primary-solid); opacity:0; transition:opacity .15s;
}
.card-divider:hover, .card-divider.is-dragging { opacity:.25; }
.card-activity {
  width:var(--activity-w,340px); flex-shrink:0; min-height:0;
  border-left:1px solid var(--border); background:var(--surface);
  display:flex; flex-direction:column;
}
.card-activity-head {
  flex-shrink:0; display:flex; align-items:center; gap:8px;
  padding:16px 20px; border-bottom:1px solid var(--border);
  font-size:14px; font-weight:600;
}
.card-activity-head > i.fi { color:var(--text-2); }
.card-activity-head .act-count { margin-left:auto; }

/* feed = unified list (activity + comment) เรียงเก่า→ใหม่ */
.feed { flex:1; overflow-y:auto; padding:16px 20px; display:flex; flex-direction:column; gap:16px; }

/* comment item (avatar + bubble — สัญญาณ "คนพูด") */
.cmt-item { display:flex; align-items:flex-start; gap:12px; min-width:0; }
.cmt-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
.cmt-head { display:flex; align-items:baseline; gap:8px; }
.cmt-head b { font-size:var(--fs-sm); font-weight:600; min-width:0; overflow-wrap:anywhere; word-break:break-word; }
.cmt-time { font-size:11px; color:var(--text-2); }
.cmt-bubble {
  background:var(--surface-2); border-radius:12px; padding:8px 12px;
  font-size:14px; color:var(--text); white-space:pre-wrap; overflow-wrap:anywhere; word-break:break-word;
}

/* composer ปักล่างเพนขวา */
.cmt-composer { flex-shrink:0; border-top:1px solid var(--border); padding:12px 16px; background:var(--surface); }
.cmt-composer-row { display:flex; align-items:flex-end; gap:8px; }
/* override .ta (min-height:72px) → 1 บรรทัด แล้วค่อยโตด้วย cmtGrow (สเปก ง, rows=1) */
.cmt-input { flex:1; min-width:0; resize:none; line-height:1.4; min-height:36px; padding:8px 12px; }
/* touch target 44px (DS §6.3 / WCAG 2.5.5) */
.cmt-send { min-width:44px; min-height:44px; padding:8px 12px; flex-shrink:0;
  display:inline-flex; align-items:center; justify-content:center; }
.cmt-send:disabled { opacity:.4; cursor:default; }
/* error จาก server (retarget เข้ามา) — ว่าง = ซ่อนเอง */
.cmt-error { font-size:12px; color:var(--alert-text); padding:6px 4px 0; overflow-wrap:anywhere; word-break:break-word; } /* S27 P0: alert-text (AA) */
.cmt-error:empty { display:none; }

/* property row (reusable) + tags → row (m2) */
.prop-row { display:flex; align-items:center; gap:8px; padding:8px 0; min-width:0; }
.prop-row > i.fi {
  width:16px; aspect-ratio:1/1; flex-shrink:0; font-size:14px; color:var(--text-2);
  display:flex; align-items:center; justify-content:center;
}
.prop-label { font-size:12px; font-weight:600; color:var(--text-2); flex-shrink:0; min-width:72px; }
.prop-value {
  flex:1; min-width:0; display:flex; align-items:center; flex-wrap:wrap; gap:6px;
  font-size:var(--fs-sm); color:var(--text); overflow-wrap:anywhere;
}
.prop-empty { color:var(--text-2); opacity:.7; }
.prop-empty--err { color:var(--alert-text); opacity:1; } /* S27 P0: alert-text (AA) */
/* input เพิ่ม tag แบบ inline (ไม่มี panel-head ใหญ่) — คง .tag-add + .tag-suggest ให้ tagSuggest() ทำงาน */
.tag-add--inline { position:relative; flex-direction:row; align-items:center; gap:0; width:auto; }
.tag-add-input {
  border:1px dashed var(--border); background:transparent; color:var(--text);
  border-radius:8px; padding:4px 10px; font:inherit; font-size:var(--fs-sm); width:120px; outline:none;
}
.tag-add-input:focus { border-style:solid; border-color:var(--primary-solid); }

/* ตอนลาก divider (มี .no-select) ตัด transition = ไม่หน่วง (reuse pattern คลื่น 1) */
body.no-select .card-divider,
body.no-select .card-activity,
body.no-select .card-main { transition:none; }

/* <1024: ซ่อน divider + tab สลับทีละเพน (web responsive จริง — DS §6.3 ยกเว้นได้) */
@media (max-width:1023px) {
  .card-tabs { display:inline-flex; align-self:flex-start; margin:12px 16px 0; }
  .card-divider { display:none; }
  .card-main, .card-activity { width:auto; flex:1; border-left:none; }
  .card-split[data-tab="details"]  .card-activity { display:none; }
  .card-split[data-tab="activity"] .card-main     { display:none; }
}

/* ============================================================ Bulk Action (เฟส 2.7 — DEC-052) */
/* checkbox column (Table multi-select) */
.tbl-th--cb, .tbl-td--cb { width:44px; padding-left:12px; padding-right:0; text-align:center; }
.cbx--cell { display:inline-flex; justify-content:center; gap:0; }

/* bulk bar — ลอยล่างจอกลาง (z ต่ำกว่า sheet 4000 เพื่อให้ sheet ทับได้) */
.bulk-bar {
  position:fixed; left:50%; bottom:24px; transform:translateX(-50%);
  z-index:1500; max-width:calc(100vw - 32px);
  display:flex; align-items:center; gap:16px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:12px 16px; box-shadow:0 8px 32px rgba(0,0,0,.18);
}
.bulk-bar[hidden] { display:none; } /* CSS display ทับ [hidden] attribute — ต้อง override ให้ bar ซ่อนตอนเลือก 0 ใบ */
.bulk-count { display:inline-flex; align-items:center; gap:8px; font-size:14px; font-weight:500; white-space:nowrap; }
.bulk-count > i.fi { color:var(--primary-solid); }
.bulk-count b { color:var(--primary-solid); }
.bulk-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
/* ทุก control ในแถบสูง/ฟอนต์/padding เท่ากัน (ghost trigger + danger + clear) — เลิกสะเปะสะปะ */
.bulk-actions .btn-sm,
.bulk-actions .btn-danger {
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  min-height:44px; padding:8px 14px; font-size:var(--fs-sm); font-weight:500; /* touch target ≥44 (WCAG 2.5.5) */
  white-space:nowrap; border-radius:12px;
}
/* ปุ่มลบ: สูง/ฟอนต์เท่าเพื่อน (weight 500 แทน 600) แต่คงสี danger ไว้ */
.bulk-actions .btn-danger { font-weight:500; }
.bulk-x { min-width:44px; padding:8px; } /* icon-only = จตุรัส สูงเท่าเพื่อน */
/* เส้นคั่นกลุ่ม set-value action กับกลุ่มทำลาย/ล้าง */
.bulk-div { width:1px; height:24px; background:var(--border); flex:0 0 auto; margin:0 2px; align-self:center; }

/* input sheet (estimate / tag) */
.bulk-inp { display:flex; flex-direction:column; gap:16px; padding:8px 4px 4px; }
.bulk-inp .cell-input { width:100%; padding:12px 14px; font-size:14px; }
.bulk-inp .btn-primary { justify-content:center; }

/* confirm sheet (bulk delete) */
.bulk-confirm { display:flex; flex-direction:column; gap:16px; padding:8px 4px 4px; }
.bulk-confirm-t { margin:0; font-size:14px; color:var(--text-2); overflow-wrap:anywhere; }
.bulk-confirm .btn-danger { justify-content:center; }
/* [M4] แถวปุ่มยืนยัน/ยกเลิก — ghost ซ้าย primary ขวา (mirror modal-footer) */
.bulk-confirm-actions { display:flex; gap:8px; justify-content:flex-end; }
.bulk-confirm-actions .btn-ghost, .bulk-confirm-actions .btn-primary { justify-content:center; }

/* report success (partial/skip ใช้ .alert-box เดิม) */
.bulk-ok {
  display:inline-flex; align-items:center; gap:8px;
  background:color-mix(in srgb, var(--green) 12%, transparent); color:var(--green-text); /* S27 P0: green-text (AA) */
  border-radius:var(--r); padding:10px 16px; font-size:14px; font-weight:500;
}

/* ===== Daily table (feature) ===== */
/* ตารางแบน reuse .tbl* global — เพิ่มเฉพาะ daily-scoped (ไม่แตะ master Table view) */
.dy-fitem { display:inline-flex; align-items:center; gap:8px; }
#dy-total.dy-total { font-size:var(--fs-sm); color:var(--text-2); display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
#dy-total.dy-total b { color:var(--text); font-weight:600; }
.tbl--daily { min-width:720px; }                                  /* +Space column */
.tbl-td--dtask, .tbl-th--dtask { min-width:240px; max-width:360px; white-space:normal; }
.tbl-td--dspace, .tbl-th--dspace { min-width:120px; max-width:200px; }
.dy-space { display:inline-flex; align-items:center; gap:6px; color:var(--text-2); font-size:var(--fs-sm); overflow-wrap:anywhere; word-break:break-word; }
/* SDLC-T3 column (REQ-S12 ข้อ 1) */
.tbl-td--dsdlc, .tbl-th--dsdlc { min-width:120px; max-width:200px; }
.dy-sdlc { color:var(--text-2); font-size:var(--fs-sm); overflow-wrap:anywhere; word-break:break-word; }
/* ปุ่มเปิด master card จาก daily (REQ-S12 ข้อ 8) */
.dy-open { display:inline-flex; align-items:center; justify-content:center; width:24px; height:24px; flex-shrink:0;
  border-radius:8px; color:var(--text-2); text-decoration:none; }
.dy-open:hover { background:var(--surface-2); color:var(--primary-solid); }
.tbl-td--dnote { min-width:160px; }
.tbl-td--dmd { min-width:96px; white-space:nowrap; }
.tbl-td--dmd .cell-input { width:auto; min-width:56px; max-width:80px; } /* daily-scoped: ไม่กระทบ master .cell-input */
.dy-unit { color:var(--text-2); font-size:11px; margin-left:2px; }
.tbl-th--dact, .tbl-td--dact { min-width:auto; width:72px; }
.tbl-mcard-note { font-size:12px; color:var(--text-2); overflow-wrap:anywhere; word-break:break-word; }

/* Duplicate modal — grid ของ custom checkbox */
.dup-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px 16px; margin-top:16px; }
.dup-grid .cbx { padding:8px; border-radius:10px; }
.dup-grid .cbx:hover { background:var(--surface-2); }
@media (max-width:560px){ .dup-grid { grid-template-columns:1fr; } }

/* Daily list ใต้ master card (card_daily_panel) */
.cd-row { display:flex; align-items:flex-start; gap:16px; padding:10px 0; border-top:1px solid var(--border); font-size:14px; }
.cd-row:first-of-type { border-top:none; }
.cd-date { display:inline-flex; align-items:center; gap:6px; white-space:nowrap; color:var(--text-2); min-width:120px; }
.cd-member { display:inline-flex; align-items:center; gap:6px; white-space:nowrap; min-width:120px; }
.cd-md { white-space:nowrap; font-weight:600; min-width:64px; }
.cd-note { flex:1; min-width:0; color:var(--text); overflow-wrap:anywhere; word-break:break-word; }
/* แถว daily ที่แก้ได้ (owner/admin) — คลิกทั้งแถวเปิด popup แก้ (custom control) */
/* S27 P0 (audit B7): เดิม focus-visible พึ่ง background-only (--surface→--surface-2 = 1.14:1 fail 1.4.11)
   — เปลี่ยนเป็น pattern เดียวกับ .risk-row (border-color:var(--primary-solid) = 4.43:1/3.76:1 ผ่าน AA)
   border:1px solid transparent ที่ rest กัน CLS (ไม่ขยับ layout ตอน focus เพิ่ม border) */
.cd-row--edit { cursor:pointer; margin:0 -8px; padding-left:8px; padding-right:8px; border-radius:8px; border:1px solid transparent; transition:background .12s, border-color .12s; }
.cd-row--edit:hover { background:var(--surface-2); }
.cd-row--edit:focus-visible { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.cd-edit-ic { display:inline-flex; align-items:center; color:var(--text-3); opacity:0; transition:opacity .12s; }
.cd-row--edit:hover .cd-edit-ic, .cd-row--edit:focus-visible .cd-edit-ic { opacity:1; }
/* S34 ปุ่มลบ daily ใน panel — reveal-on-hover เหมือน pencil (สะอาด) · โผล่ตอน hover row หรือ focus ปุ่มเอง */
.cd-del-ic { flex:none; opacity:0; transition:opacity .12s; }
.cd-row--edit:hover .cd-del-ic, .cd-row--edit:focus-within .cd-del-ic, .cd-del-ic:focus-visible { opacity:1; }
@media (max-width:640px){ .cd-row { flex-wrap:wrap; gap:6px 12px; } .cd-edit-ic, .cd-del-ic { opacity:1; } }

/* ===== Daily modal — master-card preview (dt-ctx) [S24 polish] ===== */
/* mini-card อ่านอย่างเดียวในฟอร์ม ลง/แก้ Daily — เดิม container ไม่มี CSS เลย (render unstyled)  */
/* เติมตาม DS: nested surface + type scale เล็กลง (title 13 / meta 12 / micro 11) + light+dark    */
.dt-ctx { display:flex; flex-direction:column; gap:8px;
  background:var(--surface-2); border:1px solid var(--border); border-radius:12px; padding:12px 16px; }
.dt-ctx-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; min-width:0; }
.dt-ctx-title { flex:1; min-width:0; font-size:var(--fs-sm); font-weight:600; color:var(--text);
  overflow-wrap:anywhere; word-break:break-word; }
.dt-ctx-grid { display:flex; flex-wrap:wrap; align-items:center; gap:8px 16px; }
.dt-ctx-item { display:inline-flex; align-items:center; gap:6px; min-width:0;
  font-size:12px; color:var(--text-2); overflow-wrap:anywhere; }
.dt-ctx-item i.fi { font-size:12px; color:var(--text-2); }
.dt-ctx-grid .avatar--sm { width:24px; height:24px; font-size:11px; }
.dt-ctx-sdlc { display:flex; align-items:center; gap:6px; min-width:0;
  font-size:12px; color:var(--text-2); overflow-wrap:anywhere; word-break:break-word; }
.dt-ctx-sdlc i.fi { font-size:12px; flex-shrink:0; }
.dt-ctx-ro { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--text-2);
  padding-top:8px; border-top:1px solid var(--border); }
.dt-ctx-ro i.fi { font-size:11px; }
/* state: ยังไม่เลือกการ์ด (single row info) */
.dt-ctx--empty { flex-direction:row; align-items:center; gap:8px; font-size:var(--fs-sm); color:var(--text-2); }
.dt-ctx--empty i.fi { font-size:14px; color:var(--text-2); flex-shrink:0; }

/* ===== Member/Team Mgmt (feature) ===== */
/* เฟส 2.10 — Member Management + Manage Team + Profile (member.html/profile.html) */

.mb-wrap { display:flex; flex-direction:column; gap:32px; }
.mb-section { display:flex; flex-direction:column; gap:16px; }
.mb-section-head { display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; }
.mb-sh-title { display:flex; align-items:center; gap:8px; }
.mb-sh-title h2 { margin:0; font-size:18px; font-weight:600; }
.mb-sh-title > i.fi { width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px; }
.mb-sh-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }

/* user-chip เป็นลิงก์ (topbar → profile) */
.user-chip--link { text-decoration:none; color:inherit; padding:4px 8px; border-radius:12px; transition:background .15s; }
.user-chip--link:hover { background:var(--surface-2); }

/* filter bar */
.mb-filter { display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:12px 16px; }
.mb-filter-chips { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
.mb-filter-label { font-size:12px; font-weight:500; color:var(--text-2); }
.mb-search { margin-left:auto; min-width:200px; }
.mb-search input { width:100%; padding:8px 12px 8px 36px; border-radius:12px;
  border:1px solid var(--border); background:var(--surface-2); color:var(--text); font-size:14px; font-family:inherit; }

/* table */
/* overflow-x:auto — จอแคบ scroll แนวนอนได้ ไม่ตัดเนื้อหาทิ้ง (M1 รุมเทส 3) · min-width กันคอลัมน์ยุบ */
.mb-table-wrap { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); overflow-x:auto; }
.mb-table { width:100%; min-width:680px; border-collapse:collapse; font-size:14px; }
.mb-table th, .mb-table td { padding:12px 16px; text-align:left; overflow-wrap:anywhere; }
.mb-table thead th { font-size:12px; font-weight:500; color:var(--text-2); background:var(--surface-2);
  border-bottom:1px solid var(--border); white-space:nowrap; }
.mb-table tbody tr { border-bottom:1px solid var(--border); }
.mb-table tbody tr:last-child { border-bottom:none; }
.mb-th-act { text-align:right; width:1%; white-space:nowrap; }
/* ปิดบัญชี: ❌ ไม่หรี่ทั้งแถวด้วย opacity (text ตก WCAG AA — M2 รุมเทส 3) · สื่อสถานะด้วย badge "ปิดบัญชี" + หรี่เฉพาะ avatar */
.mb-row--off .avatar { opacity:.5; }
.mb-name { display:flex; align-items:center; gap:8px; min-width:0; font-weight:500; }
.mb-email { color:var(--text-2); }
.mb-muted { color:var(--text-2); }
.mb-you { font-size:11px; color:var(--primary-solid); background:color-mix(in srgb, var(--primary-solid) 10%, transparent); border-radius:999px; padding:2px 8px; }
.mb-act { display:flex; align-items:center; justify-content:flex-end; gap:8px; white-space:nowrap; }
.mb-empty { text-align:center; color:var(--text-2); padding:24px; }

/* badges — role + status */
.badge--role { color:var(--text-2); background:var(--surface-2); }
.badge--admin { color:var(--alert-aa); background:color-mix(in srgb, var(--primary-solid) 12%, transparent); } /* S27-a11y (เอ): เหตุผลเดียวกับ .badge--cascade — bg tint ลด contrast, alert-aa ผ่านชัวร์ */
[data-theme="dark"] .badge--admin { color:var(--primary-light); }
.badge--lead  { color:var(--secondary-text); background:color-mix(in srgb, var(--secondary-end) 14%, transparent); } /* S27 P0: secondary-end ตัวดิบ light ผ่าน(4.75) แต่ dark fail(2.56) — secondary-text ผ่านทั้ง 2 mode */
.badge--staff { color:var(--cold-text); background:rgba(100,160,255,.16); } /* S27 P0: --cold ดิบ 2.28:1 fail — cold-text ใหม่ (AA) */
.badge--on  { color:var(--green-text); background:color-mix(in srgb, var(--green) 14%, transparent); }
.badge--off { color:var(--text-2); background:var(--surface-2); }

/* iconbtn ok variant (reactivate) */
.iconbtn--ok:hover { color:var(--green-text); } /* S27 P0: icon-only แต่ยังต่ำกว่า 3:1 ดิบ — green-text (AA) */

/* team grid */
.mb-team-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:16px; }
.mb-team-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:16px;
  display:flex; flex-direction:column; gap:12px; }
/* ยุบทีม: ❌ ไม่หรี่ทั้งการ์ดด้วย opacity (M2) — สื่อด้วย badge "ยุบแล้ว" · หรี่เฉพาะ icon meta */
.mb-team-card--off .mb-team-meta i.fi { opacity:.5; }
.mb-team-top { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; }
.mb-team-name { font-weight:600; font-size:14px; overflow-wrap:anywhere; }
.mb-team-act { display:flex; gap:4px; flex-shrink:0; }
.mb-team-meta { display:flex; flex-direction:column; gap:6px; font-size:var(--fs-sm); color:var(--text-2); }
.mb-team-meta i.fi { color:var(--text-2); margin-right:4px; }
/* multi-lead (S55 · พีสั่ง "ทีมใส่หัวหน้าได้กี่คนก็ได้"):
   - .mb-team-leads/.mb-leadchip = แสดงหัวหน้าหลายคนในการ์ดทีม (chip neutral — ไม่ผูกสีสถานะ)
   - .mb-leadpick = grid ของ custom checkbox (.cbx/.cbx--sheet, DS §4 ❌ ไม่ใช้ native select multiple) */
.mb-team-leads { display:flex; flex-wrap:wrap; align-items:center; gap:4px; }
.mb-leadchip { display:inline-flex; align-items:center; font-size:12px; font-weight:500;
  border-radius:999px; padding:2px 8px; background:var(--surface-2); color:var(--text); overflow-wrap:anywhere; }
.mb-leadpick { display:grid; grid-template-columns:repeat(auto-fill,minmax(200px,1fr)); gap:8px; }

/* #team-form-alert — slot error ของฟอร์มทีม (M1). ว่าง = ซ่อน ไม่งั้นกิน gap:16px ของ .modal-body
   (ช่องโหว่บนสุดโมดัลตอนไม่มี error) · focus-visible ring ตอน JS ย้าย focus มา (N1 keyboard) */
#team-form-alert:empty { display:none; }
#team-form-alert .alert-box:focus-visible { outline:2px solid var(--primary); outline-offset:2px; }

/* form helpers */
.mb-hint { display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-2);
  background:var(--surface-2); border-radius:12px; padding:8px 12px; overflow-wrap:anywhere; }
.mb-hint i.fi { color:var(--cold); flex-shrink:0; }

/* profile page */
.pf-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(300px,1fr)); gap:24px; align-items:start; }
.pf-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:20px;
  display:flex; flex-direction:column; gap:16px; }
.pf-head { display:flex; align-items:center; gap:8px; }
.pf-head h2 { margin:0; font-size:18px; font-weight:600; }
.pf-head > i.fi { width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px; }
.pf-identity { display:flex; align-items:center; gap:12px; }
.avatar--lg { width:48px; height:48px; font-size:20px; }
.pf-name { font-weight:600; font-size:16px; overflow-wrap:anywhere; }
.pf-sub { font-size:var(--fs-sm); color:var(--text-2); overflow-wrap:anywhere; }
.pf-rows { display:flex; flex-direction:column; gap:8px; }
.pf-row { display:flex; align-items:center; justify-content:space-between; gap:12px; font-size:14px; }
.pf-k { color:var(--text-2); }
.pf-result:empty { display:none; }
.pf-result { margin:-4px 0 4px; }

/* ===== Import/Export (feature) ===== */
/* reuse .panel/.field/.dd/.btn-* เดิม — เพิ่มเฉพาะส่วนของ import/export (light+dark ผ่าน var) */
.ie-panel { margin-bottom:24px; }
/* inline validation alert แทน native alert() (M3 รุมเทส 3) · [hidden] คุมด้วย JS */
.ie-alert { margin-bottom:16px; }
.ie-alert[hidden] { display:none; }

/* file input (native — ไม่อยู่ในรายการ control ที่ DS ห้าม) */
.ie-file {
  display:flex; align-items:center; gap:12px; cursor:pointer;
  border:1px dashed var(--border); border-radius:12px; padding:16px;
  background:var(--surface-2); color:var(--text-2); font-size:14px;
  transition:border-color .15s, color .15s;
}
.ie-file:hover { border-color:var(--primary-solid); color:var(--text); }
.ie-file input[type=file] {
  /* 🔴 ห้าม display:none กับ input ที่ required — เบราว์เซอร์จะโฟกัสไม่ได้ แล้ว "กดส่งแล้วเงียบ"
     (Chrome: "An invalid form control ... is not focusable" · ผู้ใช้ไม่รู้ว่าทำอะไรผิด)
     ใช้ซ่อนแบบยังโฟกัสได้แทน — ทับพื้นที่ของ label ไว้ ผู้ใช้กดตรงไหนก็โดน  ·  เจอโดยด่าน uiguard กฎ C */
  position:absolute; width:100%; height:100%; inset:0; opacity:0; cursor:pointer;
}
.ie-file { position:relative; }
.ie-file > i.fi { font-size:20px; color:var(--primary-solid); }
.ie-file-name { overflow-wrap:anywhere; word-break:break-word; min-width:0; flex:1; }

.ie-actions { display:flex; align-items:center; gap:16px; flex-wrap:wrap; margin-top:16px; }
.ie-actions form { display:flex; align-items:center; gap:16px; }

.ie-spin { opacity:0; transition:opacity .15s; color:var(--text-2); font-size:12px;
  display:inline-flex; align-items:center; gap:6px; }
.ie-spin.htmx-request { opacity:1; }
.ie-spin i.fi { animation:ie-spin 1s linear infinite; }
@keyframes ie-spin { to { transform:rotate(360deg); } }

/* fatal file-level error */
.ie-fatal { overflow-wrap:anywhere; word-break:break-word; }

/* summary chips */
.ie-summary { display:flex; flex-wrap:wrap; gap:10px; margin:4px 0 16px; }
.ie-stat {
  display:inline-flex; align-items:center; gap:6px;
  background:var(--surface-2); color:var(--text-2);
  border-radius:999px; padding:6px 14px; font-size:var(--fs-sm); font-weight:500;
}
.ie-stat i.fi { font-size:var(--fs-sm); }
.ie-stat--ok { background:color-mix(in srgb, var(--green) 12%, transparent); color:var(--green-text); } /* S27 P0: green-text (AA) */
.ie-stat--err { background:var(--alert-bg); color:var(--alert-text); } /* S27 P0: alert-text (AA) */
.ie-stat--warn { background:var(--warn-bg); color:var(--warm-text); } /* S27 P0: warm-text (AA) */

/* file notices */
.ie-notices { list-style:none; margin:0 0 16px; padding:12px 16px;
  background:var(--surface-2); border-radius:12px; display:flex; flex-direction:column; gap:8px; }
.ie-notices li { display:flex; align-items:flex-start; gap:8px; font-size:var(--fs-sm); color:var(--text-2);
  overflow-wrap:anywhere; word-break:break-word; }
.ie-notices i.fi { color:var(--cold); margin-top:2px; flex-shrink:0; }

/* report table */
.ie-table-wrap { overflow-x:auto; border:1px solid var(--border); border-radius:12px; }
.ie-table { width:100%; border-collapse:collapse; font-size:var(--fs-sm); }
.ie-table th, .ie-table td { text-align:left; padding:10px 12px; border-bottom:1px solid var(--border);
  vertical-align:top; overflow-wrap:anywhere; word-break:break-word; }
.ie-table thead th { background:var(--surface-2); color:var(--text-2); font-weight:600; font-size:12px;
  position:sticky; top:0; }
.ie-table tbody tr:last-child td { border-bottom:none; }
.ie-row--err { background:var(--alert-bg); }
.ie-line { color:var(--text-2); white-space:nowrap; width:48px; }
.ie-title { font-weight:500; color:var(--text); min-width:120px; }
.ie-reasons { display:flex; flex-direction:column; gap:4px; }
.ie-reason { display:flex; align-items:flex-start; gap:6px; }
.ie-reason i.fi { margin-top:2px; flex-shrink:0; font-size:12px; }
.ie-reason--err { color:var(--alert-text); } /* S27 P0: alert-text (AA) */
.ie-reason--warn { color:var(--warm-text); } /* S27 P0: warm-text (AA) */

.ie-tag { display:inline-flex; align-items:center; border-radius:999px; padding:2px 10px;
  font-size:12px; font-weight:600; white-space:nowrap; }
.ie-tag--ok { background:color-mix(in srgb, var(--green) 14%, transparent); color:var(--green-text); } /* S27 P0: green-text (AA) */
.ie-tag--err { background:var(--alert-bg); color:var(--alert-text); } /* S27 P0: alert-text (AA) */

/* ==== Report Engine (By Staff / By Project — bucket A, DEC-058) ==== */
/* seg-link — tab/toggle เป็นลิงก์ (ใช้สไตล์เดียวกับ .seg custom radio) */
.seg-link {
  flex:1; min-width:0; display:flex; align-items:center; justify-content:center; gap:6px;
  min-height:44px; /* touch target (DS §6.3 / WCAG 2.5.5) — ตรงกับ .seg-btn */
  font-size:var(--fs-sm); font-weight:500; color:var(--text-2); text-decoration:none; text-align:center;
  border-radius:8px; padding:8px 12px; transition:color .15s, background .15s;
}
.seg-link:hover { color:var(--text); }
.seg-link.is-active { background:var(--surface); color:var(--primary-aa); box-shadow:var(--shadow-tab); } /* S27-a11y (เอ): 4.43→5.09:1 AA */
[data-theme="dark"] .seg-link.is-active { color:var(--primary-light); }
.rpt-tabs { max-width:520px; margin-bottom:16px; }
.rpt-tabs--5 { max-width:none; }   /* 5 tab (S30 +ติดตามทีม) — ให้ยืดพอ ไม่บีบ 4 tab เดิมเบี้ยว */
.rpt-tabs--6 { max-width:none; }   /* 6 tab (S44 E4-4A +Plan vs Actual) — regression watch ยู §10: "--5 → --6"
                                       เดิม ห้ามให้ tab ที่ 6 บีบ 5 tab เดิมเบี้ยว (มือถือ <768 มี .report-head
                                       .rpt-tabs{max-width:none} ครอบอยู่แล้วบรรทัด 2889 ไม่กระทบ) */

/* หัวรายงาน lean — หัวข้อ + tab (level เดียวกัน) + Export บรรทัดเดียว */
.report-head { align-items:center; justify-content:flex-start; flex-wrap:wrap; margin-bottom:12px; }
.report-head h1 { flex-shrink:0; }
.report-head .rpt-tabs { margin-bottom:0; flex:0 1 auto; }
.report-head .seg-link { flex:0 0 auto; white-space:nowrap; padding-left:14px; padding-right:14px; }
.report-head .rpt-export { margin-left:auto; flex-shrink:0; }
/* มือถือ: stack แนวตั้ง — tab เต็มกว้าง 3 ช่องเท่ากัน (กดครบ ไม่ล้นขอบ) + Export เต็มกว้าง */
@media (max-width:768px){
  .report-head { flex-direction:column; align-items:stretch; }
  .report-head .rpt-tabs { width:100%; max-width:none; }
  .report-head .seg-link { flex:1; white-space:normal; padding-left:8px; padding-right:8px; }
  .report-head .rpt-export { margin-left:0; width:100%; justify-content:center; }
}

.tbl--report { min-width:880px; }
.tbl-th--num { text-align:right; } /* .tbl-td--num right-align รวมเป็น global rule แล้ว (บรรทัด ~1339) — ไม่ต้อง scope ซ้ำที่นี่ */
.tbl-th--ratio, .tbl-td--ratio { min-width:140px; text-align:right; } /* จัดขวาให้เข้าแถวคอลัมน์ num อื่น (เดิมลืมใส่ → Ratio โดดชิดซ้าย เบี้ยว) */

/* ==== Cross-View: คอลัมน์ Space / List (Task A6) — ใต้ {{if .Cross}} เท่านั้น ====
   Wegital DS: even-pixel · สีจาก CSS var (ห้าม hardcode) · light+dark ผ่าน token
   col-new = ไฮไลต์จางบอกว่าเป็นคอลัมน์ที่เพิ่มมาในมุมมองรวม (derive จาก --primary-solid) */
.col-new { background:color-mix(in srgb, var(--primary-solid) 5%, transparent); }
.tbl-th--cv { white-space:nowrap; }
.space-cell { display:inline-flex; align-items:center; gap:6px; min-width:0; overflow-wrap:anywhere; }
.space-dot  { width:8px; height:8px; border-radius:999px; flex-shrink:0; background:var(--chip-c,var(--lb-gray)); box-sizing:border-box;
  /* [M3] ขอบเข้มดัน non-text contrast ≥3:1 (WCAG 1.4.11) — สีอ่อน pink/orange/gray/teal บนพื้นขาวเดิม <3:1 */
  border:1px solid color-mix(in srgb, var(--chip-c,var(--lb-gray)) 60%, var(--text)); }
/* [M2] space badge — ตัวอักษรแรกของ Space บนแถบ timeline/calendar (ไม่พึ่งสีล้วน WCAG 1.4.1) — tint แบบ lb-chip ผ่าน AA */
.space-badge { display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; aspect-ratio:1/1;
  flex-shrink:0; border-radius:999px; font-size:11px; font-weight:600; line-height:1;
  background:color-mix(in srgb, var(--chip-c,var(--lb-gray)) 22%, var(--surface));
  color:color-mix(in srgb, var(--chip-c,var(--lb-gray)) 60%, var(--text)); }
.list-cell  { display:inline-flex; align-items:center; gap:6px; min-width:0; color:var(--text-2); overflow-wrap:anywhere; }
.list-cell i.fi { font-size:12px; flex-shrink:0; }
/* location chip บนการ์ด Board/Timeline/Calendar cross (ใต้ {{if .Cross}}) — บอก Space ของการ์ด
   (Space color จาก spaceColorKey) · pill จาง สีจาก token (light+dark) */
.bd-loc { margin:2px 0 1px; }
.loc-chip { display:inline-flex; align-items:center; gap:4px; max-width:100%; padding:2px 8px; border-radius:999px;
  background:var(--surface-2); color:var(--text-2); font-size:11px; line-height:16px; overflow:hidden; }
.loc-chip .space-dot { width:8px; height:8px; }
.loc-chip-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* space color key (spaceColorKey → 8 สี DS palette) → --chip-c token */
.space-dot--coral  { --chip-c:var(--primary-solid); }
.space-dot--blue   { --chip-c:var(--lb-blue); }
.space-dot--green  { --chip-c:var(--lb-green); }
.space-dot--purple { --chip-c:var(--lb-violet); }
.space-dot--orange { --chip-c:var(--lb-orange); }
.space-dot--teal   { --chip-c:var(--lb-teal); }
.space-dot--pink   { --chip-c:var(--lb-pink); }
.space-dot--gray   { --chip-c:var(--lb-gray); }

/* sticky คอลัมน์แรก (ชื่อสมาชิก/โปรเจค) — 14 คอลัมน์เลื่อนแนวนอนแล้วยังเห็นว่าแถวไหน
   reuse pattern .tbl-td--title (app.css) — ต้องมี background ทึบ ไม่งั้นตัวเลขที่เลื่อนมาทะลุ */
.tbl--report .tbl-th:first-child        { position:sticky; left:0; z-index:3; background:var(--surface); }
.tbl--report .tbl-row .tbl-td:first-child   { position:sticky; left:0; z-index:2; background:var(--surface); }
.tbl--report .rpt-grouprow td:first-child   { position:sticky; left:0; z-index:2; background:var(--surface-2); }
.tbl--report .rpt-totalrow td:first-child   { position:sticky; left:0; z-index:2; background:var(--surface); }
.tbl--report .tbl-row:hover .tbl-td:first-child { background:var(--surface-2); }
/* S32 polish: depth cue — soft right shadow บนคอลัมน์ตรึง แยกจากตัวเลขที่เลื่อนผ่านใต้ (ชัดว่าคอลัมน์แรก float) */
.tbl--report .tbl-th:first-child,
.tbl--report .tbl-row .tbl-td:first-child,
.tbl--report .rpt-grouprow td:first-child,
.tbl--report .rpt-totalrow td:first-child { box-shadow:2px 0 4px -2px rgba(0,0,0,.10); }

/* pending (bucket B/C ยังไม่คำนวณ) — italic เป็น cue แทน opacity เพื่อคง contrast ผ่าน AA (WCAG 1.4.3) */
.rpt-wait { color:var(--text-2); font-size:12px; font-style:italic; }
.rpt-note { font-size:12px; color:var(--text-2); display:inline-flex; align-items:center; gap:6px; }
.rpt-job { font-size:12px; color:var(--text-2); font-weight:400; }
.rpt-sublabel { font-size:11px; color:var(--text-2); font-weight:400; } /* M2: คำอธิบาย grain คอลัมน์ (เดือนนี้) */

/* group header + total row */
.rpt-grouprow td { background:var(--surface-2); font-size:var(--fs-sm);
  max-width:none; white-space:normal; overflow:visible; } /* colspan เต็มแถว — override .tbl-td clip */
.rpt-grouprow td b { overflow-wrap:anywhere; }
.rpt-ghead { display:inline-flex; align-items:center; gap:8px; vertical-align:middle; } /* avatar+ชื่อ บรรทัดเดียว (avatar เป็น flex/block) */
.rpt-grouprow i.fi { margin-right:6px; color:var(--primary-solid); }
.rpt-totalrow td { border-top:1px solid var(--border); font-weight:500; color:var(--text); }
.rpt-td--indent { padding-left:24px; overflow-wrap:anywhere; }

/* ratio / complete bar (data-viz = --secondary, DS §5) */
.rpt-ratio { display:flex; align-items:center; gap:8px; }
.rpt-ratio-v { font-size:var(--fs-sm); font-weight:500; min-width:44px; text-align:right; }
.rpt-bar { flex:1; height:8px; min-width:48px; max-width:120px; border-radius:999px;
  background:var(--surface-2); overflow:hidden; }
.rpt-bar-fill { display:block; height:100%; border-radius:999px; background:var(--secondary-chart); } /* S41: --secondary-chart (audit §1 — dark contrast fix) */

/* mobile card (List-first — DS §6.3) */
.rpt-mobile { display:none; flex-direction:column; gap:12px; }
.rpt-mcard { background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:16px; display:flex; flex-direction:column; gap:10px; }
.rpt-mcard-head { display:flex; align-items:center; gap:8px; font-size:14px; overflow-wrap:anywhere; }
.rpt-mcard-head b { min-width:0; overflow-wrap:anywhere; }
.rpt-mcard-head i.fi { color:var(--primary-solid); }
.rpt-mcard-total { margin-left:auto; font-size:12px; font-weight:600; color:var(--primary-aa); white-space:nowrap; } /* S27-a11y (เอ): 4.43→5.09:1 AA */
[data-theme="dark"] .rpt-mcard-total { color:var(--primary-light); }
.rpt-mrow { display:grid; grid-template-columns:1fr auto; align-items:center; gap:4px 8px; font-size:var(--fs-sm); }
.rpt-mrow-name { overflow-wrap:anywhere; min-width:0; }
.rpt-mrow-val { color:var(--text-2); font-size:12px; white-space:nowrap; }
.rpt-mrow .rpt-bar { grid-column:1 / -1; max-width:none; }
.rpt-mcap { font-size:12px; color:var(--text-2); display:flex; align-items:center; gap:6px;
  border-top:1px solid var(--border); padding-top:8px; overflow-wrap:anywhere; }
.rpt-mcap i.fi { color:var(--primary-solid); }
.rpt-mwait { font-size:11px; color:var(--text-2); display:flex; align-items:center; gap:6px;
  border-top:1px solid var(--border); padding-top:8px; overflow-wrap:anywhere; }
@media (max-width:768px) { .rpt-mobile { display:flex; } }

/* ---- Report ถัง C (Milestone/Variance + Budget, DEC-060) ---- */
/* แผนรวม % ต่อคน (soft-warn) บน group header — เตือนไม่บล็อก */
.rpt-alloc { font-size:11px; font-weight:500; color:var(--text-2);
  background:var(--surface); border:1px solid var(--border); border-radius:999px;
  padding:2px 8px; display:inline-flex; align-items:center; gap:4px; margin-left:4px; white-space:nowrap; }
.rpt-alloc i.fi { font-size:12px; }
/* ใช้ *-text (เข้มผ่าน AA) เป็นสีตัวอักษร; border ยังใช้ --alert/--warm (เส้นขอบไม่นับ text-contrast) */
.rpt-alloc--over  { color:var(--alert-text); border-color:color-mix(in srgb, var(--alert) 40%, var(--border)); }
.rpt-alloc--under { color:var(--warm-text);  border-color:color-mix(in srgb, var(--warm) 40%, var(--border)); }
.rpt-incomplete { font-size:11px; color:var(--warm-text); font-weight:500; white-space:nowrap; }
.rpt-mrow-sub { grid-column:1 / -1; font-size:11px; color:var(--text-2); overflow-wrap:anywhere; }
.rpt-mcap--warn, .rpt-mcap--warn i.fi { color:var(--warm-text); }

/* ---- Custom Pivot Report (tab ที่ 3) ---- */
.rpt-tabs--3 { max-width:640px; }
.rpt-tabs--4 { max-width:800px; }
/* ตารางคอลัมน์แบบ dynamic — ไม่บังคับ min-width 880 (คอลัมน์น้อยไม่ต้อง scroll เกินจำเป็น) */
.tbl--pivot { min-width:auto; }
.tbl--pivot .tbl-th i.fi, .tbl--pivot .rpt-mrow-name i.fi { color:var(--text-2); margin-right:2px; }
/* sticky คอลัมน์แรก (ชื่อ project) — เลื่อนแนวนอนแล้วยังเห็นว่าแถวไหน (pattern .tbl-td--title)
   + เงาขวาเป็น scroll hint บอกว่าเลื่อนแนวนอนต่อได้ (alpha เข้มพอให้เห็น) */
.tbl--pivot .tbl-th:first-child,
.tbl--pivot .tbl-row .tbl-td:first-child,
.tbl--pivot .rpt-totalrow td:first-child {
  position:sticky; left:0; z-index:2; background:var(--surface);
  box-shadow:2px 0 6px rgba(0,0,0,.14);
}
.tbl--pivot .tbl-th:first-child { z-index:3; } /* header มุมซ้ายบนทับทั้งแนวตั้ง+นอน */
/* hover: sticky cell เปลี่ยนพื้นตามแถว (ทับ background:surface ของตัวเอง) */
.tbl--pivot .tbl-row:hover .tbl-td:first-child { background:var(--surface-2); }

/* metric picker */
.pv-colbtn { min-width:0; }
.pv-mp-lb { display:inline-flex; align-items:center; gap:6px; overflow-wrap:anywhere; }
.pv-mp-lb i.fi { color:var(--primary-solid); }
.pv-mp-apply { width:100%; justify-content:center; margin-top:12px; }
/* S32 — range sheet (min/max) filter ตัวเลข */
.pv-range-row { display:flex; align-items:center; gap:12px; padding:10px 8px; }
.pv-range-lb { flex-shrink:0; width:64px; font-size:var(--fs-sm); color:var(--text-2); }
.pv-range-row .fb-inp { flex:1; min-width:0; }

/* chips ของ metric ที่เลือก (removable) */
.pv-chips { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-bottom:12px; }
.pv-chips-lb { font-size:12px; font-weight:500; color:var(--text-2); }
.pv-chip { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:500;
  background:var(--surface-2); color:var(--text); border-radius:999px; padding:4px 6px 4px 12px; white-space:nowrap; }
.pv-chip > i.fi { color:var(--primary-solid); font-size:12px; }
.pv-chip-x { position:relative; display:inline-flex; align-items:center; justify-content:center; aspect-ratio:1/1; flex-shrink:0;
  width:18px; height:18px; border:none; border-radius:50%; cursor:pointer;
  background:transparent; color:var(--text-2); font-size:12px; }
/* ขยาย tap area เป็น 44px (WCAG 2.5.5) โดยคง visual 18px — invisible hit target ครอบ */
.pv-chip-x::before { content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:44px; height:44px; border-radius:50%; }
.pv-chip-x:hover { background:var(--surface); color:var(--alert); }
.pv-chip-x:disabled { opacity:.35; cursor:default; } /* ✕ ตัวสุดท้าย: ห้ามลบจนเหลือ 0 คอลัมน์ */
.pv-chip-x:disabled::before { display:none; }
.pv-foot { margin-top:10px; }
.pv-mtotal { border-color:var(--primary-light); }
.pv-mtotal .rpt-mcard-head i.fi { color:var(--primary-solid); }

/* ==== Pivot Table v2 (Excel-style: collapsible field panel + multi-level header) ==== */
/* field panel = แถบสรุปพับได้ (progressive disclosure) → ตาราง pivot เป็น hero */
.pv-fieldpanel { background:var(--surface); border:1px solid var(--border); border-radius:12px;
  margin-bottom:16px; overflow:hidden; }
/* แถบสรุป (collapsed) — คลิกทั้งแถบ toggle */
.pv-summary { width:100%; display:flex; align-items:center; flex-wrap:wrap; gap:8px 16px;
  min-height:44px; padding:12px 16px; background:transparent; border:none; cursor:pointer;
  text-align:left; font-size:12px; color:var(--text); }
.pv-summary:hover { background:var(--surface-2); }
.pv-summary-lead { font-weight:600; color:var(--text); display:inline-flex; align-items:center; gap:6px; }
.pv-summary-lead i.fi { color:var(--primary-solid); }
.pv-sum-part { min-width:0; overflow-wrap:anywhere; }
.pv-sum-k { color:var(--text-2); font-weight:500; }
.pv-chev { margin-left:auto; color:var(--text-2); flex-shrink:0; display:inline-flex; transition:transform .2s; }
.pv-fieldpanel.is-open .pv-chev { transform:rotate(180deg); }

/* panel body (expanded) — compact แถวแนวนอน (สูงตามเนื้อหา) */
.pv-panel-body { display:none; }
.pv-fieldpanel.is-open .pv-panel-body { display:flex; flex-direction:column; gap:12px;
  padding:12px 16px 16px; border-top:1px solid var(--border); }
.pv-row { display:flex; align-items:flex-start; gap:12px; }
.pv-row-label { flex:0 0 auto; width:104px; min-height:36px; font-size:12px; font-weight:600; color:var(--text-2);
  display:inline-flex; align-items:center; gap:6px; }
.pv-row-label i.fi { color:var(--primary-solid); flex-shrink:0; }
.pv-row-items { flex:1 1 0; min-width:0; display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.pv-row-sep { color:var(--text-2); }

/* field chip (row/col/value/filter) — inline auto width, wrap ในแถว (ไม่บีบ) */
.pv-fchip { display:inline-flex; align-items:center; gap:6px; max-width:100%; font-size:12px; font-weight:500;
  background:var(--surface-2); color:var(--text); border-radius:8px; padding:6px 6px 6px 10px;
  overflow-wrap:anywhere; }
.pv-fchip > i.fi { color:var(--text-2); font-size:12px; flex-shrink:0; }
.pv-vlabel { min-width:0; overflow-wrap:anywhere; }
.pv-vchip .pv-chip-x { flex-shrink:0; }
/* agg selector บน value chip — color:--text (brand-red text fail AA บนพื้นเข้ม dark) · hit-area 44 · nowrap */
.pv-vchip .pv-aggbtn { position:relative; display:inline-flex; align-items:center; gap:4px; flex-shrink:0;
  white-space:nowrap; font-size:11px; font-weight:500;
  background:var(--surface); border:1px solid var(--border); color:var(--text);
  border-radius:6px; padding:2px 6px; min-height:28px; cursor:pointer; }
.pv-vchip .pv-aggbtn > i.fi { color:var(--text-2); }
.pv-vchip .pv-aggbtn:hover { border-color:var(--primary-light); }
.pv-vchip .pv-aggbtn::before { content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  min-width:44px; width:100%; height:44px; } /* ขยาย tap area ≥44px (WCAG 2.5.5) คง visual 28px */
.pv-filchip > i.fi { color:var(--text-2); } /* check-circle: --green fail 3:1 บน surface-2 → neutral ผ่านทั้ง 2 mode */

/* add button per zone — touch target 44 (WCAG 2.5.5) */
.pv-addbtn { display:inline-flex; align-items:center; gap:6px; min-height:44px; font-size:12px; font-weight:500;
  background:transparent; border:1px dashed var(--border); color:var(--text-2);
  border-radius:8px; padding:8px 12px; cursor:pointer; }
.pv-addbtn:hover { border-color:var(--primary-light); color:var(--primary-solid); }
.pv-addbtn:disabled { opacity:.45; cursor:default; } /* ครบ 3 ระดับ/แกน → disable (แทน alert) */
.pv-addbtn:disabled:hover { border-color:var(--border); color:var(--text-2); }

/* multi-level header — แยกชั้นด้วยพื้น + เส้นเข้มขึ้น (เดือน>สถานะ>ค่า อ่านง่าย) */
.tbl--pivot .pv-hdr { text-align:center; vertical-align:middle; border-left:1px solid var(--border);
  border-bottom:1px solid color-mix(in srgb, var(--text-2) 35%, var(--border)); }
.tbl--pivot .pv-hdr.tbl-th--num { text-align:right;
  background:color-mix(in srgb, var(--primary-solid) 7%, var(--surface-2)); } /* ชั้นค่า tint แยกจากชั้น dim */
.tbl--pivot thead th:first-child { text-align:left; }
.tbl--pivot .pv-totcell { background:color-mix(in srgb, var(--primary-solid) 5%, var(--surface)); }

/* ==== Pivot layout tabular + collapse (S15) ==== */
/* row-header cell (tabular: N คอลัมน์ / group label compact) — ชิดซ้าย + ตัดคำไทยได้
   max-width เลขคู่ (ไม่ใช่ none) เพื่อให้ overflow-wrap ทำงาน — label ไทยยาวขึ้นบรรทัดแทนดันคอลัมน์ */
.tbl--pivot .rpt-td--rowhdr, .tbl--pivot .pv-glabel {
  text-align:left; white-space:normal; overflow:visible; overflow-wrap:anywhere; max-width:240px; font-size:var(--fs-sm);
}
.tbl--pivot .pv-glabel b, .tbl--pivot .rpt-td--rowhdr b { overflow-wrap:anywhere; }
/* ปุ่ม +/- ย่อขยายกลุ่ม — visual = hit-area 28px (≥24px WCAG 2.5.8 AA) ไม่มี ::before ล้น
   (เดิม ::before 44px ทับแถว/ปุ่มข้างเคียงตาม indent depth → กดพลาด) · Uicons · focus = shared outline */
.pv-tog { display:inline-flex; align-items:center; justify-content:center; aspect-ratio:1/1;
  flex-shrink:0; width:28px; height:28px; margin-right:6px; vertical-align:middle; cursor:pointer;
  border:1px solid var(--border); border-radius:6px; background:var(--surface); color:var(--text-2); font-size:12px; }
.pv-tog:hover { border-color:var(--primary-light); color:var(--primary-solid); }
/* ยอดรวมกลุ่มบน group row — ซ่อน default (คง column width) → โผล่เมื่อพับ (.is-collapsed) */
.tbl--pivot .pv-grouprow .pv-gval { visibility:hidden; }
.tbl--pivot .pv-grouprow.is-collapsed .pv-gval { visibility:visible; font-weight:600; }
/* segmented ขนาดเล็ก (layout/subtotal) — ไม่ยืดเต็ม, พอดีเนื้อหา
   active = --text + weight 600 (ไม่ใช่ --primary-solid ที่ contrast < 4.5 AA บน surface ทั้ง 2 mode)
   — override เฉพาะ pv control ใหม่ (ไม่แตะ .seg-link global ที่ tab ทั้งแอป reuse) */
.pv-segmini { flex:0 0 auto; width:auto; }
.pv-segmini .seg-link { flex:0 0 auto; min-height:36px; padding:6px 14px; font-size:12px; }
.pv-segmini .seg-link.is-active { color:var(--text); font-weight:600; }
/* ghost button (พับ/กางทั้งหมด) — ❌ ไม่ reuse .pv-addbtn (เส้นประ = convention "เพิ่ม" ทั้งแอป) */
.pv-actbtn { display:inline-flex; align-items:center; gap:6px; min-height:36px; font-size:12px; font-weight:500;
  background:var(--surface); border:1px solid var(--border); color:var(--text-2);
  border-radius:8px; padding:6px 12px; cursor:pointer; }
.pv-actbtn:hover { border-color:var(--primary-light); color:var(--text); }
.pv-actbtn i.fi { color:var(--text-2); }

/* banner (truncated / row warn) = คำเตือน (amber) ไม่ใช่ error (แดง) */
.pv-banner { display:flex; align-items:center; gap:8px; font-size:12px; border-radius:10px;
  padding:8px 12px; margin-bottom:10px; }
.pv-banner--warn { background:var(--warn-bg); color:var(--warm-text); }
.pv-banner i.fi { flex-shrink:0; }

/* mobile: field panel = view-only (spec §10) — โชว์แค่แถบสรุป, พับ/กางไม่ได้ */
.pv-mnote { display:none; }
@media (max-width:768px){
  .pv-fieldpanel.is-open .pv-panel-body { display:none; } /* บังคับ collapsed (แก้ config ไม่ได้บนจอแคบ) */
  .pv-summary { cursor:default; }
  .pv-chev { display:none; }
  .pv-mnote { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2);
    border-top:1px solid var(--border); padding:8px 16px; }
  .pv-mnote i.fi { color:var(--primary-solid); }
}

/* ==== Save View (Pivot §8) ==== */
/* view picker strip (นอก panel-body → มือถือเลือก view + เห็นชื่อ view ปัจจุบันได้) */
.pv-viewstrip { display:flex; align-items:center; flex-wrap:wrap; gap:8px; padding:12px 16px;
  border-top:1px solid var(--border); }
.pv-viewstrip-lb { font-size:12px; font-weight:600; color:var(--text-2); display:inline-flex; align-items:center; gap:6px; }
.pv-viewstrip-lb i.fi { color:var(--primary-solid); }
.pv-viewrow .pv-row-items { align-items:center; }
.pv-viewbtn { width:auto; min-width:0; max-width:280px; }
.pv-viewbtn .dd-label { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.pv-trim { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--warm-text);
  background:var(--warn-bg); border-radius:8px; padding:4px 10px; overflow-wrap:anywhere; }
.pv-trim i.fi { flex-shrink:0; }
/* sheet section header (จัดกลุ่ม view ใน picker) */
.sheet-section-h { font-size:11px; font-weight:600; color:var(--text-2); padding:12px 8px 4px; }
/* save dialog */
.pv-savebox { display:flex; flex-direction:column; gap:8px; padding:4px 8px 8px; }
.pv-vinput { width:100%; min-height:44px; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border-control); border-radius:10px; padding:10px 12px; } /* S27 Batch2: border-control (AA 1.4.11) */
.pv-vinput:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.pv-vinput--err { border-color:var(--alert); }
.pv-savebox + .btn, .pv-savebox + .btn-ghost, .btn + .pv-mp-apply, .btn-ghost + .pv-mp-apply { margin-top:4px; }
/* inline error ใน sheet (แทน native alert) */
.pv-saveerr, .pv-confirm-err { color:var(--alert-text); background:var(--alert-bg); border-radius:8px;
  padding:8px 10px; font-size:12px; margin-top:4px; overflow-wrap:anywhere; }
.pv-del { color:var(--alert-text); }
.pv-del:hover { border-color:color-mix(in srgb, var(--alert) 40%, var(--border)); }
/* ปุ่มลบ: เว้นระยะห่างปุ่มอื่นชัด (กันกดผิด) — destructive แยกกลุ่ม */
.pv-del-sep { margin-top:24px; }
/* confirm overlay (ซ้อนเหนือ save sheet — z สูงกว่า sheet 4000) */
.pv-confirm-bd { z-index:4100; }
.pv-confirm-msg { font-size:14px; color:var(--text); overflow-wrap:anywhere; padding:4px 8px; }
.pv-confirm-msg b { color:var(--alert-text); }

/* ---- Manage Monthly Plan (grid ratio%) ---- */
.plan-grid { min-width:640px; }
.plan-th--name, .plan-td--name { text-align:left; white-space:nowrap; }
.plan-td--name .avatar { vertical-align:middle; margin-right:6px; }
.plan-cell { text-align:center; }
.plan-in { width:64px; min-height:44px; text-align:right; font-family:inherit; font-size:var(--fs-sm);
  padding:10px 8px; border:1px solid var(--border); border-radius:8px; /* touch target 44px (WCAG 2.5.5) — ตรง .seg-link */
  background:var(--surface); color:var(--text); }
.plan-in:focus { outline:none; border-color:var(--primary-solid); }
.plan-sum { font-weight:600; text-align:right; white-space:nowrap; }
.plan-sum--over  { color:var(--alert-text); }
.plan-sum--under { color:var(--warm-text); }
.plan-help { font-size:12px; color:var(--text-2); display:flex; align-items:center; gap:6px;
  margin-top:12px; overflow-wrap:anywhere; }
.plan-help i.fi { color:var(--primary-solid); }
.plan-actions { margin-top:16px; display:flex; justify-content:flex-end; align-items:center; gap:12px; }

/* success alert (modifier ของ .alert-box) */
.alert-box--ok { background:color-mix(in srgb, var(--green) 16%, var(--surface)); color:var(--green-text); } /* S27 P0: text จริง (plan.html) ใช้ --green ดิบ 1.82:1 fail — green-text (AA), เจอเพิ่มนอก audit list เดิม */

/* ============================================================
   วีวี — หน้าแชทผู้ช่วย AI (M1 ask-your-data) — Wegital DS
   ============================================================ */
.vivi-page { display:flex; flex-direction:column; max-width:820px; }
.vivi-head { display:flex; align-items:center; gap:12px; }
.vivi-ava {
  flex-shrink:0; width:44px; height:44px; border-radius:14px;
  background:var(--primary); color:#fff;
  display:inline-flex; align-items:center; justify-content:center; font-size:20px;
}
.vivi-ava--sm { width:30px; height:30px; border-radius:10px; font-size:14px; }
.vivi-demo {
  display:inline-flex; align-items:center; gap:6px; flex-shrink:0;
  font-size:12px; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border);
  border-radius:999px; padding:4px 12px;
}
.vivi-demo i.fi { color:var(--primary-solid); }
/* spinner ระหว่างถามวีวี (reuse .ie-spin) — โชว์ทั้งตอนกดส่งและกด chip */
.vivi-spin { padding:0 4px 6px; }

.vivi-feed {
  flex:1; overflow-y:auto; min-height:0;
  display:flex; flex-direction:column; gap:14px;
  padding:16px 4px; margin-top:8px;
}
.vivi-msg { display:flex; align-items:flex-end; gap:8px; max-width:100%; }
.vivi-msg--bot { justify-content:flex-start; }
.vivi-msg--user { justify-content:flex-end; }
.vivi-bubble {
  white-space:pre-line; font-size:14px; line-height:1.6;
  border-radius:var(--r); padding:12px 16px; max-width:76%;
  overflow-wrap:anywhere; word-break:break-word; min-width:0;
}
.vivi-bubble--bot { background:var(--surface); border:1px solid var(--border); color:var(--text); border-bottom-left-radius:4px; }
/* พื้น coral ทึบ (--primary-aa) แทน gradient → ขาวผ่าน AA 5.09:1 */
.vivi-bubble--user { background:var(--primary-aa); color:#fff; border-bottom-right-radius:4px; }

.vivi-chips { display:flex; flex-wrap:wrap; gap:8px; padding:4px 0 12px; }
.vivi-chip {
  display:inline-flex; align-items:center; gap:6px; cursor:pointer;
  font-family:inherit; font-size:var(--fs-sm); color:var(--text);
  background:var(--surface); border:1px solid var(--border);
  border-radius:999px; padding:12px 16px; min-height:44px; transition:border-color .15s, color .15s;
}
.vivi-chip:hover { border-color:var(--primary-solid); color:var(--primary-solid); }
.vivi-chip i.fi { color:var(--primary-solid); font-size:14px; }
/* disable ระหว่าง request (กันกดซ้อน) + spin ไอคอนเป็น loading cue */
.vivi-chip:disabled { opacity:.5; cursor:default; }
.vivi-chip.htmx-request i.fi { animation:ie-spin 1s linear infinite; }

.vivi-composer {
  display:flex; align-items:flex-end; gap:8px;
  padding:12px; border-top:1px solid var(--border);
  background:var(--surface); border-radius:var(--r);
}
.vivi-input { min-height:0; height:auto; resize:none; border-radius:14px; }
.vivi-input::placeholder { color:var(--text-2); opacity:.7; }
/* touch target 44px (DS §6.3 / WCAG 2.5.5) — เหมือน .cmt-send */
.vivi-send { flex-shrink:0; align-self:stretch; min-width:44px; min-height:44px;
  display:inline-flex; align-items:center; justify-content:center; }
.vivi-send:disabled { opacity:.4; cursor:default; }

/* ---------------------------------------------------------------------------
   Home "สุขภาพงานวันนี้" (วีวี M2 sensor) — stat cards + risk list (light+dark ผ่าน var)
   ❌ ไม่พึ่งสีอย่างเดียว: badge มี icon+label · ใช้ text-safe var (--warm-text/--alert-text)
   --------------------------------------------------------------------------- */
/* stat cards: auto-fit ให้ไหล 4 ใบเมื่อกว้าง / ย่อ 2 ใบเมื่อจอกลาง (ไม่ต้อง media แยก) */
.hp-stats { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:16px; margin-bottom:16px; }
.hp-stat {
  display:flex; align-items:center; gap:16px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; box-shadow:var(--shadow-card); min-width:0;
}
.hp-stat-icon {
  width:44px; height:44px; flex:0 0 44px; border-radius:12px;
  display:flex; align-items:center; justify-content:center; aspect-ratio:1/1;
  font-size:20px;
}
.hp-stat--red    .hp-stat-icon { background:var(--alert-bg); color:var(--alert-text); }
.hp-stat--yellow .hp-stat-icon { background:var(--warn-bg); color:var(--warm-text); }
.hp-stat--stuck  .hp-stat-icon { background:var(--surface-2); color:var(--text-2); }
.hp-stat--miss   .hp-stat-icon { background:var(--warn-bg); color:var(--warm-text); }
.hp-stat-num { font-size:24px; font-weight:600; line-height:1.1; }
.hp-stat-label { font-size:12px; color:var(--text-2); font-weight:500; margin-top:2px; }

.hp-list { display:flex; flex-direction:column; gap:12px; }
.hp-item {
  display:flex; align-items:flex-start; gap:12px;
  padding:12px; border:1px solid var(--border); border-radius:12px; background:var(--surface);
  min-width:0;
}
.hp-badge {
  display:inline-flex; align-items:center; gap:6px; flex-shrink:0;
  font-size:11px; font-weight:500; border-radius:999px; padding:4px 10px;
}
.hp-badge i.fi { font-size:12px; }
.hp-badge--red    { background:var(--alert-bg); color:var(--alert-text); }
.hp-badge--yellow { background:var(--warn-bg); color:var(--warm-text); }
.hp-badge--stuck  { background:var(--surface-2); color:var(--text-2); }
.hp-badge--miss   { background:var(--warn-bg); color:var(--warm-text); }
/* .hp-item-body = anchor คลุมทั้งแถว (คลิกได้ ≥44px จากความสูงเนื้อหา) — pattern เดียวกับ .tk-rowmain */
.hp-item-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; text-decoration:none; color:inherit; }
.hp-item-title {
  font-size:14px; font-weight:500; color:var(--text);
  overflow-wrap:anywhere; word-break:break-word;
}
.hp-item-body:hover .hp-item-title { color:var(--primary-solid); }
.hp-item-meta {
  display:flex; align-items:center; flex-wrap:wrap; gap:12px;
  font-size:12px; color:var(--text-2);
}
.hp-item-meta span { display:inline-flex; align-items:center; gap:4px; overflow-wrap:anywhere; }
.hp-item-meta i.fi { font-size:12px; }
/* burn_only tag — โทนเตือนอ่อน (--warm) ไม่ใช่ grey-neutral (ที่ดูไม่น่ากังวล ขัดเจตนา) */
.hp-tag-warn {
  display:inline-flex; align-items:center; gap:4px;
  color:var(--warm-text); background:var(--warn-bg);
  border-radius:999px; padding:2px 8px; font-size:11px;
}
.hp-tag-warn i.fi { font-size:11px; }
/* burn_only note — คำอธิบาย inline ที่เห็นตลอด (ไม่พึ่ง hover) */
.hp-item-note {
  display:flex; align-items:flex-start; gap:4px;
  font-size:11px; color:var(--warm-text); overflow-wrap:anywhere;
}
.hp-item-note i.fi { font-size:11px; margin-top:2px; flex-shrink:0; }
.hp-item-reason {
  display:flex; align-items:flex-start; gap:4px;
  font-size:12px; color:var(--text-2); overflow-wrap:anywhere;
}
.hp-item-reason i.fi { font-size:11px; margin-top:2px; flex-shrink:0; }

/* ---------------------------------------------------------------------------
   Home 3A (S30, DEC-075) — คิวงานวันนี้ของฉัน + stat กดได้ + สะกิด Daily
   ทุกสีมาจาก token (light+dark) · ❌ ไม่มี left-accent · focus ring ตาม DS §0.5
   --------------------------------------------------------------------------- */
/* stat กดได้ (ปิด B4) — ทั้งใบเป็น <a>; count=0 → ไม่เป็นลิงก์ (กัน dead end) */
a.hp-stat { text-decoration:none; color:inherit; position:relative; transition:background .15s ease; }
a.hp-stat:hover { background:var(--surface-2); }
a.hp-stat:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.hp-stat-go { margin-left:auto; color:var(--text-2); font-size:16px; flex-shrink:0; }
a.hp-stat:hover .hp-stat-go { color:var(--text); }
.hp-stat--zero .hp-stat-num { color:var(--text-2); }
.hp-stat--zero .hp-stat-icon { opacity:.6; }

/* ป้ายบอกขอบเขต เหนือแถว stat (scope เปลี่ยนกลางหน้า: ของฉัน → ทีม/บริษัท) */
.hq-scope { display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-2);
  font-weight:500; margin:16px 0 8px; }
.hq-scope i.fi { font-size:12px; }

/* คิวงาน — ถัง/หัวถัง/ปุ่มดูทั้งหมด */
.hq-total { margin-left:auto; font-size:12px; font-weight:500; color:var(--text-2); }
.hq-bucket { display:flex; flex-direction:column; gap:8px; }
.hq-bucket-head { display:flex; align-items:center; gap:8px; font-size:12px; font-weight:500;
  color:var(--text-2); min-height:24px; }
.hq-bucket-head i.fi { font-size:12px; }
.hq-bucket-name { overflow-wrap:anywhere; }
.hq-bucket-count { background:var(--surface-2); color:var(--text-2); border-radius:999px;
  padding:2px 8px; font-size:11px; font-weight:500; }
.hq-more { align-self:flex-start; display:inline-flex; align-items:center; gap:4px;
  min-height:44px; padding:0 4px;                    /* touch target ด้วย padding ไม่ใช่ขยายตัวอักษร */
  font-size:12px; font-weight:500; color:var(--primary-solid); text-decoration:none; }
.hq-more:hover { text-decoration:underline; }
.hq-more:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-radius:8px; }
/* ถัง "ถัดไป/ไม่มีกำหนดส่ง" = native <details> (keyboard/a11y ฟรี — DEC-068#1 native-first) */
.hq-fold { display:flex; flex-direction:column; gap:8px; }
.hq-fold-sum { cursor:pointer; list-style:none; min-height:44px; }
.hq-fold-sum::-webkit-details-marker { display:none; }
.hq-fold-sum::marker { content:""; }
.hq-fold-sum:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-radius:8px; }
.hq-fold-caret { margin-left:auto; transition:transform .15s ease; }
.hq-fold[open] .hq-fold-caret { transform:rotate(180deg); }
/* badge ถังคิวงาน (ต่อจาก .hp-badge--*) */
.hp-badge--overdue     { background:var(--alert-bg); color:var(--alert-text); }
.hp-badge--today       { background:var(--warn-bg); color:var(--warm-text); }
.hp-badge--next,
.hp-badge--unscheduled { background:var(--surface-2); color:var(--text-2); }

/* Container สำหรับ nudge/empty (DS §6.5 pattern: container-type อยู่ที่ "parent" — เหมือน .bd-col-body
   → .bd-card). ❌ ห้ามตั้ง container-type บน .hq-nudge/.hq-empty เอง เพราะ @container วัดจาก ancestor
   container ตัวใกล้สุด ไม่ใช่ตัว element ที่ประกาศ → rule จะไม่ยิงเลย (บั๊ก 375px: body บีบเหลือ 0
   ตัวอักษรตกบรรทัดละตัว). ใช้ container (ไม่ใช่ @media) เพราะความกว้าง panel ขึ้นกับ drawer พับ/กาง */
.home-sec--queue .panel { container-type:inline-size; container-name:hq-panel; }

/* สะกิดลง Daily — inline card ในกล่องคิวงาน (❌ ไม่ใช่ banner, ❌ ไม่มี left-accent) */
.hq-nudge { display:flex; align-items:center; gap:16px; padding:16px;
  background:var(--warn-bg); border:1px solid color-mix(in srgb, var(--warm) 35%, transparent);
  border-radius:12px; }
.hq-nudge-icon { display:flex; align-items:center; justify-content:center; aspect-ratio:1/1;
  flex:0 0 40px; width:40px; height:40px; border-radius:12px;
  background:var(--surface); color:var(--warm-text); font-size:18px; }
.hq-nudge-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px;
  font-size:14px; overflow-wrap:anywhere; }
.hq-nudge-body span { font-size:12px; color:var(--text-2); }
.hq-nudge-cta { flex-shrink:0; min-height:44px; }
@container hq-panel (max-width:520px) {
  .hq-nudge { flex-wrap:wrap; }
  .hq-nudge-cta { width:100%; justify-content:center; }
}

/* คิวว่าง / คิวดึงไม่ได้ — อยู่ในกล่องคิวงานเท่านั้น (เรดาร์+stat ยังแสดงปกติ) */
.hq-empty { display:flex; align-items:center; gap:16px; padding:16px;
  background:var(--surface-2); border:1px solid var(--border); border-radius:12px; }
.hq-empty-icon { display:flex; align-items:center; justify-content:center; aspect-ratio:1/1;
  flex:0 0 40px; width:40px; height:40px; border-radius:12px;
  background:var(--surface); color:var(--text-2); font-size:18px; }
.hq-empty-body { flex:1; min-width:0; display:flex; flex-direction:column; gap:2px;
  font-size:14px; overflow-wrap:anywhere; }
.hq-empty-body span { font-size:12px; color:var(--text-2); }
.hq-empty-cta { flex-shrink:0; min-height:44px; }
@container hq-panel (max-width:520px) {
  .hq-empty { flex-wrap:wrap; }
  .hq-empty-cta { width:100%; justify-content:center; }
}
/* 🔴 a11y (WCAG 4.1.2 Name-Role-Value + 2.4.4 Link Purpose): global .btn-ghost span{display:none}
   @768 (icon-only pattern ของ toolbar) ทำให้ 2 ปุ่มนี้ accessible name ว่างบนมือถือ (icon aria-hidden
   + text ถูกซ่อน + ไม่มี aria-label). ปุ่มพวกนี้ wrap เต็มกว้างบนมือถืออยู่แล้ว = มีที่พอโชว์ text
   → override คงตัวอักษรไว้ (ไม่ใช่ icon-only). specificity เท่า global แต่มาทีหลัง = ชนะที่ ≤768px */
.hq-nudge-cta span, .hq-empty-cta span { display:inline; }

.hq-error { display:flex; align-items:center; gap:8px; padding:12px;
  background:var(--alert-bg); border:1px solid color-mix(in srgb, var(--alert) 35%, transparent);
  border-radius:12px; font-size:12px; color:var(--alert-text); overflow-wrap:anywhere; }

@media (max-width:768px) {
  /* stat อยู่กลางหน้าแล้ว — เรียง 2×2 ไม่ใช่ 1 คอลัมน์ ไม่งั้นดันเรดาร์หลุดจอไปไกล */
  .hp-stats { grid-template-columns:repeat(2,1fr); gap:8px; }
  .hp-stat { padding:12px; gap:8px; }
  .hp-stat-icon { width:32px; height:32px; flex:0 0 32px; font-size:16px; border-radius:10px; }
  .hp-stat-num { font-size:20px; }
  .hp-stat-go { display:none; }   /* ประหยัดพื้นที่ — ทั้งใบยังกดได้ */
  .hp-item { flex-direction:column; }
}

/* ============================================================
   §8 Status/Risk/DoD Narrative + Change Request (Wegital DS)
   ============================================================ */
/* --- narrative panel (By Project) — card DS §4, ไม่มี left-accent --- */
.rpt-noterow td { background:var(--bg); padding:8px 12px; }
.note-panel { background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; display:flex; flex-direction:column; gap:14px; box-shadow:var(--shadow-card); }
.note-panel--empty { padding:14px 20px; }
.note-head { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; }
.note-title { display:inline-flex; align-items:center; gap:8px; font-weight:600; font-size:14px; color:var(--text); }
.note-title i.fi { width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px; }
.note-field { display:flex; flex-direction:column; gap:4px; min-width:0; }
.note-text { font-size:14px; color:var(--text); overflow-wrap:anywhere; white-space:pre-wrap; margin:0; }
.note-grid3 { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; }
.note-link { display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-sm); color:var(--primary-solid);
  text-decoration:none; overflow-wrap:anywhere; }
.note-link:hover { text-decoration:underline; }
.note-meta { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2);
  border-top:1px solid var(--border); padding-top:10px; overflow-wrap:anywhere; }
.note-empty-in { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
  font-size:var(--fs-sm); color:var(--text-2); }
.note-empty-in i.fi { margin-right:6px; }
.note-ta { width:100%; font-family:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border); border-radius:10px; padding:10px 12px;
  overflow-wrap:anywhere; resize:vertical; }
.note-ta:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
@media (max-width:768px){ .note-grid3 { grid-template-columns:1fr; } }

/* --- CR module --- */
.page-head-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.cr-table td, .cr-table th { white-space:nowrap; }
.cr-name { max-width:220px; white-space:normal; overflow-wrap:anywhere; }
.cr-remark { max-width:180px; overflow:hidden; text-overflow:ellipsis; }
.cr-flag { text-align:center; }
.cr-on { color:var(--green-text); } /* S27 P0: icon-only แต่ยังต่ำกว่า 3:1 ดิบ — green-text (AA) */
.cr-code b { color:var(--primary-solid); }
.cr-mact { display:flex; flex-wrap:wrap; gap:8px; border-top:1px solid var(--border); padding-top:8px; }
.cr-spacedd { position:relative; }
.cr-spacedd .dd-menu a { display:block; padding:8px 12px; color:var(--text); text-decoration:none; border-radius:8px; }
.cr-spacedd .dd-menu a:hover { background:var(--surface-2); }
/* detail modal: 3-คอลัมน์ → 1 คอลัมน์เสมอ (กรอบ modal 440px, @media เช็ค viewport ไม่ได้ — DS §6.3) */
.note-grid3--modal { grid-template-columns:1fr; }
/* required marker แดง — S27 P0 (audit-css #2 / audit-a11y): เดิมประกาศซ้ำ 2 จุด (บรรทัดนี้ vs ~853 เดิม
   ที่ลบแล้ว) rule ท้ายชนะ ใช้ --alert ดิบ (3.41:1 fail AA) — เหลือ rule เดียว ใช้ --alert-text (ผ่าน AA) */
.req-star { color:var(--alert-text); font-weight:600; margin-left:2px; }
/* มือถือ: ซ่อนตาราง desktop ของ CR (โชว์ card แทน) — กันแสดงซ้ำ B2 */
/* (S56) ตาราง CR บนมือถือซ่อนด้วยคลาสกลาง .tbl-hascards ที่ตัว wrapper ใน cr.html แล้ว —
   ไม่มี @media เฉพาะกิจตรงนี้อีก (กลไกซ่อนตาราง = ที่เดียวทั้งไฟล์) */

/* ============================================================ วันลา: mobile landing (/m/leave)
   layout เปล่า center — กรอบ 480px · ปุ่มใหญ่ · custom control (ห้าม native) · light+dark ผ่าน var */
.mlv-page {
  min-height:100vh; background:var(--bg); display:flex; justify-content:center;
  padding:24px 16px; box-sizing:border-box;
}
.mlv-card {
  width:100%; max-width:480px; display:flex; flex-direction:column; gap:16px;
}
.mlv-head { text-align:center; display:flex; flex-direction:column; align-items:center; gap:6px; margin-bottom:4px; }
.mlv-logo {
  width:56px; height:56px; border-radius:50%; background:var(--primary); color:#fff;
  display:flex; align-items:center; justify-content:center; font-size:24px;
}
.mlv-title { font-size:22px; font-weight:600; }
/* F8: ชื่อไทยยาวไม่ล้น (พี่น้อง .tl-name-t/.lvr-up-name) · F10: font-size เข้า scale 14 */
.mlv-who { font-size:14px; color:var(--text-2); display:flex; align-items:center; justify-content:center; gap:6px; }
.mlv-who span { min-width:0; overflow-wrap:anywhere; }
.mlv-who b { color:var(--text); font-weight:600; }
.mlv-form {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; display:flex; flex-direction:column; gap:16px;
  box-shadow:var(--shadow-card);
}
.mlv-trigger { width:100%; }
/* segmented (รูปแบบลา 3 ปุ่ม) */
.mlv-seg { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; }
.mlv-seg-btn {
  display:flex; flex-direction:column; align-items:center; gap:4px; padding:12px 4px;
  border:1px solid var(--border); border-radius:12px; background:var(--surface);
  color:var(--text-2); font-size:var(--fs-sm); font-weight:500; cursor:pointer; transition:all .15s;
}
.mlv-seg-btn i.fi { font-size:18px; }
.mlv-seg-btn em { font-style:normal; font-size:11px; opacity:.7; }
.mlv-seg-btn:hover { border-color:var(--primary-light); }
.mlv-seg-btn.is-active {
  border-color:var(--primary-solid); color:var(--primary-aa); /* S27-a11y (เอ): text 4.43->5.09:1 AA (border ไม่ใช่ text ไม่ต้องแก้ ผ่าน 3:1 อยู่แล้ว) */
  background:color-mix(in srgb, var(--primary-solid) 8%, var(--surface));
}
[data-theme="dark"] .mlv-seg-btn.is-active { color:var(--primary-light); }
.mlv-textarea {
  width:100%; box-sizing:border-box; resize:vertical; font:inherit; color:var(--text);
  background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:10px 12px;
}
/* F10: focus ring ให้ตรงกับ input อื่น (Even-Pixel + a11y) */
.mlv-textarea:focus { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
/* F4: seg รูปแบบบน desktop /leave (กว้างเต็ม ไม่บีบเป็น mobile-narrow) */
.mlv-seg--wide { max-width:420px; }
.mlv-summary {
  display:flex; align-items:center; justify-content:space-between;
  background:var(--surface-2); border-radius:12px; padding:12px 16px;
}
.mlv-summary-lbl { font-size:var(--fs-sm); color:var(--text-2); display:flex; align-items:center; gap:6px; }
.mlv-summary-val { font-size:14px; color:var(--text); }
.mlv-summary-val b { font-size:22px; color:var(--primary-solid); font-weight:600; }
.mlv-hint { font-size:12px; color:var(--text-2); text-align:right; margin-top:-8px; overflow-wrap:anywhere; }
.mlv-submit { width:100%; justify-content:center; padding:14px; font-size:14px; }
.mlv-reportlink {
  display:flex; align-items:center; justify-content:center; gap:8px; padding:12px;
  color:var(--text-2); text-decoration:none; font-size:var(--fs-sm); font-weight:500;
}
.mlv-reportlink:hover { color:var(--primary-solid); }

/* ============================================================ วันลา: report gantt (/reports/leave) */
.lvr-bar {
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px;
  margin-bottom:16px;
}
.lvr-nav { display:flex; align-items:center; gap:8px; }
.lvr-month { font-size:16px; font-weight:600; min-width:160px; text-align:center; }
.lvr-legend { display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.lvr-lg { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2); }
.lvr-lg-sw {
  width:14px; height:14px; border-radius:4px; flex-shrink:0;
  background:color-mix(in srgb, var(--chip-c) 30%, var(--surface));
  border:1px solid color-mix(in srgb, var(--chip-c) 60%, var(--surface));
}
/* F10: legend swatch แยก weekend vs holiday ให้ตรงกับสีจริงในตาราง */
.lvr-lg-sw--we { background:var(--surface-2); border-color:var(--border); }
.lvr-lg-sw--hol { background:color-mix(in srgb, var(--primary-solid) 14%, var(--surface-2)); border-color:color-mix(in srgb, var(--primary-solid) 30%, var(--surface)); }
/* F7: legend ลาย่อย ½¼ */
.lvr-lg-frac {
  display:inline-flex; align-items:center; justify-content:center; width:18px; height:14px;
  border-radius:4px; font-size:10px; font-weight:600; color:var(--text-2); /* S27 Batch2: กล่อง fixed height:14px แคบ — bump เป็น 11px เสี่ยง clip แนวตั้ง เก็บ 10px ไว้ (เอสตัดสินใจภายหลังถ้าจะขยายกล่อง) */
  background:repeating-linear-gradient(45deg, var(--surface-2), var(--surface-2) 2px, transparent 2px, transparent 4px);
  border:1px solid var(--border);
}
/* วันหยุด (holiday) แรเงาเข้มกว่าเสาร์-อาทิตย์ให้แยกออก (F10)
   S44 E0 รุมเทส F1 (เอ, บังคับแก้): fill 14% mix เดิมอย่างเดียว contrast กับ surface/surface-2 แค่
   1.13-1.37:1 fail SC 1.4.11 (โดยเฉพาะ "หยุด vs เสาร์-อาทิตย์" 1.13-1.21:1 แยกไม่ออก) → คง fill 14% (subtle)
   + เพิ่ม edge marker primary-solid เต็มความเข้ม (ไม่ mix) top+bottom 2px — คำนวณจริงตรง target เอ:
   vs surface light 4.43:1/dark 3.76:1 · vs surface-2 light 3.90:1/dark 3.46:1 (ผ่าน ≥3:1 ทั้งคู่ทั้ง 2 theme)
   โทนแดง primary-solid แยกจาก weekend (เทา border-control ด้านบน) ชัดเจน — ครบ 3 สถานะ: ทำงาน/เสาร์-อาทิตย์/หยุด
   ลำดับ cascade: ประกาศทีหลัง .tl-cell--we/.tl-hd--we ในไฟล์ (specificity เท่ากัน 0,1,0) → วันที่เป็นทั้ง
   holiday+weekend พร้อมกัน ได้ผลลัพธ์เป็น holiday (แดง) ชนะ ตรงกับพฤติกรรม fill เดิมที่มีอยู่แล้ว */
.tl-hd--hol { background:color-mix(in srgb, var(--primary-solid) 14%, var(--surface-2)); border-left:2px solid var(--primary-solid); border-right:2px solid var(--primary-solid); }    /* [S47 เอส] top/bottom→left/right (คู่กับ weekend) — cell แนวตั้ง · ต้องแก้ holiday ด้วย ไม่งั้นวันหยุด-ตรง-weekend โชว์เส้นแปลกแดงเดิม */
.tl-cell--hol { background:color-mix(in srgb, var(--primary-solid) 14%, var(--surface-2)); border-left:2px solid var(--primary-solid); border-right:2px solid var(--primary-solid); }  /* [S47 เอส] คู่กับ weekend · ประกาศทีหลัง .tl-cell--we (cascade) ให้ holiday ชนะเมื่อทับ weekend — คงพฤติกรรมเดิม */
.tl-hd b { min-height:12px; }
/* S44 E0 รุมเทส F2 (เอ, บังคับแก้): เลขวันที่ .tl-hd em ปกติ inherit color:var(--text-2) จาก .tl-hd
   (parent) — บนพื้นวันหยุด 14%mix เหลือแค่ 3.81:1 light fail SC 1.4.3 (ต้อง ≥4.5:1) → override เป็น
   --text เต็ม (pattern เดียวกับ .tl-hd--today ที่ override เป็น --primary-solid อยู่แล้ว) คำนวณจริง:
   light 12.40:1 / dark 11.95:1 ผ่านเกินพอ */
.tl-hd--hol em { color:var(--text); }

/* F7: แท่งลาย่อย (fraction<1) — hatch + badge ½/¼ แยกจากเต็มวันด้วยตา (ไม่พึ่งสีอย่างเดียว) */
.tl-bar--frac {
  background:
    repeating-linear-gradient(45deg,
      color-mix(in srgb, var(--chip-c) 32%, var(--surface)) 0, color-mix(in srgb, var(--chip-c) 32%, var(--surface)) 4px,
      color-mix(in srgb, var(--chip-c) 12%, var(--surface)) 4px, color-mix(in srgb, var(--chip-c) 12%, var(--surface)) 8px);
  border-style:dashed;
}
.tl-bar-badge {
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  min-width:14px; height:16px; padding:0 3px; margin-right:4px; border-radius:4px;
  font-size:11px; font-weight:700; line-height:1;
  /* S41 P0 (เอ audit §3) — เดิม bg 55%mix+surface / color:surface (ขาว-บน-พาสเทล หรือเกือบดำ-บน-เข้ม)
     fail 1.4.3 ทุกสีทุก theme (worst light yellow 1.32:1) → reuse pattern เดียวกับ .tl-bar/.lb-chip
     (bg อ่อน 20%mix เท่า .tl-bar + fg เข้ม 55%mix กับ --text) worst-case ใหม่ light amber 4.50:1 /
     dark indigo 5.63:1 — ยกเว้น yellow ยังไม่ผ่านที่ 55% → override เฉพาะ yellow ด้านล่าง (45%) */
  background:color-mix(in srgb, var(--chip-c) 20%, var(--surface));
  color:color-mix(in srgb, var(--chip-c) 55%, var(--text));
}
/* S41 P0 (เอ audit §3.3) — yellow เฉพาะ ยังไม่ผ่าน 4.5:1 ที่ 55% (light 3.83:1) → ลดเป็น 45% mix
   (ตรงกับ pattern เดิมที่ .lb-chip[style*="--lb-yellow"] ใช้ 45% อยู่แล้ว) ผ่าน light 4.92:1 / dark 7.64:1
   ⚠️ .tl-bar-badge เองไม่มี style="--chip-c:..." inline (รับ --chip-c inherited จาก parent .tl-bar ที่มี
   inline style ตัวเดียว — ดู report_leave.html) → selector ต้องเช็ค attribute ที่ .tl-bar (ancestor)
   ไม่ใช่ที่ตัว badge เอง (ถ้าเช็คที่ตัวเองจะไม่ match ตลอดกาล เพราะ badge ไม่มี style attr) */
.tl-bar[style*="--lb-yellow"] .tl-bar-badge { color:color-mix(in srgb, var(--chip-c) 45%, var(--text)); }

/* F9: list-first จอแคบ — desktop โชว์ gantt, มือถือโชว์ list (สลับกันตาม breakpoint) */
.lvr-mlist { display:none; }
.lvr-mgroup {
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  padding:12px; margin-bottom:12px;
}
.lvr-mgroup-head { display:flex; align-items:center; gap:8px; font-weight:500; font-size:14px; margin-bottom:8px; }
.lvr-mrow { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:6px 0; font-size:var(--fs-sm); border-top:1px solid var(--border); }
.lvr-mrow:first-of-type { border-top:none; }
.lvr-mrow-date { color:var(--text-2); }
.lvr-mrow-form { color:var(--text-2); font-size:12px; margin-left:auto; }

/* upcoming box */
.lvr-up {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; margin-top:24px; display:flex; flex-direction:column; gap:12px;
  box-shadow:var(--shadow-card);
}
.lvr-up-head { display:flex; align-items:center; gap:8px; }
.lvr-up-head h2 { font-size:16px; font-weight:600; }
.lvr-up-head > i.fi {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px;
}
.lvr-up-list { display:flex; flex-direction:column; gap:8px; }
.lvr-up-row {
  display:flex; align-items:center; gap:10px; padding:10px 12px; flex-wrap:wrap;
  border:1px solid var(--border); border-radius:12px; font-size:14px;
}
.lvr-up-name { font-weight:500; min-width:0; overflow-wrap:anywhere; }
.lvr-up-date { color:var(--text-2); font-size:var(--fs-sm); }
.lvr-up-form { color:var(--text-2); font-size:12px; }
.lvr-up-md { margin-left:auto; font-weight:600; color:var(--primary-solid); font-size:var(--fs-sm); }

/* F9 + F10: จอแคบ — สลับ gantt→list · mandays ไม่หลุดแนวขวา (ลงบรรทัดตัวเอง ชิดขวา) */
@media (max-width:768px) {
  .lvr-gantt { display:none; }
  .lvr-mlist { display:block; }
  .lvr-up-md { margin-left:0; width:100%; text-align:right; }
}

/* ---- Calendar view (month/week — DEC-039 style read-only positioning, Task 3/4, F-review fix) ----
   DOM (templates/views.html #calendar_view):
   .cal-v-wrap > .cal-v-toolbar(.cal-v-nav/.cal-v-navbtn/.cal-v-title, .cal-v-toggle/.cal-v-togbtn)
              > .cal-v-headrow(.cal-v-hd x7)
              > .cal-v-grid > .cal-v-week > .cal-v-daygrid[--lanes](--week เมื่อ CalSpan=="week")
                                              > .cal-v-cell[--we/--hol/--today/--out](.cal-v-daynum + .cal-v-more
                                                + <template class="cal-v-pop-src"> ซ่อน — Task 5 calendar.js)
                                              + .cal-v-bar[--chip-c] > .cal-v-bar-t
   reuse pattern จาก .tl-grid/.tl-cell/.tl-bar (chip-c + color-mix, cell grid-row:1/-1 กินเต็มความสูง) ให้ shading
   ครอบเต็มคอลัมน์วันทั้งสัปดาห์ (แถบเลขวัน + ชั้น bar อยู่ grid เดียวกัน ไม่ใช่ sibling คนละ block เหมือนก่อน)
   Task 5: คลิก .cal-v-more → calendar.js เปิด .cal-v-pop (desktop, ท้ายไฟล์นี้) หรือ .sheet (มือถือ, shared.js) */
.cal-v-wrap {
  display:flex; flex-direction:column; gap:16px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; box-shadow:var(--shadow-card);
}

/* toolbar: prev/next + title (ซ้าย) · toggle เดือน/สัปดาห์ (ขวา) */
.cal-v-toolbar { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px; }
.cal-v-nav { display:flex; align-items:center; gap:8px; }
.cal-v-navbtn {
  width:28px; height:28px; flex:0 0 28px; aspect-ratio:1/1;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:8px; color:var(--text-2); text-decoration:none; font-size:14px;
  transition:background .15s, color .15s;
}
.cal-v-navbtn:hover { background:var(--surface-2); color:var(--primary-solid); }
.cal-v-title { min-width:120px; text-align:center; font-size:16px; font-weight:600; overflow-wrap:anywhere; } /* DS §0.6 */
.cal-v-toggle { display:inline-flex; align-items:center; gap:2px; background:var(--surface-2); border-radius:10px; padding:4px; }
/* padding/font ตรงกับ .view-tab เดิม (consistency H4 — F-review#3) */
.cal-v-togbtn {
  padding:8px 14px; border-radius:8px; font-size:var(--fs-sm); font-weight:500;
  color:var(--text-2); text-decoration:none; transition:background .15s, color .15s;
}
.cal-v-togbtn:hover { color:var(--text); }
.cal-v-togbtn--on { background:var(--surface); color:var(--primary-solid); box-shadow:var(--shadow-tab); }

/* หัวคอลัมน์ จ-อา (7 คอลัมน์เท่ากัน ตรงกับ daygrid ด้านล่าง) */
.cal-v-headrow { display:grid; grid-template-columns:repeat(7,1fr); }
.cal-v-hd { text-align:center; font-size:11px; font-weight:500; color:var(--text-2); padding-bottom:6px; }

/* กรอบตารางเดือน/สัปดาห์ */
.cal-v-grid { border:1px solid var(--border); border-radius:12px; overflow:hidden; }
.cal-v-week { border-bottom:1px solid var(--border); }
.cal-v-week:last-child { border-bottom:none; }

/* grid เดียวต่อสัปดาห์ — แถวเลขวัน(24px) + N เลน bar (--lanes) ต่อกัน — cell คลุมทุกแถว (grid-row:1/-1 ใน markup)
   max(1,var(--lanes,1)) กัน repeat(0,...) (ค่าว่างไม่ error เวลาไม่มี bar ในสัปดาห์นั้น) */
.cal-v-daygrid {
  display:grid; position:relative;
  grid-template-columns:repeat(7,1fr);
  grid-template-rows:24px repeat(max(1,var(--lanes,1)), 22px);
}
/* week view: มีแค่ 1 แถวสัปดาห์ (จอเหลือพื้นที่เยอะกว่า) — ยกแถวเลขวัน + เลน bar ให้สูงขึ้น
   อ่านชื่องานสบายตาขึ้นก่อนจะโดนพับเป็น "+N more" (calMaxVisible เดิม ยังคุมจำนวนเท่ากันทั้งสอง span) */
.cal-v-daygrid--week {
  grid-template-rows:32px repeat(max(1,var(--lanes,1)), 28px);
}
.cal-v-cell {
  padding:4px 6px; display:flex; align-items:flex-start; gap:4px;
  border-right:1px solid var(--border);
}
.cal-v-cell:nth-child(7) { border-right:none; } /* คอลัมน์วันมีเสมอ 7 ช่องแรกของ daygrid (มาก่อน bar ใน DOM) */
.cal-v-daynum { font-size:12px; color:var(--text-2); }
/* shading ตามสถานะวัน (reuse .tl-cell--we/--hol/--today แนวเดียวกัน) — cell กินเต็มความสูง grid-row:1/-1
   จึงครอบทั้งแถบเลขวัน + ชั้น bar ด้านล่างในคอลัมน์เดียวกันทั้งหมด (F-review#1) */
.cal-v-cell--we { background:var(--surface-2); }               /* เสาร์-อาทิตย์ */
.cal-v-cell--hol { background:color-mix(in srgb, var(--primary-solid) 14%, var(--surface-2)); } /* วันหยุด เข้มกว่าเสาร์-อาทิตย์ (F10) */
/* Task8-fix (เต๋ J1): ต้องเป็น .cal-v-cell.cal-v-cell--today (spec 0,2,0) ให้เท่า .cal-v-cell:nth-child(7)
   (0,2,0) — ไม่งั้นวันนี้ตรงวันอาทิตย์ (คอลัมน์ 7) โดน border-right:none ชนะ ring ขาดขอบขวา; เท่ากันแล้ว
   source-order ตัดสิน (today มาทีหลัง ชนะ) */
.cal-v-cell.cal-v-cell--today { border:2px solid var(--primary-solid); }   /* วันนี้ — เห็นทั้งคอลัมน์ ไม่ใช่แค่เลขวัน */
.cal-v-cell--today .cal-v-daynum {
  display:inline-flex; align-items:center; justify-content:center;
  width:20px; height:20px; border-radius:999px;
  background:var(--primary-solid); color:#fff; font-weight:600;
}
.cal-v-cell--out { opacity:.45; }                                /* วันนอกเดือน (month span) */
/* re-review#2: cell ปลายทางระหว่างลาก/resize (live feedback — วางหลัง --we/--hol ให้ทับ shading เดิม) */
.cal-v-cell--dragover { background:color-mix(in srgb, var(--primary-solid) 12%, var(--surface-2)); }
/* "+N more" (overflow เกิน calMaxVisible ต่อวัน) — สูง ≥24px ตาม WCAG 2.5.8 (F-review#2) */
.cal-v-more {
  margin-left:auto; flex-shrink:0; border:none; border-radius:999px;
  min-height:24px; padding:4px 10px; font-size:11px; font-weight:500;
  display:inline-flex; align-items:center;
  background:var(--surface-2); color:var(--text-2);
  transition:background .15s, color .15s;
}
.cal-v-more:hover { background:var(--primary-solid); color:#fff; }

/* span bar — วางทับบน cell ด้วย grid-column/grid-row (ColStart/ColEnd, Lane+1) เหมือน .tl-bar/.tl-cell */
.cal-v-bar {
  display:flex; align-items:center; height:20px; align-self:center;
  margin:0 2px; padding:0 8px;
  border-radius:8px; font-size:11px; font-weight:500; text-decoration:none;
  overflow:hidden; z-index:1; position:relative;
  background:color-mix(in srgb, var(--chip-c) 20%, var(--surface));
  color:color-mix(in srgb, var(--chip-c) 55%, var(--text));       /* ตรงกับ .tl-bar/.lb-chip (ผ่าน AA ทั้ง light+dark) */
  /* S41 (เอ audit §2) — เดิม mix กับ --surface (40%) fail 1.4.11 ทุกสีทุก theme → mix กับ --text แทน
     (สูตรเดียวกับ .tl-bar ด้านบน) worst-case ใหม่ light yellow 4.23:1 / dark indigo 6.82:1 */
  border:1px solid color-mix(in srgb, var(--chip-c) 55%, var(--text));
}
.cal-v-bar:hover { border-color:var(--chip-c); z-index:2; }
.cal-v-bar-t { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Task 6: drag & resize (calendar.js) — เฉพาะแถบที่มี draggable="true"/.cal-v-rz (server render ตาม CanEdit) */
.cal-v-bar[draggable="true"] { cursor:grab; }
.cal-v-bar--drag { opacity:.5; cursor:grabbing; } /* re-review#4: cursor ระหว่างลาก (mirror .tbl-th:active) */
/* re-review#3: even-pixel 8px · #5: grip จาง ๆ ถาวร (affordance ให้รู้ว่า span ยืด/หดได้ ไม่ต้องรอ hover)
   เข้มขึ้นตอน hover — overflow:hidden ของ .cal-v-bar clip ให้เป็นมุมโค้ง ไม่รก */
.cal-v-rz {
  position:absolute; top:0; bottom:0; width:8px; cursor:ew-resize; z-index:2;
  background:color-mix(in srgb, var(--chip-c) 16%, transparent); transition:background .12s;
}
.cal-v-rz--l { left:0; }
.cal-v-rz--r { right:0; }
.cal-v-rz:hover { background:color-mix(in srgb, var(--chip-c) 45%, transparent); }

/* ---- Task 7: agenda list (มือถือ <768px) — DS §6.3 list-first จอแคบ (mirror .lvr-mlist)
   default ซ่อน (desktop โชว์ .cal-v-grid); media <768px สลับ grid+headrow → agenda
   ไม่มี drag บนมือถือ (tap แถว → /tasks/{id}) */
.cal-v-agenda { display:none; flex-direction:column; gap:10px; }
.cal-v-ag-day {
  background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:12px;
}
.cal-v-ag-day--today { border-color:var(--primary-solid); }
.cal-v-ag-head { display:flex; align-items:baseline; gap:6px; margin-bottom:8px; }
.cal-v-ag-dow { font-size:var(--fs-sm); font-weight:600; color:var(--text); }
.cal-v-ag-num { font-size:var(--fs-md); font-weight:700; color:var(--text); }
.cal-v-ag-day--today .cal-v-ag-num {
  display:inline-flex; align-items:center; justify-content:center; min-width:22px; height:22px;
  border-radius:999px; background:var(--primary-solid); color:#fff; font-size:var(--fs-sm); font-weight:600;
}
.cal-v-ag-mon { font-size:12px; color:var(--text-2); }
.cal-v-ag-tag {
  margin-left:auto; font-size:11px; font-weight:500; color:var(--primary-solid);
  background:color-mix(in srgb, var(--primary-solid) 12%, var(--surface)); padding:2px 8px; border-radius:999px;
}
.cal-v-ag-tag--we { color:var(--text-2); background:var(--surface-2); }
.cal-v-ag-row {
  display:flex; align-items:center; gap:10px; min-height:44px; padding:8px 4px; /* touch target ≥44px (DS) */
  border-top:1px solid var(--border); text-decoration:none; color:var(--text); font-size:14px;
}
.cal-v-ag-head + .cal-v-ag-row { border-top:none; }
.cal-v-ag-t { flex:1; min-width:0; overflow-wrap:anywhere; } /* ไทยตัดบรรทัดไม่ได้ (DS §0.6) */
.cal-v-ag-range { flex-shrink:0; font-size:12px; color:var(--text-2); }

/* จอแคบ (DS §6.3 list-first): ซ่อนตารางปฏิทิน + หัวคอลัมน์ → โชว์ agenda list (mirror .lvr-mlist)
   toolbar (prev/next + toggle เดือน/สัปดาห์) ยังอยู่ — ใช้เลือกช่วงของ agenda ได้ */
@media (max-width:768px) {
  .cal-v-wrap { padding:12px; gap:12px; }
  .cal-v-title { font-size:14px; min-width:0; }
  .cal-v-grid, .cal-v-headrow { display:none; }
  .cal-v-agenda { display:flex; }
}

/* ---- "+N more" popover (desktop) — Task 5 (calendar.js)
   ข้อมูลมาจาก <template class="cal-v-pop-src"> ที่ views.html render ซ่อนไว้ต่อวัน; JS clone มาใส่ที่นี่
   pattern เดียวกับ .fb-menu (filter-builder.js) — position:fixed ลอยใกล้ปุ่ม, z สูงกว่า sheet (4000) เพื่อ
   ไม่ให้กริดปฏิทิน/bar ทับ, dismiss คลิกนอก/scroll/Esc (ผูกใน calendar.js ไม่ใช่ CSS) */
.cal-v-pop {
  position:fixed; z-index:4600; width:280px; max-width:calc(100vw - 16px); max-height:360px; overflow-y:auto;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:0 8px 28px rgba(0,0,0,.18); padding:6px; display:flex; flex-direction:column; gap:2px;
}
.cal-v-pop-head { font-size:12px; font-weight:600; color:var(--text-2); padding:6px 8px 4px; }
/* .cal-v-pop-item ใช้ทั้งใน .cal-v-pop (desktop) และ .sheet-body (มือถือ, reuse sheetOpen จาก shared.js) */
.cal-v-pop-item {
  display:flex; align-items:center; gap:8px; min-height:40px; padding:6px 8px; border-radius:8px;
  text-decoration:none; color:var(--text); font-size:var(--fs-sm); overflow-wrap:anywhere;
}
.cal-v-pop-item:hover { background:var(--surface-2); }
.cal-v-pop-t { flex:1; min-width:0; overflow-wrap:anywhere; }
.cal-v-pop-a { display:inline-flex; align-items:center; gap:4px; font-size:11px; color:var(--text-2); flex-shrink:0; }
.cal-v-pop-a > i.fi { font-size:11px; }
@media (max-width:768px) {
  .cal-v-pop { display:none; } /* มือถือใช้ .sheet-backdrop/.sheet (bottom sheet) แทน — calendar.js เลือกทางตาม innerWidth */
}

/* ============================================================ Cross-View (มุมมองรวม /everything) — S21 */
/* drawer เมนู "มุมมองรวม" — reuse .dnav; badge ชิดขวา (icon + label ซ้าย) */
.cv-nav .cv-badge { margin-left:auto; }

/* page-bar title icon */
.page-bar-title .pb-ic { display:inline-flex; align-items:center; color:var(--primary-solid); margin-right:6px; }
.page-bar-title .pb-ic i.fi { font-size:16px; }

/* control bar (cross): เลือกตำแหน่ง + date chip */
.cv-controls { flex:1 1 240px; }
.cv-due-chip { text-decoration:none; }

/* ===== A5 location selector ===== */
/* custom checkbox indeterminate (บาง list ในสเปซถูกเลือก) — DS §4 (ต่อจาก .cbx-box เดิม) */
.cbx-box--ind { background:var(--surface-2); border-color:var(--primary-light); position:relative; }
.cbx-box--ind::after { content:''; width:10px; height:2px; border-radius:2px; background:var(--primary-solid); }

/* count-pill บนปุ่มเลือกตำแหน่ง (จำนวนการ์ดปัจจุบัน) */
.count-pill { font-size:11px; font-weight:600; color:var(--primary-solid); background:color-mix(in srgb, var(--primary-solid) 10%, transparent); border-radius:999px; padding:2px 8px; margin-left:2px; }

/* anchored panel (desktop) — ใช้ .cv-locpanel แทน .panel เดิม (form) กันชนกัน */
.cv-loc-wrap { position:relative; display:inline-flex; }
.cv-locpanel {
  position:absolute; z-index:60; left:0; top:calc(100% + 6px);
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  box-shadow:var(--shadow-overlay); width:320px; overflow:hidden;
  display:flex; flex-direction:column;
}
.cvp-head { display:flex; align-items:center; gap:8px; padding:12px 14px; border-bottom:1px solid var(--border); }
.cvp-head h3 { margin:0; font-size:var(--fs-sm); font-weight:600; }
.cvp-muted { font-size:11px; color:var(--text-2); }
.cvp-head .cvp-muted { margin-left:auto; }
/* Bug A fix: panel = abspos + overflow:hidden + box-shadow ครอบ scroller นี้ → กดเลือกหลาย list
   = mass style-invalidate → Chrome compositor (GPU) ทิ้ง layer ไม่ paint = ขาวเปล่า.
   translateZ(0) = promote scroller เป็น composited layer ของตัวเอง → repaint independent กัน blank */
.cvp-body { max-height:340px; overflow-y:auto; padding:6px 8px 8px; transform:translateZ(0); }
.cvp-foot { display:flex; align-items:center; gap:8px; padding:10px 14px; border-top:1px solid var(--border); }
.cvp-foot .cvp-muted { margin-right:auto; }
/* mobile: panel content อยู่ใน bottom-sheet body (shared.js) — เต็มความกว้าง sheet */
.sheet-body .cvp-body { max-height:none; }

/* location tree row */
.loc-node { }
.loc-row { display:flex; align-items:center; gap:8px; padding:8px; border-radius:8px; cursor:pointer; }
.loc-row:hover { background:var(--surface-2); }
/* [N4] caret = <button> (โฟกัส/คีย์บอร์ด Enter/Space ได้) — reset native button appearance */
.loc-caret { width:18px; height:18px; flex-shrink:0; display:flex; align-items:center; justify-content:center; color:var(--text-2); font-size:12px; cursor:pointer; background:none; border:none; padding:0; border-radius:6px; }
.loc-caret:focus-visible { outline:none; box-shadow:0 0 0 2px color-mix(in srgb, var(--primary-solid) 35%, transparent); }
.loc-caret--ghost { visibility:hidden; }
.loc-ic { width:20px; height:20px; flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:var(--fs-sm); color:var(--text-2); }
.loc-row--space .loc-ic { color:var(--primary-solid); }
.loc-name { flex:1; min-width:0; font-size:var(--fs-sm); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; overflow-wrap:anywhere; }
.loc-row--space .loc-name { font-weight:600; }
.loc-cnt { font-size:11px; color:var(--text-2); background:var(--surface-2); border-radius:999px; padding:2px 8px; flex-shrink:0; }
.loc-children { padding-left:16px; }
@media (max-width:900px){ .cv-locpanel { width:min(92vw,320px); } }

/* ---- saved-view control (C2) — ปุ่ม "บันทึกมุมมอง ▾" + anchored dropdown ---- */
.cv-sv-wrap { position:relative; display:inline-flex; margin-left:auto; } /* ชิดขวาใน cv-controls */
.cv-sv-btn > i.fi:last-child { color:var(--text-2); }
/* dropdown (partial #sv-menu โหลดสด) — anchored ใต้ปุ่ม, ชิดขวา */
.cv-sv-menu {
  position:absolute; z-index:60; right:0; top:calc(100% + 6px);
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  box-shadow:var(--shadow-overlay); width:280px; overflow:hidden;
  padding:6px; display:flex; flex-direction:column;
}
.cv-sv-item { display:flex; align-items:center; gap:2px; border-radius:10px; }
.cv-sv-item:hover { background:var(--surface-2); }
.cv-sv-item--on { background:var(--surface-2); }
.cv-sv-load {
  flex:1; min-width:0; display:flex; align-items:center; gap:8px;
  padding:9px 10px; color:var(--text); text-decoration:none; font-size:var(--fs-sm);
}
.cv-sv-load > i.fi:first-child { color:var(--text-2); flex-shrink:0; }
.cv-sv-item--on .cv-sv-load > i.fi:first-child { color:var(--primary-solid); }
.cv-sv-name { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cv-sv-chk { color:var(--primary-solid); flex-shrink:0; }
.cv-sv-del { flex-shrink:0; margin-right:4px; opacity:0; transition:opacity .12s; }
.cv-sv-item:hover .cv-sv-del { opacity:1; }
/* [B2] hover-only ปุ่มลบ = touch/keyboard เข้าไม่ถึง → มือถือโชว์เสมอ + focus โชว์ */
.cv-sv-del:focus-visible { opacity:1; }
@media (max-width:900px){ .cv-sv-del { opacity:1; } }
.cv-sv-empty { padding:14px 10px; text-align:center; font-size:12px; color:var(--text-2); }
.cv-sv-menu .dd-sep { height:1px; background:var(--border); margin:6px 4px; }
.cv-sv-save {
  display:flex; align-items:center; gap:8px; width:100%;
  padding:9px 10px; border:none; background:transparent; border-radius:10px;
  color:var(--primary-solid); font-size:var(--fs-sm); font-weight:500; cursor:pointer; text-align:left;
}
.cv-sv-save:hover { background:var(--surface-2); }
/* save sheet — ช่องกรอกชื่อมุมมอง (custom sheet, ❌ ไม่มี native prompt) */
.cv-save-form { display:flex; flex-direction:column; gap:10px; padding:4px 2px; }
.cv-save-lab { font-size:12px; font-weight:600; color:var(--text-2); }
.cv-save-hint { font-size:11px; color:var(--text-2); }
.cv-save-form .btn-primary { align-self:flex-start; }
/* mobile: dropdown เปิดเป็น bottom-sheet body → เต็มกว้าง */
.sheet-body .cv-sv-menu { position:static; width:auto; box-shadow:none; border:none; }
@media (max-width:900px){ .cv-sv-menu { width:min(92vw,300px); } }

/* empty state (ยังไม่เลือก location) — จัดกลาง, ไม่ใช้แถบซ้าย (DS #9) */
.cv-empty { flex-direction:column; gap:10px; padding:56px 24px; text-align:center; }
.cv-empty-ic { display:inline-flex; align-items:center; justify-content:center; width:56px; height:56px; border-radius:999px; background:var(--surface-2); color:var(--primary-solid); }
.cv-empty-ic i.fi { font-size:24px; }
.cv-empty-title { font-size:16px; font-weight:600; color:var(--text); }
.cv-empty-sub { font-size:var(--fs-sm); color:var(--text-2); max-width:420px; overflow-wrap:anywhere; }

/* [§9] empty-state ในตาราง/มือถือ (cross) — จัดกลาง icon+ข้อความ+ปุ่มล้าง (ไม่ใช้ left strip DS #9) */
/* S32 polish: consistent empty-state — ใช้ icon circle + typography ชุดเดียวกับ .cv-empty (rich variant)
   ให้ empty-state แบบ "จัดกลางมี icon" หน้าตาเหมือนกันทุกหน้า (icon circle 56px + title 16/600 + sub muted) */
.cv-tbl-empty { display:flex; flex-direction:column; align-items:center; gap:10px; padding:56px 24px; text-align:center; color:var(--text-2); }
.cv-tbl-empty > i.fi { display:inline-flex; align-items:center; justify-content:center;
  width:56px; height:56px; border-radius:999px; background:var(--surface-2); color:var(--primary-solid); font-size:24px; }
.cv-tbl-empty-t { font-size:16px; font-weight:600; color:var(--text); }

/* result-cap banner (safety net 1000 — §7) — warm attention, border 1px รอบ (ไม่ใช้ left strip) */
.cap-banner {
  display:flex; align-items:center; gap:8px; margin-bottom:8px; padding:10px 14px;
  border:1px solid var(--warm); border-radius:10px; background:var(--surface);
  color:var(--text); font-size:var(--fs-sm); overflow-wrap:anywhere;
}
.cap-banner i.fi { color:var(--warm); font-size:16px; flex-shrink:0; }

/* ---------- Work Console (ศูนย์ควบคุมงาน) — S22 · rename S30 (DEC-075) ---------- */
.risk-filter { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:16px; }
/* loading cue ตอนสลับ chip (htmx swap) — จางลงระหว่างรอ ไม่ให้ผู้ใช้กดซ้ำเพราะคิดว่าไม่ตอบสนอง */
#risk_body.htmx-request { opacity:.55; transition:opacity .12s ease; }
/* บรรทัดบอกว่าโดน cap (แสดง 200 จาก 433) — ไม่ใช่ alert แค่ note */
.rf-capnote { display:flex; align-items:center; gap:8px; margin-top:16px; padding:8px 12px;
  background:var(--surface-2); border-radius:10px;
  font-size:12px; color:var(--text-2); overflow-wrap:anywhere; }
.rf-capnote i.fi { font-size:12px; flex-shrink:0; }
.rf-chip {
  display:inline-flex; align-items:center; gap:6px; padding:8px 14px; border-radius:999px;
  background:var(--surface); border:1px solid var(--border); color:var(--text-2);
  font-family:inherit; font-size:var(--fs-sm); font-weight:500; cursor:pointer; transition:background .12s, color .12s;
}
.rf-chip:hover { background:var(--surface-2); color:var(--text); }
/* S27 Batch2 (B — contrast): chip "on" มีตัวอักษร/count ทับพื้นแดง → --primary-aa solid (เหมือน .btn-primary/.cbx checked) */
.rf-chip--on { background:var(--primary-aa); border-color:transparent; color:#fff; }
.rf-chip i.fi { font-size:var(--fs-sm); }
.rf-count {
  display:inline-flex; align-items:center; justify-content:center; min-width:20px; height:20px;
  padding:0 6px; border-radius:999px; background:var(--surface-2); color:var(--text-2);
  font-size:11px; font-weight:600;
}
.rf-chip--on .rf-count { background:rgba(255,255,255,.24); color:#fff; }

.risk-row { cursor:pointer; transition:background .12s; }
.risk-row:hover { background:var(--surface-2); }
.risk-row:focus-visible { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.risk-badges { display:flex; flex-wrap:wrap; gap:6px; margin:6px 0; }

.rk-detail { display:flex; flex-direction:column; gap:8px; padding:14px 0; border-top:1px solid var(--border); }
.rk-row { display:flex; justify-content:space-between; gap:16px; font-size:14px; }
.rk-k { color:var(--text-2); flex-shrink:0; }
.rk-v { color:var(--text); text-align:right; overflow-wrap:anywhere; min-width:0; }
.rk-over { color:var(--alert-text); font-weight:600; } /* S27 P0: alert-text (AA) */
.rk-cta { border-top:1px solid var(--border); padding-top:16px; }
.rk-cta-title { font-size:12px; font-weight:600; color:var(--text-2); text-transform:uppercase; letter-spacing:.04em; margin-bottom:10px; }
.rk-resched { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.rk-resched .dd { flex:1; min-width:180px; }

/* ============================================================
   Release (รุ่น/เวอร์ชัน) — S25 (DEC-067) — Wegital DS
   badge สื่อสถานะ (ไม่ใช้ left-accent) · even-pixel · light+dark ผ่าน CSS var
   ============================================================ */
.rel-sec { margin-bottom:24px; }
.rel-sec-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px; }
.rel-sec-title { display:inline-flex; align-items:center; gap:8px; font-size:14px; font-weight:600; color:var(--text); }
.rel-new-btn { flex:none; }
.rel-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(260px, 1fr)); gap:12px; }
.rel-card {
  display:block; text-decoration:none; background:var(--surface); border:1px solid var(--border);
  border-radius:12px; padding:16px; transition:border-color .12s, box-shadow .12s;
}
/* hover shadow = light only (DS: card shadow light-only) */
.rel-card:hover { border-color:var(--primary-solid); box-shadow:0 2px 12px rgba(0,0,0,.06); }
[data-theme="dark"] .rel-card:hover { box-shadow:none; }
.rel-card-top { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
.rel-card-name { font-size:16px; font-weight:600; color:var(--text); overflow-wrap:anywhere; }
.rel-card-badges { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:8px; }
.rel-card-meta { font-size:12px; color:var(--text-2); display:flex; align-items:center; gap:6px; margin-bottom:8px; }
.rel-progress { display:flex; align-items:center; gap:8px; }
.rel-progress-bar { flex:1; height:6px; background:var(--surface-2); border-radius:999px; overflow:hidden; }
.rel-progress-bar > span { display:block; height:100%; background:var(--secondary-chart); border-radius:999px; } /* S41: --secondary-chart (audit §1 — dark contrast fix) */
.rel-progress-txt { font-size:12px; font-weight:600; color:var(--text-2); flex:none; }
.rel-empty { font-size:var(--fs-sm); color:var(--text-2); padding:12px 2px; }

/* overdue badge — text-safe (--alert-text ผ่าน AA · --alert ตัวแม่ fail 3.08:1 เมื่อเป็นตัวอักษร) */
.rel-badge--overdue { color:var(--alert-text); background:var(--alert-bg); }

/* detail */
.rel-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.rel-meta-row { display:flex; align-items:center; gap:20px; flex-wrap:wrap; margin-bottom:16px; }
.rel-meta { font-size:var(--fs-sm); color:var(--text-2); display:inline-flex; align-items:center; gap:6px; }
.rel-meta b { color:var(--text); font-weight:600; }
.rel-note { margin-bottom:16px; }
.rel-note-body { margin-top:8px; font-size:14px; color:var(--text); white-space:pre-wrap; overflow-wrap:anywhere; }
.rel-changelog .panel-head { display:flex; align-items:center; justify-content:space-between; }
.rel-cl-list { margin-top:12px; display:flex; flex-direction:column; gap:6px; }
.rel-cl-item {
  display:flex; align-items:center; gap:10px; text-decoration:none; padding:10px 12px; min-height:44px;
  border:1px solid var(--border); border-radius:8px; background:var(--surface); transition:background .12s;
}
.rel-cl-item:hover { background:var(--surface-2); }
.rel-cl-title { flex:1; font-size:14px; color:var(--text); overflow-wrap:anywhere; }

/* rel-chip — badge release บนการ์ด (list/board/table) — C1 */
.rel-chip {
  display:inline-flex; align-items:center; gap:4px; font-size:11px; font-weight:500;
  border-radius:999px; padding:2px 8px; color:var(--secondary-text);
  background:color-mix(in srgb, var(--secondary-end) 12%, transparent); max-width:160px; overflow:hidden;
}
.rel-chip > span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* =========================================================================
   Team Monitoring (แท็บ 5, S30) — Member Swimlane Matrix
   ทุกสี = CSS var (DS §0.1) · สี=ตัวอักษร ใช้ -text variant (DS §1.1) · dual-encode
   ========================================================================= */
.tm-filter { align-items:center; gap:16px; }
.tm-weeknav { display:flex; align-items:center; gap:8px; }
.tm-weeklabel { font-size:13px; font-weight:500; min-width:150px; text-align:center; overflow-wrap:anywhere; }
.tm-now { color:var(--warm-text); font-weight:600; }

.tbl--team { min-width:1040px; }
.tbl--team .tbl-th, .tbl--team .tbl-td { vertical-align:top; }   /* top-align ทุก cell (DS §6.4) */
.tm-th-member, .tm-td-member { position:sticky; left:0; z-index:2;
  background:var(--surface); min-width:184px; box-shadow:1px 0 0 var(--border); }
.tbl--team thead .tm-th-member { z-index:4; }

.tm-member { display:flex; flex-direction:column; gap:6px; }
.tm-member-id { display:flex; align-items:center; gap:8px; overflow-wrap:anywhere; min-width:0; }
.tm-active { font-size:11px; color:var(--text-2); }

.tm-cell { min-width:220px; }
.tm-cell > * + * { margin-top:8px; }

/* การ์ดงานกระชับ — reuse หลักการ .hp-item ย่อ */
.tm-task { display:flex; flex-direction:column; gap:4px; padding:8px 10px;
  border:1px solid var(--border); border-radius:10px; background:var(--surface);
  text-decoration:none; color:inherit; min-width:0; }
.tm-task:hover .tm-task-title { color:var(--primary-solid); }
.tm-task:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.tm-task-title { font-size:13px; font-weight:500; overflow-wrap:anywhere; word-break:break-word;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.tm-task-meta { display:flex; align-items:center; flex-wrap:wrap; gap:6px; }
.tm-md { display:inline-flex; align-items:center; gap:4px; font-size:11px; color:var(--text-2); }
.tm-md i.fi { font-size:11px; }

/* flag ในการ์ด (dual-encode สี+icon+label) — สี=ตัวอักษร ใช้ -text variant (DS §1.1) */
.tm-flag { display:inline-flex; align-items:center; gap:4px; font-size:11px; border-radius:999px; padding:2px 8px; }
.tm-flag i.fi { font-size:11px; }
.tm-flag--red  { background:var(--alert-bg); color:var(--alert-text); }
.tm-flag--warn { background:var(--warn-bg,color-mix(in srgb,var(--warm) 14%,transparent)); color:var(--warm-text); }
.tm-flag--gray { background:var(--surface-2); color:var(--text-2); }
.tm-flag--done { background:color-mix(in srgb,var(--green) 14%,transparent); color:var(--green-text); }
.tm-empty { color:var(--text-2); font-size:12px; padding:6px 0; }

/* ส่วนเกิน "ดูเพิ่ม" inline (details) */
.tm-more { margin-top:8px; }
.tm-more > summary { cursor:pointer; font-size:12px; color:var(--primary-aa); list-style:none;
  min-height:32px; display:flex; align-items:center; }
.tm-more > summary::-webkit-details-marker { display:none; }
.tm-more[open] > summary { margin-bottom:8px; }
.tm-more > * + * { margin-top:8px; }
.tm-more > summary:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-radius:6px; }

/* load signal (คอลัมน์คน) — dual-encode · ❌ ไม่ใช้แดง (แดง=overdue เท่านั้น) */
.tm-load { display:inline-flex; align-items:center; gap:4px; align-self:flex-start;
  font-size:11px; font-weight:500; border-radius:999px; padding:2px 8px; }
.tm-load i.fi { font-size:12px; }
.tm-load--over  { background:var(--warn-bg,color-mix(in srgb,var(--warm) 14%,transparent)); color:var(--warm-text); }
.tm-load--idle  { background:color-mix(in srgb,var(--cold) 15%,transparent); color:var(--cold-text); }
.tm-load--ok    { background:var(--surface-2); color:var(--text-2); }
.tm-load--carry { background:var(--warn-bg,color-mix(in srgb,var(--warm) 14%,transparent)); color:var(--warm-text); }

/* mobile: การ์ดต่อคน (ux §9.1) — desktop table ซ่อน mobile (reuse .tbl-mobile/.rpt-mobile เดิม) */
.tm-mcard { display:flex; flex-direction:column; gap:10px; }
.tm-mrow { display:flex; flex-direction:column; gap:8px; }
.tm-msec > summary { cursor:pointer; font-weight:500; font-size:13px; min-height:44px;
  display:flex; align-items:center; gap:8px; list-style:none; }
.tm-msec > summary::-webkit-details-marker { display:none; }
.tm-msec > summary:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-radius:6px; }
.tm-mcount { font-size:11px; color:var(--text-2); background:var(--surface-2); border-radius:999px; padding:1px 8px; }
.tm-msec[open] .tm-cell, .tm-msec[open] > * { margin-top:8px; }

/* ── Artifact gallery (S34) — หน้ารวม presentation/เอกสาร ── */
.art-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(320px, 1fr)); gap:16px; }
.art-card { display:flex; flex-direction:column; gap:12px; padding:20px; }
.art-card-ic { width:44px; height:44px; border-radius:12px; display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.art-card-ic i.fi { font-size:20px; color:var(--primary-solid); }
.art-card-body { flex:1 1 auto; display:flex; flex-direction:column; gap:6px; }
.art-card-title { font-size:var(--fs-md); font-weight:600; margin:0; overflow-wrap:anywhere; }
.art-card-desc { font-size:var(--fs-sm); color:var(--text-2); margin:0; line-height:1.5; overflow-wrap:anywhere; }
.art-card-date { display:flex; align-items:center; gap:6px; font-size:var(--fs-sm); color:var(--text-2); margin-top:2px; }
.art-card-date i.fi { font-size:14px; }
.art-card-open { align-self:flex-start; text-decoration:none; }
.art-empty { display:flex; flex-direction:column; align-items:center; gap:8px; padding:48px 16px; color:var(--text-2); }
.art-empty i.fi { font-size:32px; }

/* ===== V1 per-view manual order (S35 DEC-077): drag handle Table/List ===== */
/* ลากจัดลำดับเอง — แสดงเฉพาะ admin + โหมด manual (template gate) · สี/เส้นจาก token (light+dark) */
/* F2 (WCAG 2.1.1/2.5.7): handle = <button> → drag (เมาส์) + เปิดเมนูเลื่อนขึ้น/ลง (keyboard/touch) จากตัวเดียว
   ปุ่ม reset (พื้น/ขอบ/padding=0) ให้หน้าตาเหมือน handle เดิม · draggable อยู่บนปุ่มนี้เท่านั้น (ไม่ใช่ทั้งแถว)
   → คง text-selection ในเซลล์อื่น (F2-N1) เพราะแถวไม่ draggable แล้ว */
.dragh {
  display:inline-flex; align-items:center; justify-content:center;
  width:24px; height:24px; font-size:13px; flex:none;
  padding:0; margin:0; border:0; background:transparent; border-radius:6px;
  color:var(--text-3); cursor:grab; user-select:none;
}
.dragh:hover { color:var(--text); background:var(--surface-2); }
[data-reorder-row].vo-row--drag { opacity:.5; }
/* fix (พีบ่น scratch scroll): เดิม set แค่ width:28px — base .tbl-td{min-width:112px} (specificity เท่ากัน
   ประกาศทีหลังไม่ได้ชนะเพราะคนละ property) เลยชนะ ทำให้คอลัมน์ dragh เรนเดอร์จริง ~112px ทั้ง 2 ตาราง
   (scratch + Task Table view) กินที่ว่างเกินจำเป็น ยิ่งดันตารางกว้างเกิน ต้อง scroll มากกว่าที่ควร →
   ล็อก min/max-width:28px คู่กับ width กันฐาน .tbl-td override */
.tbl-th--dragh, .tbl-td--dragh { width:28px; min-width:28px; max-width:28px; text-align:center; padding-left:4px; padding-right:0; }
/* S53: ลากจัดลำดับการ์ดร่าง (Planning Board / กระดาษทด) — reuse .dragh + .vo-row--drag แต่ row ใช้ attr แยก (data-sc-row)
   กัน scratchorder.js/vieworder.js ชนกัน (ดู scratchorder.js) */
[data-sc-row].vo-row--drag { opacity:.5; }
/* handle ใน name-pane gantt — ชิดซ้าย ลด gap ไม่ให้เบียดชื่อ/avatar (name-pane แคบ) */
#tl-root .tl2-dragh { flex:none; margin:0 -4px 0 -6px; }
/* ============================================================================
   Leave Summary Table (S35 · DEC-079) — ตารางสรุปการใช้สิทธิ์ลา (โควตาวันลา)
   section แยกใต้ gantt วันลา · cell คู่ used/quota · admin inline-edit quota
   สเปก: docs/design/leave-summary-table-uidesign-s35.md (ยู)
   ============================================================================ */
.lq-section {
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:20px; margin-top:24px; box-shadow:var(--shadow-card);
}
.lq-section-load { padding:32px 16px; }
.lq-head { justify-content:flex-start; }
.lq-head h2 { font-size:16px; font-weight:600; }
.lq-ic {
  width:28px; height:28px; flex:0 0 28px; border-radius:50%;
  background:color-mix(in srgb, var(--primary-solid) 12%, transparent); color:var(--primary-solid);
  display:inline-flex; align-items:center; justify-content:center; font-size:14px;
}
.lq-head-ctrl { margin-left:auto; display:flex; align-items:center; gap:8px; }
.lq-year-btn { min-height:36px; }
.lq-caption { font-size:12px; color:var(--text-2); margin:6px 0 12px; }

.lq-tbl-wrap { overflow-x:auto; }
.lq-tbl { width:100%; border-collapse:collapse; }
.lq-tbl .tbl-th { position:sticky; top:0; background:var(--surface); }
.lq-tbl .lq-th-member, .lq-tbl .lq-member { text-align:left; }
/* freeze คอลัมน์สมาชิกตอน scroll แนวนอน (ยู §5.1) */
.lq-tbl .lq-th-member, .lq-tbl th[scope="row"].lq-member {
  position:sticky; left:0; background:var(--surface); z-index:1;
}
.lq-member { display:flex; align-items:center; gap:8px; font-weight:500; }
.lq-member-t { min-width:0; overflow-wrap:anywhere; }
/* zebra จาง (7 คอลัมน์ tracking ตายาก — ยู §2.5) */
.lq-tbl tbody tr:nth-child(even) td, .lq-tbl tbody tr:nth-child(even) th[scope="row"] {
  background:color-mix(in srgb, var(--surface-2) 55%, transparent);
}
.lq-foot td, .lq-foot th { border-top:2px solid var(--border); font-weight:600; }

/* cell used / quota */
.lq-cell { white-space:nowrap; }
.lq-used { color:var(--text-2); font-variant-numeric:tabular-nums; }
.lq-sep { color:var(--border-control); margin:0 2px; }
.lq-quota-ro { color:var(--text); font-variant-numeric:tabular-nums; }
.lq-quota-in {
  display:inline-block; width:52px; min-height:32px; text-align:right;
  padding-inline:8px; border:1px solid var(--border-control); border-radius:6px;
}
.lq-quota-in:hover { background:var(--surface-2); }
/* success flash หลัง commit (dual: เลขเปลี่ยน + flash) */
.lq-row { transition:background-color .8s ease; }
.htmx-settling.lq-row { background:color-mix(in srgb, var(--green) 18%, transparent); }

/* คงเหลือ ติดลบ (ลาเกินสิทธิ์) — dual-encode: สี + icon + "(เกิน)" + aria (ยู §4.2) */
.lq-neg { color:var(--alert-text); font-weight:600; }
.lq-neg i.fi { font-size:12px; }

/* mobile <768: card ต่อคน (ยู §5.2) — table → block card */
@media (max-width:767px) {
  .lq-tbl thead { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
  .lq-tbl, .lq-tbl tbody, .lq-tbl tfoot { display:block; }
  .lq-tbl tr {
    display:block; border:1px solid var(--border); border-radius:12px;
    padding:10px 12px; margin-bottom:10px;
  }
  .lq-tbl tbody tr:nth-child(even) td, .lq-tbl tbody tr:nth-child(even) th[scope="row"] { background:transparent; }
  .lq-tbl th[scope="row"].lq-member {
    position:static; display:flex; border-bottom:1px solid var(--border);
    padding-bottom:8px; margin-bottom:8px;
  }
  .lq-tbl td {
    display:flex; justify-content:space-between; align-items:center;
    padding:4px 0; border:0; text-align:right;
  }
  /* label ประเภท/roll-up ก่อนค่า (data-label) */
  .lq-tbl td::before {
    content:attr(data-label); color:var(--text-2); font-size:var(--fs-sm);
    text-align:left; margin-right:12px;
  }
}

/* ============================================================================
   Platform S35 (DEC-082/083) — module switcher · /projects · Project Hub · Cost P&L
   reuse token 3-tier (Wegital DS) — ❌ ไม่ hardcode สี (light+dark ผ่าน CSS var)
   ============================================================================ */

/* number cells: tabular-nums ชิดขวา (เงิน/manday) */
.tnum { font-variant-numeric:tabular-nums; font-feature-settings:"tnum"; }

/* ---- module switcher (topbar · Atlassian-style · reuse .topnav-manage/.topnav-menu) ---- */
.ms-divider { width:1px; align-self:stretch; margin:8px 4px; background:var(--border); }
.module-switcher .ms-btn { gap:6px; }
.module-switcher .ms-label { font-weight:500; }
.ms-btn--static { cursor:default; }  /* S35 fix: ลบ opacity:.85 (3.83:1 FAIL) — ไม่มี chevron + cursor:default สื่อ "กดไม่ได้" คง --text-2 เต็ม contrast */
.ms-menu { min-width:240px; }
.ms-head { font-size:12px; color:var(--text-2); padding:6px 12px 2px; }
.ms-item { display:flex; align-items:center; gap:10px; }
.ms-item-main { display:flex; flex-direction:column; line-height:1.25; }
.ms-item-sub { font-size:11px; color:var(--text-2); }
.ms-item--current { background:rgba(220,60,50,.08); font-weight:500; }
[data-theme="dark"] .ms-item--current { background:rgba(255,120,110,.14); }
.ms-check { margin-left:auto; color:var(--primary-solid); }

/* ---- page head (list/cost) ---- */
.ph-title h1 { display:flex; align-items:center; gap:8px; }
.ph-title h1 i.fi { color:var(--primary-solid); }
.ph-cap { margin:2px 0 0; font-size:var(--fs-sm); color:var(--text-2); }

/* ---- status pill (dual-encode: dot + คำ ไม่พึ่งสีเดียว) ---- */
.status-pill { display:inline-flex; align-items:center; gap:6px; font-size:var(--fs-sm); font-weight:500; }
.status-dot { width:8px; height:8px; border-radius:999px; background:var(--text-2); }
.status-active .status-dot { background:var(--green); }
.status-closed .status-dot { background:var(--text-2); }
/* DEC-105 M1 project lifecycle (dual-encode dot+คำ) */
.status-estimating .status-dot { background:var(--warm); }
.status-quote-approved .status-dot { background:var(--cold); }
.status-archived .status-dot { background:var(--text-2); }

/* ---- profit/loss dual-encode (สี + arrow + เครื่องหมาย + คำ) ---- */
.pl-amt { display:inline-flex; align-items:center; gap:2px; font-variant-numeric:tabular-nums; font-weight:500; }
.pl-amt i.fi { font-size:.85em; }
.pl-profit { color:var(--green-text); }  /* S35 fix: --green fail AA (2.02:1) → --green-text (AA both mode) */
.pl-loss { color:var(--alert-text); }    /* S35 fix: --danger ไม่ define ใน DS → --alert-text (AA both mode) */
.pl-na, .pl-zero { color:var(--text-2); }

/* ---- KPI strip (hub + cost) ---- */
.kpi-strip { display:flex; flex-wrap:wrap; gap:12px; margin:12px 0 16px; }
.kpi-tile { flex:1; min-width:150px; background:var(--surface); border:1px solid var(--border);
  border-radius:var(--r); padding:12px 14px; display:flex; flex-direction:column; gap:4px; }
.kpi-label { font-size:12px; color:var(--text-2); }
.kpi-value { font-size:20px; font-weight:600; }
.kpi-sub { font-size:11px; color:var(--text-2); }
/* .kpi-unit — DEC-094 (S41, money-format-thb-spec §2): suffix "THB" ตัวเล็กต่อท้ายเลข KPI card
   (เลือก class เดียวกับ .kpi-sub — 11px/--text-2 คำนวณ contrast ผ่าน AA แล้ว ไม่ต้องคำนวณใหม่)
   ⚠️ ปัจจุบัน helper fmtBahtKPI/fmtMoneyShortKPI (wefin_ui.go) ต่อ " THB" เป็น plain string
   เข้าไปในค่าตรง ๆ (ไม่มี markup แยก) → .kpi-unit ยังไม่ auto-apply กับจุดที่เรียก helper เหล่านี้
   วันนี้ (ตัวเลข+THB จะมีขนาด/สีเดียวกันทั้งสตริง) — ต้องแยก field ฝั่ง Go (เด) เป็น Value+Unit
   หรือ template.HTML ก่อนจะ wrap span นี้ได้จริง (ดู Pattern A/B ในสเปก) ไว้ก่อนสำหรับจุดที่มี
   markup แยกอยู่แล้ว/จะแยกทีหลัง */
.kpi-unit { font-size:11px; color:var(--text-2); font-weight:400; margin-left:4px; }
.kpi-tile .rpt-bar { max-width:none; margin:2px 0; }

/* ---- Project Hub header + tabs ---- */
.hub-head { padding-bottom:4px; }
.hub-back { display:inline-flex; align-items:center; gap:2px; font-size:var(--fs-sm); color:var(--text-2); text-decoration:none; }
.hub-back:hover { color:var(--primary-solid); }
.hub-title { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:2px; }
.hub-title h1 { margin:0; overflow-wrap:anywhere; }
.hub-meta { margin:2px 0 0; font-size:var(--fs-sm); color:var(--text-2); }
/* M2.1 fix: 9 tab (เพิ่ม Delay/Payment/Defense) ล้นที่ <~1000px/มือถือ → เดิม seg-link flex:1 min-width:0 หด
   ต่ำกว่าเนื้อหาจนตัวอักษรทะลุซ้อน. แก้: tab ไม่หด (flex:1 0 auto) → กว้างพอ=fill desktop เหมือนเดิม ·
   ล้น=scroll แนวนอน (nowrap) แทนซ้อน · scrollbar บางตาม DS (โผล่เฉพาะตอนล้น · desktop กว้างพอ=ไม่โผล่) */
.hub-tabs { margin:12px 0 16px; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch;
  flex-wrap:nowrap; scrollbar-width:thin; scrollbar-color:var(--scrollbar-thumb) transparent; }
.hub-tabs::-webkit-scrollbar { height:6px; }
.hub-tabs::-webkit-scrollbar-track { background:transparent; }
.hub-tabs::-webkit-scrollbar-thumb { background:var(--scrollbar-thumb); border-radius:999px; }
.hub-tabs .seg-link { flex:1 0 auto; white-space:nowrap; }   /* ไม่หด (flex-shrink:0) + ไม่ตัดคำ = ล้นแล้ว scroll ไม่ซ้อน */
.hub-tabpanel { }
.hub-2col { display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.hub-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:16px; }
.hub-card-h { margin:0 0 10px; font-size:15px; }
.hub-dl { display:grid; grid-template-columns:auto 1fr; gap:6px 14px; margin:0; font-size:var(--fs-sm); }
.hub-dl dt { color:var(--text-2); }
.hub-dl dd { margin:0; overflow-wrap:anywhere; }
.hub-tasks-cross { margin-top:12px; }

/* ---- chips (linked spaces) ---- */
.chip-row { display:flex; flex-wrap:wrap; gap:8px; }
.chip { display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:999px;
  background:var(--surface-2); border:1px solid var(--border); font-size:var(--fs-sm); }
.chip--link { text-decoration:none; color:var(--text); }
.chip--link:hover { border-color:var(--primary-solid); color:var(--primary-solid); }
.chip-x { border:none; background:none; cursor:pointer; color:var(--text-2); display:inline-flex; padding:2px;
  min-width:24px; min-height:24px; align-items:center; justify-content:center; border-radius:999px; }
.chip-x:hover { color:var(--alert-text); background:var(--surface); }
.chip-add { display:flex; gap:8px; align-items:center; margin-top:8px; }

/* ---- P&L summary (hub cost tab) ---- */
.pl-summary { background:var(--surface); border:1px solid var(--border); border-radius:var(--r);
  padding:8px 16px; margin-bottom:16px; max-width:520px; }
.pl-line { display:flex; justify-content:space-between; align-items:center; padding:8px 0;
  border-bottom:1px solid var(--border); font-size:var(--fs-sm); }
.pl-line:last-child { border-bottom:none; }
.pl-line--total b, .pl-line--profit b { font-size:16px; }
.pl-line--note { color:var(--text-2); font-size:12px; }
.pl-margin { font-size:.85em; color:var(--text-2); }

/* ---- ledger (add expense) ---- */
.ledger { margin-top:8px; }
.ledger-add { margin-top:12px; }
.ledger-add-row { display:flex; flex-wrap:wrap; gap:8px; align-items:center; }
.ledger-add-row .cell-input { flex:1; min-width:120px; }
.ledger-add-row .dd { min-width:150px; }

/* ---- markup calculator (disclosure) ---- */
.markup-calc { margin:8px 0; border:1px solid var(--border); border-radius:var(--r); padding:0 12px; }
.markup-calc > summary { cursor:pointer; padding:10px 0; font-size:var(--fs-sm); font-weight:500; }
.markup-body { padding-bottom:12px; }
.markup-out { display:flex; align-items:center; gap:10px; flex-wrap:wrap; font-size:var(--fs-sm); }

/* ---- project modal + form ---- */
.modal-box--wide { max-width:560px; width:calc(100vw - 32px); }
.pj-form-2col { display:grid; grid-template-columns:1fr 1fr; gap:0 14px; }
.pj-sep { height:1px; background:var(--border); margin:12px 0; }
/* DEC-105 M1: auto-pair note ในฟอร์มสร้างโปรเจค */
.pj-autopair-note { display:flex; gap:10px; align-items:flex-start; background:var(--surface-2); border:1px solid var(--border); border-radius:12px; padding:12px 14px; font-size:13px; color:var(--text-2); }
.pj-autopair-note i { color:var(--primary-solid); margin-top:2px; flex-shrink:0; }
.pj-autopair-note b { color:var(--text); }
.pj-actions { display:flex; gap:4px; white-space:nowrap; }
/* .iconbtn--danger:hover มีอยู่แล้ว (app.css:579 · var(--alert)) — ไม่ redefine (เลี่ยง --danger ที่ไม่มีจริง) */
.alert-box--sm { padding:6px 10px; font-size:12px; margin-bottom:8px; }

/* ---- empty card extras ---- */
.empty-ic { font-size:32px; color:var(--text-2); display:block; margin-bottom:8px; }
.empty-title { font-weight:600; margin:0 0 4px; }

/* ---- people list ---- */
.people-list { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px; }
.person { display:flex; align-items:center; gap:10px; padding:8px 12px; background:var(--surface);
  border:1px solid var(--border); border-radius:var(--r); }
.person-meta { display:flex; flex-direction:column; line-height:1.25; }
.person-role { font-size:11px; color:var(--text-2); }

/* ---- responsive (mobile <768) ---- */
@media (max-width:768px) {
  .hub-2col { grid-template-columns:1fr; }
  .pj-form-2col { grid-template-columns:1fr; }
  .module-switcher .ms-label { display:none; }  /* mobile: icon อย่างเดียว */
  .kpi-tile { min-width:130px; }
  .ledger-add-row { flex-direction:column; align-items:stretch; }
}

/* =========================================================================
   WeFin (Product 2 · S36) — ทุกสี = DS token (light+dark auto) · ไม่ hardcode
   ========================================================================= */
/* generic form/card primitives (WeFin เจอก่อน — ยังไม่มีใน DS) */
.card { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:16px; }
.input { width:100%; padding:9px 12px; border:1px solid var(--border); border-radius:10px;
  background:var(--surface); color:var(--text); font:inherit; font-size:14px; }
.input:focus { outline:none; border-color:var(--primary-solid);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 14%, transparent); }
.btn-secondary { display:inline-flex; align-items:center; gap:6px; padding:8px 14px; border-radius:10px;
  border:1px solid var(--border); background:var(--surface); color:var(--text); font:inherit; font-weight:500;
  cursor:pointer; text-decoration:none; }
.btn-secondary:hover { background:var(--surface-2); }

.fin-note { color:var(--text-2); font-size:12px; }
.fin-muted { color:var(--text-2); }
/* fin-banner — แถบแจ้ง info/เตือน (S39 · flag mrr_source / งวดแรก) · dual-encode icon+ข้อความ */
.fin-banner { display:flex; align-items:flex-start; gap:8px; padding:10px 12px; border-radius:8px;
  font-size:13px; margin:0 0 12px; border:1px solid var(--border-control); }
.fin-banner i { margin-top:1px; color:var(--secondary-text); }
.fin-banner--info { background:var(--surface-2); color:var(--text); }
.fin-banner code { font-size:12px; background:var(--surface); padding:1px 5px; border-radius:4px; }
.fin-empty { color:var(--text-2); font-size:13px; padding:12px 0; }

/* period selector (pills) */
.fin-periodbar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:4px 0 16px; }
.fin-periodbar-lbl { font-size:13px; color:var(--text-2); display:inline-flex; align-items:center; gap:5px; }
/* a11y B4 (S36 · SC 1.4.11 · เอ พบ 1.17:1 fail): ขอบ control (--border hairline ตกแต่ง) → --border-control
   (3.26:1 บน --surface — ดู comment app.css:36-39) ให้ pill/seg/chip ที่เป็น control จริงแยกจากพื้นชัดเจน */
/* touch-target ≥44px (DS §0.10 / WCAG 2.5.5): pill/seg/chip เป็น control จริง — recurrence จาก backlog A4
   (component ใหม่ WeFin พลาดซ้ำ · อู๋ พบ) → min-height:44px + inline-flex align-items:center คงหน้าตา
   compact (padding เดิม) แต่ hit-area ตั้ง (poka-yoke: css_touchtarget_test.go สแกน block นี้กัน regress) */
.fin-pill { display:inline-flex; align-items:center; min-height:44px; padding:5px 12px; border-radius:999px;
  border:1px solid var(--border-control); background:var(--surface);
  color:var(--text-2); font-size:13px; text-decoration:none; white-space:nowrap; }
.fin-pill:hover { background:var(--surface-2); color:var(--text); }
.fin-pill--active { background:var(--primary-solid); border-color:var(--primary-solid); color:#fff; font-weight:600; }
/* a11y B3: focus-visible หาย (SC 1.4.11) — reuse pattern มาตรฐาน app.css:500-518 */
.fin-pill:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }

.kpi-tile .kpi-sub { margin-top:2px; }
/* C (S36 · เด polish): MoM delta badge เดิม reuse .pl-amt เต็มขนาด (inherit font-size จาก .kpi-tile ~13px)
   → ใหญ่ไปเทียบ context KPI tile — เล็กลงเท่า .kpi-sub (11px) ตาม PROTOTYPE-TEMP ที่ เด ทิ้งไว้ */
.kpi-delta { font-size:11px; margin-left:6px; }

/* 2-col grid (chart + summary) */
.fin-grid2 { display:grid; grid-template-columns:3fr 2fr; gap:16px; margin-top:4px; }
.fin-card { padding:16px; }
.fin-card-title { font-size:15px; font-weight:600; margin:0 0 12px; }

/* revenue-by-stream stacked bar chart (server-render · height % · สี --cat-*) */
.fin-chart { display:flex; align-items:flex-end; gap:14px; height:200px; padding:8px 4px 0; }
.fin-bar-col { flex:1; display:flex; flex-direction:column; align-items:center; gap:4px; min-width:0; height:100%; justify-content:flex-end; }
.fin-bar-stack { width:100%; max-width:46px; height:160px; display:flex; flex-direction:column; justify-content:flex-end;
  border-radius:6px 6px 0 0; overflow:hidden; background:var(--surface-2); }
.fin-bar { width:100%; }
/* a11y B1 (S36 · SC 1.4.11 · เอ พบ 2.55:1 fail): เส้นแบ่งระหว่าง segment สี (border-bottom กันชนกับตัวถัดไป
   ที่ต่อกันด้านล่างใน DOM order) — var(--surface) คำนวณจริงผ่าน ≥3:1 ทุก segment ทั้ง 2 theme
   (light ต่ำสุด cat-1 3.63:1 · dark ต่ำสุด cat-2 3.46:1) ไม่พึ่งสีอย่างเดียวแยกขอบเขต segment อีกต่อไป */
.fin-bar:not(:last-child) { border-bottom:2px solid var(--surface); }
.fin-bar--sub { background:var(--cat-1); }
.fin-bar--svc { background:var(--cat-2); }
.fin-bar--other { background:var(--cat-other); }
.fin-bar-val { font-size:11px; color:var(--text-2); font-variant-numeric:tabular-nums; }
.fin-bar-lbl { font-size:11px; color:var(--text-2); white-space:nowrap; }
.fin-legend { display:flex; flex-wrap:wrap; gap:14px; margin-top:12px; font-size:12px; color:var(--text-2); }
.fin-leg { display:inline-flex; align-items:center; gap:6px; }
/* a11y B1: legend มีตัวเลขจริง (ไม่ใช่แค่สี+ชื่อ) — redundant text encoding คู่กับ divider */
.fin-leg-val { color:var(--text); font-weight:500; font-variant-numeric:tabular-nums; }
.fin-sw { width:12px; height:12px; border-radius:3px; display:inline-block; }
.fin-sw--sub { background:var(--cat-1); }
.fin-sw--svc { background:var(--cat-2); }
.fin-sw--other { background:var(--cat-other); }

/* P&L summary (dashboard) */
.fin-pl-summary { margin:0; }
.fin-pl-line { display:flex; justify-content:space-between; align-items:center; padding:7px 0; border-bottom:1px solid var(--border); }
.fin-pl-line dt { color:var(--text-2); font-size:13px; }
.fin-pl-line dd { margin:0; font-variant-numeric:tabular-nums; font-weight:500; }
.fin-pl-line--sub dt, .fin-pl-line--sub dd { font-weight:600; color:var(--text); }
.fin-pl-line--total { border-bottom:none; border-top:2px solid var(--text); margin-top:4px; padding-top:10px; }
.fin-pl-line--total dt, .fin-pl-line--total dd { font-weight:700; font-size:15px; }
.fin-pl-more { margin-top:12px; }

/* P&L statement table — ใช้ .tbl-scroll ร่วมกับทุกตารางแล้ว (S56 กลับด้าน: ไม่มีคลาสไหนซ่อนตัวเองอีก
   จึงไม่ต้องมี .fin-pl-scroll แยกเพื่อหนีการซ่อน — คลาสนั้นถูกยุบทิ้งแล้ว)
   fix A: ตาราง 2 คอลัมน์ (ป้าย/จำนวนเงิน) ไม่ต้องการ min-width:960px ของ .tbl กลาง (ออกแบบไว้สำหรับตาราง
   การ์ด/multi-column) → ปลด min-width ให้คอลัมน์จำนวนเงินอยู่ในความกว้าง container จริงเสมอ ไม่ต้อง scroll
   แนวนอนที่ desktop เนื้อที่แคบ (~570px มี sidebar) หรือ mobile */
.fin-pl-table { width:100%; min-width:0; }
/* ป้ายยาว (เช่น "กำไรจากการดำเนินงาน (Operating Profit)") ที่จอแคบมาก — ให้ตัดคำแทน ellipsis กันข้อความหาย
   (.tbl-td ปกติ nowrap+ellipsis เหมาะกับตารางหลายคอลัมน์ แต่ 2 คอลัมน์นี้เนื้อที่พอ wrap ได้สวยกว่า) */
.fin-pl-table .tbl-td { white-space:normal; max-width:none; overflow:visible; text-overflow:clip; }
.fin-pl-table .tbl-td--num { white-space:nowrap; } /* จำนวนเงิน/pl_cell ไม่ wrap เสมอ (tabular-nums ชิดขวา) */
.fin-pl-head td { font-weight:700; color:var(--text); padding-top:14px; background:var(--surface-2); }
.fin-pl-indent { padding-left:24px !important; color:var(--text-2); }
.fin-pl-subtotal td { font-weight:600; border-top:1px solid var(--border); }
.fin-pl-gross td, .fin-pl-grandtotal td { font-weight:700; }
.fin-pl-grandtotal td { border-top:2px solid var(--text); border-bottom:2.5px double var(--text); font-size:15px; }
.fin-pl-margin td { font-size:12px; }
.fin-drill { margin-top:20px; }
.fin-drill-note { margin-top:10px; }

/* form segmented control + chip select */
.fin-form-card { margin:8px 0 20px; }
.fin-form { display:flex; flex-direction:column; gap:14px; max-width:640px; }
.fin-row { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.fin-field { display:flex; flex-direction:column; gap:5px; }
.fin-lbl { font-size:12px; color:var(--text-2); font-weight:500; }
/* a11y B4: border-control (3.26:1) แทน --border hairline (1.17:1) — .fin-seg/.fin-seg-opt/.fin-chip เป็น control จริง */
.fin-seg { display:inline-flex; border:1px solid var(--border-control); border-radius:10px; overflow:hidden; width:fit-content; }
.fin-seg-opt { display:inline-flex; align-items:center; min-height:44px; /* touch-target ≥44px (DS §0.10) */
  padding:8px 16px; background:var(--surface); color:var(--text-2); border:none; font:inherit; font-size:13px;
  cursor:pointer; border-right:1px solid var(--border-control); }
.fin-seg-opt:last-child { border-right:none; }
.fin-seg-opt.is-on { background:var(--primary-solid); color:#fff; font-weight:600; }
/* a11y B3: focus-visible หาย (SC 1.4.11) — reuse pattern มาตรฐาน app.css:500-518 */
.fin-seg-opt:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.fin-chipselect { display:flex; flex-wrap:wrap; gap:8px; }
.fin-chip { display:inline-flex; align-items:center; min-height:44px; /* touch-target ≥44px (DS §0.10) */
  padding:6px 12px; border-radius:999px; border:1px solid var(--border-control); background:var(--surface);
  color:var(--text-2); font:inherit; font-size:13px; cursor:pointer; }
.fin-chip:hover { background:var(--surface-2); }
.fin-chip.is-on { background:var(--primary-solid); border-color:var(--primary-solid); color:#fff; font-weight:600; }
.fin-chip:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.fin-form-actions { margin-top:4px; }

/* stream tag (revenue list) */
.fin-streamtag { display:inline-block; padding:2px 9px; border-radius:999px; font-size:12px; font-weight:600;
  background:color-mix(in srgb, var(--cat-1) 16%, transparent); color:var(--text); }
.fin-streamtag--service { background:color-mix(in srgb, var(--cat-2) 16%, transparent); }
.fin-streamtag--other { background:var(--surface-2); }

/* =========== P&L Waterfall chart (S36 · ซี · chart-spec-s36 §4 + ds-additions-s36 §3) ===========
   server-render SVG (x/y คำนวณฝั่ง Go — wefin_charts.go) · container-type เผื่อ scale ภายในต่อยอด
   (breakpoint desktop→mobile ใช้ media query กว้าง เพราะเป็นการสลับ SVG↔mini-stack ระดับหน้า ไม่ใช่แค่ resize
   ส่วนย่อยในการ์ด — สอดคล้อง §4.6 chart-spec ที่อ้าง breakpoint กลาง <768 ของ DS) */
.fin-figure { margin:0; }
.waterfall-chart { container-type:inline-size; container-name:waterfall; }
.waterfall-chart svg { width:100%; height:auto; display:block; overflow:visible; }
.waterfall-connector { stroke:var(--border-control); stroke-dasharray:2 3; stroke-width:1; }
/* S36 gap fix (เอส §1.5/§3 คำนวณจริง): fill สีเขียว/แดงบนพื้นสว่างต้องใช้ -text variant เสมอ
   (--green ดิบ = 2.02:1 fail 1.4.11 non-text-contrast บนพื้นทึบขนาดใหญ่แบบแท่งกราฟ)
   S36 gap เพิ่ม (ซี พบจริงตอน verify browser — คำนวณ relative-luminance เอง ไม่ใช่กะสายตา):
   สเปกเดิมให้ total bar = --secondary-end (#7846D2) อิงจากที่ chart-spec/ds-additions เช็คแค่ light
   (5.82:1 ผ่าน) แต่ dark vs --surface (#1E1E1E) = 2.86:1 fail 1.4.11 (<3:1) — ไม่ใช่จุดเดียวกับที่ เอส
   เคย flag ไว้ (นั่นคือ vs --surface-2 ของ Bar Chart/Score Ring เดิม คนละ component) → สลับมาใช้
   --secondary-text แทน (6.93:1 light / 7.09:1 dark ผ่านขาดทั้งคู่ — ตรงกฎเดียวกับ green/alert: ใช้ -text
   variant เป็น fill เสมอ ไม่ใช้ base semantic ดิบ) */
.waterfall-bar--total { fill:var(--secondary-text); }
.waterfall-bar--decrease { fill:var(--alert-text); }
.waterfall-bar--increase { fill:var(--green-text); }
.waterfall-bar-g:focus-visible .waterfall-bar { outline:2px solid var(--primary-solid); outline-offset:2px; }
.waterfall-label { font-size:11px; fill:var(--text); font-variant-numeric:tabular-nums lining-nums; }
.waterfall-label--total { font-weight:600; text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:4px; }
.waterfall-axis-label { font-size:11px; fill:var(--text-2); }
.waterfall-mobile { display:none; }
.waterfall-delta-list { list-style:none; margin:12px 0 0; padding:0; display:flex; flex-direction:column; gap:2px; }
.waterfall-delta-list li { display:flex; justify-content:space-between; font-size:13px; padding:6px 0; border-bottom:1px solid var(--border); }
.waterfall-delta--neg { color:var(--alert-text); font-weight:500; }
.waterfall-table { margin-top:14px; }
.waterfall-table summary { cursor:pointer; font-size:13px; color:var(--text-2); padding:4px 0; }
.waterfall-table summary:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* =========== MRR Movement waterfall additions (S39 Wave 2 · DEC-088 · วิช §1.4/§1.5) =========== */
/* customer-count sub-label ใต้ตัวเลขเงินในแท่ง delta (SVG text · non-color signal §1.4-4) */
.waterfall-label-sub { font-size:10px; fill:var(--text-2); font-variant-numeric:tabular-nums lining-nums; }
/* increase delta ใน mobile fallback (เดิมมีแค่ --neg เพราะ P&L มีแต่ decrease · §1.9-2) */
.waterfall-delta--pos { color:var(--green-text); font-weight:500; }
.waterfall-delta-sub { color:var(--text-2); font-weight:400; font-size:11px; }
/* legend strip เหนือกราฟ (HTML ไม่ใช่ SVG — icon-font glyph ตรง ๆ · §1.4-2) · dual-encode icon+ชื่อ+กลุ่มสี */
.waterfall-legend { display:flex; flex-wrap:wrap; gap:10px 18px; margin:0 0 10px; font-size:12px; }
.wf-legend-group { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.wf-legend-group > span:not(.wf-legend-item):not(.wf-legend-dot) { font-weight:600; color:var(--text-2); }
.wf-legend-dot { width:10px; height:10px; border-radius:2px; display:inline-block; }
.wf-legend-group--pos .wf-legend-dot { background:var(--green-text); }
.wf-legend-group--neg .wf-legend-dot { background:var(--alert-text); }
.wf-legend-item { display:inline-flex; align-items:center; gap:3px; color:var(--text); }
.wf-legend-item i { font-size:12px; }
.wf-legend-group--pos .wf-legend-item i { color:var(--green-text); }
.wf-legend-group--neg .wf-legend-item i { color:var(--alert-text); }

/* KPI delta inverse (churn: น้อย=ดี → กลับสี · §2.2 · generic ใช้ต่อ burn rate อนาคต) */
.kpi-delta.is-inverse.pl-profit, .kpi-delta.is-inverse .pl-profit { color:var(--alert-text) !important; }
.kpi-delta.is-inverse.pl-loss, .kpi-delta.is-inverse .pl-loss { color:var(--green-text) !important; }

/* NRR/GRR threshold color บนค่าเด่น + สถานะ dual-encode (§3.2) */
.mrr-stat-val--good { color:var(--green-text); }
.mrr-stat-val--bad { color:var(--alert-text); }
.mrr-kpi-status { font-size:11px; color:var(--text-2); font-weight:400; margin-left:4px; }
.mrr-kpi-sub { font-size:12px; color:var(--text-2); margin-top:2px; }

/* MRR trend sparkline (line chart 6 งวด · reuse SVG server-render) */
.mrr-trend svg { width:100%; height:auto; display:block; overflow:visible; }
.mrr-trend-line { fill:none; stroke:var(--primary-solid); stroke-width:2; }
.mrr-trend-dot { fill:var(--primary-solid); }
.mrr-trend-axis { font-size:10px; fill:var(--text-2); }

/* =========== Budget vs Actual Bullet chart (Stephen Few) — S36 · ซี · chart-spec-s36 §5 + ds-additions-s36 §4
   Anatomy: qualitative range band (grayscale, context เท่านั้น ไม่ใช่สี status) + actual bar ทึบ (--secondary-start)
   + target tick (--text) · สี semantic (เกินงบ/ต่ำกว่าเป้า) แยกไปอยู่ที่ badge ข้าง ๆ ไม่ overload บนแท่งเดียวกัน
   container-type อยู่ที่ .bullet-list (ancestor) — @container query สลับ layout .bullet-row ตอนแคบ =========== */
.bullet-list { container-type:inline-size; container-name:bullet-list; margin:4px 0 16px; display:flex; flex-direction:column; }
.bullet-row { display:grid; grid-template-columns:minmax(120px,180px) 1fr auto auto; align-items:center; gap:12px; padding:8px 0; border-bottom:1px solid var(--border); }
.bullet-row:last-child { border-bottom:none; }
.bullet-label { font-size:13px; color:var(--text); overflow-wrap:anywhere; }
.bullet-track { position:relative; height:28px; border-radius:4px; background:var(--surface); border:1px solid var(--border-control); overflow:visible; }
.bullet-band { position:absolute; inset:0; border-radius:4px 0 0 4px; pointer-events:none; }
.bullet-band--mid { width:66%; background:rgba(var(--text-2-rgb),.12); }
.bullet-band--good { width:33%; background:rgba(var(--text-2-rgb),.22); }
.bullet-actual { position:absolute; top:30%; left:0; height:40%; min-width:2px; background:var(--secondary-start); border-radius:0 2px 2px 0; }
.bullet-target { position:absolute; top:-10%; height:120%; width:2px; background:var(--text); transform:translateX(-1px); }
.bullet-nums { font-size:12px; white-space:nowrap; font-variant-numeric:tabular-nums lining-nums; }
.bullet-status { font-size:12px; }
@container bullet-list (max-width:480px) {
  .bullet-row { grid-template-columns:1fr; gap:4px; padding:10px 0; }
  .bullet-nums, .bullet-status { justify-self:start; }
}

/* =========== Donut chart (part-to-whole ≤3 หมวด) — S54 Wave 2 · วิช, data-viz ===========
   server-render SVG (stroke-dasharray stacked circle, ต่อยอด Score/Metric Ring §5 DS) — ใช้เฉพาะ
   ที่ DS §1.3 อนุญาต (categorical ≤3 หมวด + data label) ไม่ใช่ default chart-selection สำหรับ
   part-to-whole ทั่วไป (ยังแนะนำ stacked bar เป็นหลัก — donut นี้เป็นกราฟ "เสริม" ของหน้าที่มีอยู่แล้ว)
   สี stroke/background ต่อ segment ผูกผ่าน class ปิดชุด `.donut-c-*` (ดูเหตุผลด้านล่าง) ไม่ใช่ inline style
   [S55 รอบแก้ตาม เต๋ verify] layout เปลี่ยนจาก "ทุกอย่าง center ซ้อนกันเป็นคอลัมน์" →
   plot (วง+ป้ายใต้วง) กับ legend เรียงตามความกว้างจริงของการ์ดด้วย container query (§6 DS)
   + ทุกอย่างชิดแกนซ้ายเดียวกับเนื้อหาการ์ด (เดิม legend ลอยกลางไม่มีแกนร่วมกับ content: M5/M7/N7) */
.donut-wrap { container-type:inline-size; container-name:donut; }
.donut-figure { margin:0; display:flex; flex-direction:column; align-items:flex-start; gap:16px; }
/* plot = วงกลม + sub-label ใต้วง (ผูกกันเป็นก้อนเดียว ไม่ให้ sub-label หลุดไปอยู่ใต้ legend) */
.donut-plot { width:160px; max-width:100%; flex:0 0 auto; display:flex; flex-direction:column; align-items:center; gap:4px; }
.donut-svg { width:100%; height:auto; display:block; overflow:visible; }
/* track = ช่องคั่นระหว่างส่วนโค้ง (gap เจาะจาก stroke-dasharray ฝั่ง Go) — โปร่งใสเพื่อให้เห็น "พื้นจริง"
   ที่กราฟวางอยู่ทะลุมา (การ์ดหน้านี้พื้น --bg / การ์ดจริงอื่น ๆ พื้น --surface) = เส้นคั่นตรงพื้นเสมอ
   โดยไม่ hardcode สีพื้น (M2 + ช่วย H4: แยกส่วนโค้งที่อยู่ติดกันแม้สีเพี้ยนใกล้กันภายใต้ตาบอดสี) */
.donut-track { fill:none; stroke:transparent; }
.donut-seg { fill:none; stroke-linecap:butt; }
/* ⚠️ ห้ามใช้ inline style="stroke:var(--x)"/"background:var(--x)" กับค่าที่มาจาก Go template — html/template
   sanitize CSS context เจอ var() แล้วไม่รู้จักว่าปลอดภัย → แทนที่ด้วย "ZgotmplZ" ทั้งก้อน (พิสูจน์จริงตอน curl
   verify S54 · เจอ ZgotmplZ ในหน้าจริง) ต้องผ่าน "class ปิดชุดที่รู้จักล่วงหน้า" แทนเสมอ (ตรงกับ donutColorClass
   ใน presales_charts.go) — รายชื่อ token ที่อนุญาต = ชุดที่ผ่าน WCAG 1.4.11 ที่มีอยู่แล้วใน DS (§1.3 categorical +
   -text variant + text-2) ไม่ hardcode hex ใหม่ */
circle.donut-c-primary-solid { stroke:var(--primary-solid); } span.donut-sw.donut-c-primary-solid { background:var(--primary-solid); }
circle.donut-c-cat-1 { stroke:var(--cat-1); } span.donut-sw.donut-c-cat-1 { background:var(--cat-1); }
circle.donut-c-cat-2 { stroke:var(--cat-2); } span.donut-sw.donut-c-cat-2 { background:var(--cat-2); }
circle.donut-c-cat-3 { stroke:var(--cat-3); } span.donut-sw.donut-c-cat-3 { background:var(--cat-3); }
circle.donut-c-cat-4 { stroke:var(--cat-4); } span.donut-sw.donut-c-cat-4 { background:var(--cat-4); }
circle.donut-c-cat-5 { stroke:var(--cat-5); } span.donut-sw.donut-c-cat-5 { background:var(--cat-5); }
circle.donut-c-cat-6 { stroke:var(--cat-6); } span.donut-sw.donut-c-cat-6 { background:var(--cat-6); }
circle.donut-c-cat-other { stroke:var(--cat-other); } span.donut-sw.donut-c-cat-other { background:var(--cat-other); }
circle.donut-c-green-text { stroke:var(--green-text); } span.donut-sw.donut-c-green-text { background:var(--green-text); }
circle.donut-c-alert-text { stroke:var(--alert-text); } span.donut-sw.donut-c-alert-text { background:var(--alert-text); }
circle.donut-c-warm-text { stroke:var(--warm-text); } span.donut-sw.donut-c-warm-text { background:var(--warm-text); }
circle.donut-c-cold-text { stroke:var(--cold-text); } span.donut-sw.donut-c-cold-text { background:var(--cold-text); }
circle.donut-c-secondary-text { stroke:var(--secondary-text); } span.donut-sw.donut-c-secondary-text { background:var(--secondary-text); }
circle.donut-c-text-2 { stroke:var(--text-2); } span.donut-sw.donut-c-text-2 { background:var(--text-2); }
/* [S55 N4 · DEC-068#8] dark: แดงชิ้นใหญ่บนพื้นมืดใช้ --primary-light (#FF7864 = 6.45:1 บน --surface)
   แทน --primary-solid (#DC3C32 = 3.76:1 ผ่านหวุดหวิด + เพี้ยนใกล้ --cat-5 ใต้ deuteranopia มากกว่า)
   — จับคู่กับ override เดียวกันของ .util-seg--rev/.util-sw--rev ในหน้า presales (สีต้องตรงกัน: M1) */
[data-theme="dark"] circle.donut-c-primary-solid { stroke:var(--primary-light); }
[data-theme="dark"] span.donut-sw.donut-c-primary-solid { background:var(--primary-light); }
/* [S55 H4] hatch = dual-encode ที่ไม่ใช่สี (WCAG 1.4.1) — ใช้กับ segment ที่สีล้วนแยกจากเพื่อนบ้านไม่ได้
   สำหรับตาบอดสี: won #1F7A3D vs lost #C42B21 ระยะห่างหลัง simulate deuteranopia (Machado-2009) = 33.5
   < 35 ที่ DS §1.3 ตั้งเป็น threshold ของตัวเอง + luminance ต่างกันแค่ 1.05:1 (ไม่มี cue ความสว่างช่วย)
   → เติม texture เฉียงตาม pattern เดียวกับ §5 "Slip Segment" ของ DS ที่ใช้ hatch แยกความหมายอยู่แล้ว
   เส้น hatch ใช้ --bg: contrast กับส่วนโค้ง 5.19:1 (light) / 6.63:1 (dark) → เห็นลายชัดทั้ง 2 theme */
.donut-hatch-line { stroke:var(--bg); }
span.donut-sw.donut-sw--hatch { background-image:repeating-linear-gradient(135deg, transparent 0 3px, var(--bg) 3px 5px); }
.donut-center-main { font-family:'Prompt', 'Prompt Fallback', sans-serif; font-weight:600; font-size:22px; fill:var(--text);
  font-variant-numeric:tabular-nums lining-nums; }
/* [S55 M3] เลขกลางยาว (manday ทศนิยม 3 ตำแหน่ง / จำนวนดีล 4 หลัก) ต้องย่อ ไม่ใช่ล้นไปทับวงแหวน
   — ขนาดที่ย่อแล้วยัง ≥11px จริงบนมือถือ (viewBox 120/160 = ×0.75 → 18→13.5px, 15→11.25px) */
.donut-center-main--sm { font-size:18px; }
.donut-center-main--xs { font-size:15px; }
/* [S55 H2/M3] sub-label = HTML ใต้วง ไม่ใช่ <text> ใน SVG — font-size ใน viewBox ถูกย่อตามความกว้างจริง
   ของกราฟ (160→120 = ×0.75 → 10px เหลือ 7.5px จริง) + พื้นที่ในรูโดนัทไม่พอ (ทับเลขกลาง/ล้นขอบวง)
   11px = tier Micro เล็กสุดที่ DS อนุญาต และเป็น px จริงคงที่ทุก breakpoint */
.donut-sub { margin:0; font-size:11px; line-height:16px; color:var(--text-2); text-align:center; max-width:100%; overflow-wrap:anywhere; }
.donut-legend { display:flex; flex-wrap:wrap; justify-content:flex-start; gap:8px 24px; margin:0; font-size:13px; color:var(--text); }
.donut-sw { width:12px; height:12px; border-radius:4px; display:inline-block; margin-right:8px; vertical-align:-1px; flex-shrink:0; }
.donut-lg { display:inline-flex; align-items:center; white-space:nowrap; }
/* [S55 M5] การ์ดกว้าง → วงซ้าย/legend ขวา (เดิม column กลางการ์ดตลอด เหลือที่ว่างข้างละ ~370px)
   ใช้ container query ไม่ใช่ media query เพราะตัวชี้ขาดคือ "ความกว้างการ์ดที่กราฟอยู่" ไม่ใช่ viewport
   (การ์ดเดียวกันอาจอยู่ในคอลัมน์แคบของ dashboard) — §6 DS */
@container donut (min-width:420px) {
  .donut-figure { flex-direction:row; align-items:center; gap:32px; }
  .donut-legend { flex-direction:column; flex-wrap:nowrap; gap:10px; }
}
@container donut (max-width:419px) {
  .donut-lg { white-space:normal; overflow-wrap:anywhere; }
}
@container donut (max-width:340px) {
  .donut-plot { width:120px; }
}

/* =========== product picker /apps (core · PMLabs) =========== */
.apps-page { min-height:100vh; display:flex; flex-direction:column; background:var(--bg); }
.apps-head { display:flex; justify-content:space-between; align-items:center; padding:16px 24px; border-bottom:1px solid var(--border); }
.apps-brand { font-weight:700; font-size:18px; display:inline-flex; align-items:center; gap:8px; color:var(--text); }
.apps-user { display:flex; align-items:center; gap:8px; color:var(--text-2); font-size:13px; }
.apps-user-name { color:var(--text); }
.apps-main { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:40px 24px; text-align:center; }
.apps-title { font-size:26px; font-weight:700; margin:0 0 6px; }
.apps-sub { color:var(--text-2); margin:0 0 32px; }
.apps-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,300px)); gap:20px; }
.app-card { display:flex; flex-direction:column; align-items:flex-start; gap:8px; text-align:left; padding:24px;
  background:var(--surface); border:1px solid var(--border); border-radius:var(--r); box-shadow:var(--shadow-card);
  text-decoration:none; color:var(--text); transition:transform .12s, box-shadow .12s; }
.app-card:hover { transform:translateY(-3px); box-shadow:var(--shadow-dropdown); }
.app-card--locked { opacity:.6; cursor:not-allowed; }
.app-card-ic { width:52px; height:52px; border-radius:14px; display:flex; align-items:center; justify-content:center;
  font-size:26px; color:#fff; }
.app-card-ic--team { background:var(--cat-3); }
.app-card-ic--fin { background:var(--cat-2); }
.app-card-name { font-size:19px; font-weight:700; }
.app-card-lock { font-size:13px; color:var(--text-2); }
.app-card-desc { color:var(--text-2); font-size:13px; line-height:1.5; }
.app-card-go { margin-top:6px; color:var(--primary-solid); font-weight:600; font-size:14px; display:inline-flex; align-items:center; gap:4px; }

@media (max-width:768px) {
  .fin-grid2 { grid-template-columns:1fr; }
  .fin-row { grid-template-columns:1fr; }
  .apps-grid { grid-template-columns:1fr; }
  /* S36 waterfall §4.6: จอแคบ ตัด SVG เต็มรูปแบบออก → mini-KPI stack + delta list แทน */
  .waterfall-chart { display:none; }
  .waterfall-mobile { display:block; }
}

/* ============================================================================
   Scenario / Forecast (what-if) — /fin/scenario (S40 · DEC-091 · เด)
   reuse token ล้วน (ไม่ hardcode สี) · การ์ด chart bg = --surface (วิช: --secondary-end vs --surface-2 dark fail)
   ============================================================================ */
.scn-basebar { display:flex; align-items:center; gap:6px; flex-wrap:wrap; font-size:13px; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r); padding:8px 12px; margin:0 0 14px; }
.scn-basebar b { color:var(--text); font-weight:600; }
.scn-basebar i { color:var(--primary); }

/* cards */
.scn-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(280px,1fr)); gap:14px; align-items:stretch; margin-top:14px; }
.scn-card { display:flex; flex-direction:column; gap:10px; }
.scn-card--baseline { background:var(--surface-2); }
.scn-card-head { display:flex; align-items:center; gap:8px; }
.scn-card-name { font-weight:600; font-size:15px; color:var(--text); flex:1; }
.scn-badge { font-size:10px; font-weight:700; letter-spacing:.06em; color:var(--primary-aa);
  background:var(--primary-light); border-radius:999px; padding:2px 8px; }
.scn-card-menu { display:flex; gap:2px; }
.scn-card-menu .scn-del:hover { color:var(--alert-text); }
.scn-card-sum { font-size:12px; color:var(--text-2); margin:0; }
.scn-headline { display:flex; flex-direction:column; gap:6px; padding:8px 0; border-top:1px solid var(--border); }
.scn-metric { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
.scn-m-label { font-size:12px; color:var(--text-2); min-width:120px; }
.scn-m-val { font-size:15px; font-weight:600; color:var(--text); }
.scn-m-val--warn { color:var(--alert-text); }
.scn-card-cta { margin-top:auto; justify-content:center; }
.scn-card-open { margin-left:auto; }

/* Δ diff (dual-encode: เครื่องหมาย+สี+คำ) */
.scn-diff { display:inline-flex; align-items:baseline; gap:4px; font-size:12px; font-weight:600; font-variant-numeric:tabular-nums; }
.scn-diff--profit { color:var(--green-text); }
.scn-diff--loss { color:var(--alert-text); }
.scn-diff--zero { color:var(--text-3); }
.scn-diff-dir { font-size:10px; font-weight:500; opacity:.8; }

/* no-baseline checklist */
.scn-checklist { list-style:none; margin:12px 0 0; padding:0; display:flex; flex-direction:column; gap:8px; }
.scn-check { display:flex; align-items:center; gap:8px; font-size:14px; }
.scn-check i { width:20px; text-align:center; }
.scn-check--ok i { color:var(--green-text); }
.scn-check--miss i { color:var(--alert-text); }
.scn-check a { margin-left:auto; font-size:13px; }

/* dialog */
.scn-dlg { border:none; border-radius:var(--r); padding:20px; max-width:420px; width:92%; background:var(--surface); color:var(--text); }
.scn-dlg-title { margin:0 0 14px; font-size:16px; }
.scn-dlg-actions { display:flex; justify-content:flex-end; gap:8px; margin-top:16px; }

/* editor 2-panel */
.scn-edit-top { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:14px; }
.scn-edit-name { font-weight:600; font-size:16px; }
.scn-ro-note { display:inline-flex; align-items:center; gap:4px; }
.scn-name-input { max-width:260px; font-weight:600; }
.scn-recalc { display:none; align-items:center; gap:5px; font-size:12px; color:var(--text-2); margin-left:auto; }
.scn-recalc.htmx-request { display:inline-flex; }
.scn-save { margin-left:8px; }
.scn-panels { display:flex; gap:18px; align-items:flex-start; }
.scn-assume { flex:0 0 340px; display:flex; flex-direction:column; gap:14px; position:sticky; top:12px; }
.scn-result { flex:1; min-width:0; display:flex; flex-direction:column; gap:14px; }
.scn-hz-row { display:flex; align-items:center; gap:8px; }
.scn-hz { max-width:130px; }
.scn-group { border:1px solid var(--border); border-radius:var(--r); padding:12px; margin:0; }
.scn-group-title { font-size:13px; font-weight:600; color:var(--text); padding:0 4px; display:inline-flex; align-items:center; gap:6px; }
.scn-group-title i { color:var(--primary); }
.scn-driver { display:flex; flex-direction:column; gap:4px; margin-top:10px; }
.scn-stepper { display:flex; align-items:center; gap:6px; }
.scn-stepper .iconbtn { flex:0 0 auto; }
.scn-num { flex:1; min-width:0; text-align:right; }
.scn-unit { font-size:13px; color:var(--text-2); }
.scn-ref { font-size:11px; color:var(--text-3); }

/* KPI */
.scn-kpi { margin:0; }

/* Projection Trend chart */
.scn-trend-card { background:var(--surface); }
.scn-legend { display:flex; gap:16px; flex-wrap:wrap; font-size:12px; color:var(--text-2); margin-bottom:10px; }
.scn-lg { display:inline-flex; align-items:center; gap:5px; }
.scn-trend-grid { display:grid; grid-template-columns:repeat(2, 1fr); gap:14px; }
.scn-trend { margin:0; min-width:0; }
.scn-trend-title { font-size:13px; font-weight:600; color:var(--text); margin-bottom:4px; }
.scn-trend-svg { width:100%; height:auto; display:block; }
.trend-line--actual { fill:none; stroke:var(--secondary-end); stroke-width:2; }
.trend-line--base-forecast { fill:none; stroke:var(--secondary-end); stroke-width:2; stroke-dasharray:6 4; }
.trend-line--scenario { fill:none; stroke:var(--cat-1); stroke-width:2; stroke-dasharray:6 4; }
.trend-marker { stroke:var(--border-control); stroke-width:1.5; stroke-dasharray:2 3; }
.trend-zero { stroke:var(--border-control); stroke-width:1; }
.trend-anchor-dot { fill:var(--secondary-end); }
.trend-runway-dot { fill:var(--cat-1); }
.trend-runway-lbl { fill:var(--alert-text); font-size:9px; font-weight:600; }
.trend-end-lbl { font-size:9px; font-weight:600; }
.trend-end-lbl--base { fill:var(--secondary-end); }
.trend-end-lbl--scn { fill:var(--cat-1); }
.scn-trend-tbl { margin-top:6px; font-size:12px; }
.scn-trend-tbl summary { cursor:pointer; color:var(--text-2); font-size:12px; }

/* P&L compare + disclaimer */
.scn-pl-compare .tbl-th { white-space:nowrap; }
.scn-disclaimer { margin-top:12px; display:flex; gap:6px; align-items:flex-start; }

/* toast */
.scn-toast { position:fixed; bottom:24px; left:50%; transform:translateX(-50%) translateY(20px); opacity:0;
  background:var(--text); color:var(--surface); padding:10px 18px; border-radius:999px; font-size:14px;
  display:flex; align-items:center; gap:6px; transition:opacity .2s, transform .2s; z-index:200; pointer-events:none; }
.scn-toast--show { opacity:1; transform:translateX(-50%) translateY(0); }

/* responsive: tablet/mobile — panel ซ้อน + chart 1 คอลัมน์ */
@media (max-width: 900px) {
  .scn-panels { flex-direction:column; }
  .scn-assume { position:static; flex:1 1 auto; width:100%; }
  .scn-trend-grid { grid-template-columns:1fr; }
}

/* ============================================================================
   S41 — Quick-Add Master Card Template (DEC-100) · Wegital DS · light+dark ผ่าน token
   ============================================================================ */
.card-grid--2 { grid-template-columns:repeat(2,1fr); }

/* ปุ่ม quick-add (icon-only) ข้างปุ่มเพิ่มการ์ด */
.add-card-group { display:inline-flex; align-items:center; gap:8px; }
.iconbtn--qa {
  width:40px; height:40px; border-radius:10px;
  border:1px solid var(--border); background:var(--surface); color:var(--text-2);
}
.iconbtn--qa:hover { background:var(--surface-2); color:var(--primary-solid); box-shadow:0 0 0 1px var(--border); }
.iconbtn--qa::after { inset:-2px; }   /* 40+4 = 44px hit-area */

/* หน้า Manage list */
.tpl-wrap { margin-top:4px; }
.tpl-list { display:flex; flex-direction:column; gap:12px; }
.tpl-card { border:1px solid var(--border); border-radius:12px; background:var(--surface); padding:14px 16px; }
.tpl-card-top { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.tpl-card-name { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.tpl-name { font-weight:600; color:var(--text); overflow-wrap:anywhere; }
.tpl-prefix-badge { font-size:var(--fs-sm); padding:2px 8px; border-radius:999px; background:var(--surface-2); color:var(--text-2); }
.tpl-scope-badge { font-size:var(--fs-sm); padding:2px 8px; border-radius:999px; background:var(--surface-2); color:var(--text-2); }
.tpl-warn-badge { font-size:var(--fs-sm); padding:2px 8px; border-radius:999px; background:var(--warm); color:var(--warm-text); display:inline-flex; align-items:center; gap:4px; }
.tpl-card-act { display:flex; gap:4px; flex-shrink:0; }
.tpl-chips { display:flex; flex-wrap:wrap; gap:6px; margin-top:10px; }
.tpl-chip { font-size:var(--fs-sm); padding:3px 10px; border-radius:8px; background:var(--surface-2); color:var(--text); display:inline-flex; align-items:center; gap:6px; }
.tpl-chip-k { color:var(--text-2); font-size:calc(var(--fs-sm) - 1px); }
.tpl-chip--warn { background:var(--warm); color:var(--warm-text); }
.tpl-empty { display:flex; flex-direction:column; align-items:center; gap:10px; padding:48px 16px; color:var(--text-2); text-align:center; }
.tpl-empty > .fi { font-size:36px; opacity:.6; }
.tpl-empty p { margin:0; font-weight:600; color:var(--text); }
.tpl-empty-sub { font-size:var(--fs-sm); }

/* manage form */
.tpl-name-hint { font-size:var(--fs-sm); color:var(--text-2); margin:-4px 0 8px; }
.tpl-textarea {
  width:100%; box-sizing:border-box; padding:10px 12px; border:1px solid var(--border-control);
  border-radius:10px; background:var(--surface); color:var(--text); font:inherit; resize:vertical; min-height:56px;
}
.tpl-textarea:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; border-color:var(--primary-solid); }
.tpl-chk-rows { display:flex; flex-direction:column; gap:8px; margin-bottom:8px; }
.tpl-chk-row { display:flex; align-items:center; gap:8px; }
.tpl-chk-row .input-wrap { flex:1 1 auto; }

/* quick-add สเต็ป B */
.qa-head-act { display:flex; align-items:center; gap:8px; }
.tpl-titlewrap { display:flex; align-items:stretch; border:1px solid var(--border-control); border-radius:10px; overflow:hidden; background:var(--surface); }
.tpl-titlewrap:focus-within { outline:2px solid var(--primary-solid); outline-offset:2px; border-color:var(--primary-solid); }
.tpl-prefix-adorn { display:flex; align-items:center; padding:0 12px; background:var(--surface-2); color:var(--text-2); font-weight:600; white-space:nowrap; border-right:1px solid var(--border-control); }
.tpl-titlewrap input { flex:1 1 auto; border:none; background:none; padding:10px 12px; color:var(--text); font:inherit; outline:none; min-width:0; }
.tpl-lockchip { display:inline-flex; align-items:center; gap:8px; padding:8px 12px; border-radius:10px; background:var(--surface-2); color:var(--text); }
.tpl-lockchip .fi { color:var(--text-2); }
.tpl-lockchip-hint { font-size:var(--fs-sm); color:var(--text-2); }
.tpl-section-label { font-weight:600; color:var(--text-2); font-size:var(--fs-sm); margin:14px 0 8px; }
.tpl-preview { border:1px solid var(--border); border-radius:12px; background:var(--surface-2); padding:12px 14px; margin-top:14px; }
.tpl-preview-head { display:flex; align-items:center; gap:8px; font-weight:600; color:var(--text); font-size:var(--fs-sm); }
.tpl-preview-note { font-weight:400; color:var(--text-2); }
.tpl-preview .tpl-chip { background:var(--surface); }
.tpl-warnbox { display:flex; align-items:center; gap:10px; padding:10px 14px; border-radius:10px; background:var(--warm); color:var(--warm-text); margin-bottom:12px; }
.tpl-warnbox .fi { flex-shrink:0; }

/* quick-add สเต็ป A: pick list */
.qa-picklist { display:flex; flex-direction:column; gap:10px; }
.qa-pick { position:relative; text-align:left; border:1px solid var(--border); border-radius:12px; background:var(--surface); padding:14px 40px 14px 16px; cursor:pointer; width:100%; }
.qa-pick:hover { background:var(--surface-2); }
.qa-pick:focus-visible { outline:none; border-color:var(--primary-solid); box-shadow:0 0 0 1px var(--primary-solid); }
.qa-pick-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.qa-pick-name { font-weight:600; color:var(--text); }
.qa-pick-go { position:absolute; right:14px; top:50%; transform:translateY(-50%); color:var(--text-2); }

/* ============================================================================
   S42 — Baseline vs Actual + Delay Defense (Wegital DS §3.0/§5)
   reuse tokens ทั้งหมด (--cat-*/--lb-*-solid/--alert-text/--border-control/--shadow-tab)
   DS class: .stat-value .tl-bar-baseline .tl-bar-slip .tl-datadate .tl-attr (คัดจาก SKILL.md)
   product layout: .dfz-* (self-contained defense Gantt %-track — ไม่แตะ shared timeline grid)
   ============================================================================ */

/* §3.0 Display/Stat tier — hero KPI number
   S42 follow-up (ซี): apply เพิ่มที่ ".kpi-strip ที่มี .kpi-tile เดี่ยวโดด" (solo hero card ไม่ใช่กริดหลายใบ)
   — เลือกใช้ selector โครงสร้าง :only-child แทนการเติม class="stat-value" ตรง ๆ ในเทมเพลต เพราะรอบนี้
   ซี แตะเฉพาะ app.css (โด ถือ templates ไม่ชน) · เจอจริง 1 จุดที่ .kpi-strip มี .kpi-tile เดียว = /fin/expense
   (ค่าใช้จ่ายรวมงวดนี้) + project_hub.html เมื่อ !.IsAdmin (เห็นแค่การ์ด "ความคืบหน้า" ใบเดียว) — ที่เหลือ
   (.kpi-strip ทุกกริดหลายใบ: cost/budget/dashboard/mrr/runway ฯลฯ) ยังคง .kpi-value เดิม (20px) ไม่แตะ */
.stat-value, .kpi-strip > .kpi-tile:only-child .kpi-value{
  font-size:44px; font-weight:700; color:var(--text); font-variant-numeric:tabular-nums lining-nums;
  line-height:1.1; letter-spacing:-0.01em; }
.stat-sub, .kpi-strip > .kpi-tile:only-child .kpi-sub{ font-size:13px; color:var(--text-2); margin-top:4px; }

/* §5 Baseline Ghost Bar */
.tl-bar-baseline{
  position:absolute; z-index:0; height:12px; align-self:flex-start; margin-top:8px;
  border-radius:6px; pointer-events:none;
  background:color-mix(in srgb, var(--text-2) 30%, var(--surface));
  border:1px dashed var(--border-control);
}
/* §5 Slip Segment (hatch 135° alert) */
.tl-bar-slip{
  position:relative; z-index:1; height:28px; margin:4px 0; border-radius:0 8px 8px 0;
  border:1px solid var(--alert-text); border-left:none;
  background:repeating-linear-gradient(135deg,
    var(--alert-text) 0 3px, var(--alert-bg) 3px 6px);
}
/* §5 Data Date Line (halo/casing กันจมเมื่อพาดแท่งสีเข้ม) */
.tl-datadate{
  position:absolute; top:0; bottom:0; width:0; z-index:3; pointer-events:none;
  border-left:2px dashed var(--alert-text);
  box-shadow:-1px 0 0 var(--surface), 1px 0 0 var(--surface);
}
.tl-datadate-flag{
  position:absolute; top:-22px; left:0; transform:translateX(-50%); white-space:nowrap;
  background:var(--alert-text); color:var(--surface); font-size:10px; font-weight:600;
  padding:2px 6px; border-radius:4px;
}
/* §5 Attribution Stacked Bar (reuse --cat-1/3/6 · Client→Internal→Third) */
.tl-attr{ display:flex; height:16px; border-radius:999px; overflow:hidden; background:var(--surface-2); }
.tl-attr-seg{ height:100%; }
.tl-attr-seg--client{ background:var(--cat-3); }
.tl-attr-seg--internal{ background:var(--cat-1); }
.tl-attr-seg--third{ background:var(--cat-6); }
.tl-attr-seg--force{ background:var(--cat-5); }
.tl-attr-legend{ display:flex; flex-wrap:wrap; gap:12px 18px; margin-top:10px; }
.tl-attr-leg{ display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--text-2); overflow-wrap:anywhere; }
.tl-attr-sw{ width:12px; height:12px; border-radius:4px; flex-shrink:0; }
.tl-attr-sw--client{ background:var(--cat-3); }
.tl-attr-sw--internal{ background:var(--cat-1); }
.tl-attr-sw--third{ background:var(--cat-6); }
.tl-attr-sw--force{ background:var(--cat-5); }

/* ---- product layout: self-contained defense Gantt (%-track, ไม่แตะ shared .tl-grid) ----
   [S42 Wave2 fix — ซี รุมเทส responsive 320px] เดิมไม่มี overflow-x/sticky label เลย ต่างจาก
   pattern .tl-scroll/.tl-name ที่ Timeline หน้าอื่นใช้ (proven มือถือ) → ที่ label-w 200px คงที่ +
   1fr เดียว บนจอ 320px เหลือพื้นที่ track ~70-90px บีบทั้งช่วงวัน (baseline/actual/slip/data-date)
   จนอ่านไม่ออก (regression เทียบ pattern เดิม) — ใส่ --dfz-track-min กัน track แคบเกิน + overflow-x
   scroll แนวนอนแทนบีบ (เหมือน .tl-scroll) + sticky label column (เหมือน .tl-name) กัน label หลุดจอ
   ตอนเลื่อน */
.dfz-gantt{ --dfz-label-w:160px; --dfz-track-min:420px; margin-top:8px; overflow-x:auto; }
.dfz-axis{ display:grid; grid-template-columns:var(--dfz-label-w) minmax(var(--dfz-track-min),1fr);
  align-items:end; font-size:11px; color:var(--text-2); margin-bottom:6px;
  min-width:calc(var(--dfz-label-w) + var(--dfz-track-min)); }
.dfz-axis > div:first-child{ position:sticky; left:0; z-index:3; background:var(--surface); }
.dfz-axis-ticks{ position:relative; height:16px; }
.dfz-axis-tick{ position:absolute; transform:translateX(-50%); white-space:nowrap; }
.dfz-plot{ position:relative; min-width:calc(var(--dfz-label-w) + var(--dfz-track-min)); }
.dfz-datedate-region{ position:absolute; left:var(--dfz-label-w); right:8px; top:0; bottom:0; pointer-events:none; z-index:2; }
.dfz-row{ display:grid; grid-template-columns:var(--dfz-label-w) minmax(var(--dfz-track-min),1fr);
  align-items:center; min-height:38px; border-top:1px solid var(--border); }
.dfz-rowlabel{ position:sticky; left:0; z-index:3; display:flex; align-items:center; gap:8px;
  padding-right:12px; min-width:0; font-size:13px; color:var(--text); overflow-wrap:anywhere;
  background:var(--surface); border-right:1px solid var(--border); }
.dfz-rowlabel .dfz-rowtitle{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dfz-track{ position:relative; height:28px; }
.dfz-track .tl-bar{ position:absolute; top:0; margin:0; box-shadow:var(--shadow-tab); max-width:none; }
.dfz-track .tl-bar-slip{ position:absolute; top:0; margin:0; }
.dfz-slipnote{ font-size:11px; color:var(--alert-text); font-weight:600; margin-left:6px; }
.dfz-empty{ padding:24px; text-align:center; color:var(--text-2); }

/* baseline version badge (reuse chip look) */
.dfz-basebadge{ display:inline-flex; align-items:center; gap:6px; font-size:13px; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border); border-radius:999px; padding:4px 12px; }

/* ---- S42 Defense page layout (product tier) ---- */
.dfz-slip-neg{ color:var(--alert-text); }
.dfz-slip-pos{ color:var(--green-text); }
.dfz-head-row{ display:flex; align-items:flex-start; justify-content:space-between; gap:16px; flex-wrap:wrap; }
.dfz-head-actions{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.dfz-h-sub{ font-weight:400; color:var(--text-2); font-size:var(--fs-sm); }
.dfz-card-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-bottom:8px; }

/* summary block: stat + reconcile */
.dfz-summary{ display:flex; align-items:center; gap:32px; flex-wrap:wrap; }
.dfz-stat{ min-width:180px; }
.dfz-reconcile{ display:flex; gap:24px; flex-wrap:wrap; }
.dfz-recon-item{ display:flex; flex-direction:column; gap:2px; }
.dfz-recon-lab{ font-size:12px; color:var(--text-2); overflow-wrap:anywhere; }
.dfz-recon-val{ font-size:18px; font-weight:600; color:var(--text); overflow-wrap:anywhere; }
/* S44 E0 รุมเทส slip-unit interim label (เต๋/บี finding — business ตัดสินหน่วยจริงรอพีเคาะแยก):
   .stat-value ด้านบน (working-day slip, computed) กับ .dfz-recon-val "อธิบายได้" (impact_days ที่ user
   กรอกเป็นวันปฏิทิน) เขียน "วัน" เหมือนกันทั้งคู่ทั้งที่คนละหน่วย → ต่อ unit เล็กกว่าตัวเลขหลัก (reuse
   สัดส่วน hero-number+small-suffix เดียวกับ money-format §3.2 "1.2M THB") กำกับให้ชัดว่าคนละมาตร โดยไม่
   เปลี่ยนตัวเลข/logic ใด ๆ — แค่ label ชัดขึ้น ไม่สร้าง token สีใหม่ (ใช้ --text-2 เดิม) */
.dfz-unit{ font-size:var(--fs-micro); font-weight:500; color:var(--text-2); margin-left:4px; }
.dfz-attr-block{ margin-top:20px; }

/* gantt legend key */
.dfz-ganttkey{ display:flex; gap:16px; flex-wrap:wrap; }
.dfz-key{ display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2); }
.dfz-key-ghost{ width:20px; height:10px; border-radius:5px; background:color-mix(in srgb, var(--text-2) 30%, var(--surface)); border:1px dashed var(--border-control); }
.dfz-key-actual{ width:20px; height:12px; border-radius:6px; background:var(--lb-teal); } /* [S42 Wave2 fix — ซี] เดิม var(--lb-blue-solid,...) — --lb-blue-solid ไม่มีจริงใน token (มีแค่ red/brown/emerald -solid) ใช้ fallback --cat-3 เงียบ ๆ เสมอ ไม่ผิดแต่งงตอนอ่าน token — เปลี่ยนอ้างอิงตรงเป็น --lb-teal (สีตัวอย่างแท่ง actual ทั่วไปใน mockup ไม่ผูก status จริง) กันสับสน */
.dfz-key-slip{ width:20px; height:12px; border-radius:0 6px 6px 0; border:1px solid var(--alert-text); border-left:none;
  background:repeating-linear-gradient(135deg, var(--alert-text) 0 3px, var(--alert-bg) 3px 6px); }

/* delay form */
.dfz-form{ border:1px solid var(--border); border-radius:12px; background:var(--surface-2); padding:16px; margin-bottom:16px; }
.dfz-form-grid{ display:grid; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)); gap:12px 16px; }
.dfz-field-wide{ grid-column:1/-1; }
.dfz-form-foot{ margin-top:12px; }
.dfz-input{ width:100%; border:1px solid var(--border-control); border-radius:10px; background:var(--surface);
  color:var(--text); font:inherit; padding:9px 12px; outline:none; }
.dfz-input:focus-visible{ outline:2px solid var(--primary-solid); outline-offset:2px; }
.dfz-input::placeholder{ color:var(--text-2); }

/* events list */
.dfz-events{ display:flex; flex-direction:column; }
.dfz-event{ display:grid; grid-template-columns:96px 1fr auto; gap:12px; align-items:start;
  padding:14px 0; border-top:1px solid var(--border); }
.dfz-event-date{ font-size:13px; color:var(--text-2); padding-top:2px; }
.dfz-event-body{ min-width:0; }
.dfz-event-head{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.dfz-event-title{ font-weight:600; color:var(--text); overflow-wrap:anywhere; }
.dfz-event-impact{ font-weight:600; color:var(--alert-text); }
.dfz-event-note{ font-size:13px; color:var(--text); margin-top:4px; overflow-wrap:anywhere; }
.dfz-event-meta{ font-size:12px; color:var(--text-2); margin-top:6px; display:flex; gap:4px; flex-wrap:wrap; align-items:center; }
.dfz-event-meta a{ color:var(--secondary-text); text-decoration:none; }

/* baseline versions */
.dfz-sets{ display:flex; flex-direction:column; }
.dfz-set{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
  padding:12px 0; border-top:1px solid var(--border); }
.dfz-set-info{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; overflow-wrap:anywhere; }
.dfz-set-actions{ display:flex; align-items:center; gap:8px; }

/* print: report-only view */
@media print{
  .no-print{ display:none !important; }
  .topbar, .drawer, .app-drawer, nav{ display:none !important; }
  .app-layout{ display:block !important; }
  .page{ padding:0 !important; }
  .hub-card{ break-inside:avoid; box-shadow:none; }
  /* [S42 Wave2 fix — ซี] .dfz-gantt overflow-x:auto (มือถือ) ต้องคืนเป็น visible ตอนพิมพ์ —
     ไม่งั้น browser print เอนจิน (ไม่มี scrollbar ในหน้ากระดาษ) จะ "ตัด" ส่วนที่เกิน viewport ทิ้ง
     Gantt ยาวเกิน 1 หน้าจะหายไปครึ่งนึงตอน print-to-PDF/Excel export ไม่ครบ */
  .dfz-gantt{ overflow-x:visible; }
  .dfz-rowlabel, .dfz-axis > div:first-child{ position:static; }
}

/* ============================================================
   Planning-Flow (S43 · E2 convert-flow) — เด · ยึด Wegital DS (var-based, light+dark)
   ============================================================ */
.pf-struct{display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;margin-bottom:16px}
.pf-struct .lv{background:var(--surface-2);border:1px solid var(--border);border-radius:8px;padding:6px 10px;color:var(--text-2)}
.pf-struct .lv.cur{border-color:var(--primary-solid);color:var(--text);font-weight:600;background:var(--surface)}
.pf-struct .sep{color:var(--text-2)}

.pf-state{display:flex;align-items:center;gap:6px;font-size:12px;flex-wrap:wrap;margin-bottom:16px}
.pf-state .s{padding:6px 12px;border-radius:999px;border:1px solid var(--border-control);color:var(--text-2);min-height:32px;display:inline-flex;align-items:center;gap:6px}
.pf-state .s.done{color:var(--green-text);border-color:var(--green-text)}
.pf-state .s.now{background:var(--alert-bg);color:var(--alert-text);border-color:var(--alert-text);font-weight:600}
.pf-state .sep{color:var(--text-2)}

.pf-table-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:12px}
.pf-table{width:100%;min-width:560px;border-collapse:collapse;font-size:13px}
.pf-table th{background:var(--surface-2);text-align:left;font-size:11px;font-weight:600;color:var(--text-2);padding:10px 12px;border-bottom:1px solid var(--border);white-space:nowrap}
.pf-table td{padding:10px 12px;border-bottom:1px solid var(--border);vertical-align:middle;overflow-wrap:anywhere}
.pf-table tr:last-child td{border-bottom:none}
.pf-table .c-num{text-align:right;white-space:nowrap}
.pf-total{display:flex;align-items:center;gap:8px;font-size:13px}
.pf-total b{font-size:18px}

.pf-est3{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}
.pf-est3 .box{border:1px solid var(--border);border-radius:12px;padding:12px;background:var(--surface)}
.pf-est3 .box.locked{background:var(--surface-2)}
.pf-est3 .box .lb{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;color:var(--text-2)}
.pf-est3 .box .v{font-size:26px;font-weight:700;margin-top:2px}
.pf-est3 .box .u{font-size:12px;color:var(--text-2)}
@media (max-width:640px){.pf-est3{grid-template-columns:1fr}}

.pf-exec-row{display:flex;justify-content:space-between;align-items:center;gap:12px;border:1px solid var(--border);border-radius:12px;padding:12px;background:var(--surface)}
.pf-exec-row .meta{font-size:12px;color:var(--text-2);margin-top:2px}

/* proposal/origin badge variants (reuse .badge base) */
.badge.plan-draft{background:var(--surface-2);color:var(--text-2);border:1px solid var(--border-control)}
.badge.plan-sent{background:color-mix(in srgb,var(--cat-3) 18%,var(--surface));color:var(--cold-text)}
.badge.plan-locked{background:color-mix(in srgb,var(--primary-solid) 14%,var(--surface));color:var(--alert-text)}
.badge.plan-planned{background:color-mix(in srgb,var(--cat-3) 18%,var(--surface));color:var(--cold-text)}
.badge.plan-unplanned{background:color-mix(in srgb,var(--cat-6) 16%,var(--surface));color:var(--warm-text)} /* F3 (เอ): 22%→16% = 4.81:1 ผ่าน AA */
.badge.plan-adhoc{background:color-mix(in srgb,var(--secondary-end) 16%,var(--surface));color:var(--secondary-text)}

/* planning form controls (reuse DS · ไม่ native) */
.pf-scopelist .page-title,.page-title{font-size:24px;font-weight:600;margin:8px 0 2px}
.input{width:100%;font-family:inherit;font-size:14px;color:var(--text);background:var(--surface);border:1px solid var(--border-control);border-radius:10px;padding:10px 12px;min-height:44px}
.input:focus{outline:none;border-color:var(--primary-solid);box-shadow:0 0 0 3px rgba(220,60,50,.10)}
.input::placeholder{color:var(--text-2);opacity:.7}
.pf-radio-card{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--border-control);border-radius:10px;padding:10px 14px;font-size:13px;cursor:pointer;min-height:44px;background:var(--surface);color:var(--text)}
.pf-radio-card:has(input:checked){border-color:var(--primary-solid);background:color-mix(in srgb,var(--primary-solid) 8%,var(--surface));font-weight:600}
.pf-radio-card:focus-within{outline:2px solid var(--primary-solid);outline-offset:2px}
/* a11y (เต๋ BLOCKER): radio ต้อง focusable ด้วย keyboard — visually-hidden ไม่ใช่ display:none/hidden (WCAG 2.1.1) */
.pf-sr-input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none;margin:0}
/* spacing (พี UAT): gap scale 8 · action buttons แยก section ชัด ไม่ชน add-form · even-pixel */
.mt24{margin-top:24px}
.pf-addform{margin-top:24px;padding:20px}
.pf-actions{margin-top:24px;padding-top:20px;border-top:1px solid var(--border);display:flex;flex-wrap:wrap;gap:12px;justify-content:flex-end;align-items:center}
.pf-addform .pf-addrow{display:flex;flex-wrap:wrap;gap:16px;align-items:flex-end}

/* ── planning redesign (S43 · ยู spec: mini-table + stepper + focus) — var-based, Even-Pixel ── */
.pf-stepper{display:flex;align-items:center;margin-bottom:12px;flex-wrap:wrap;gap:6px 0}
.pf-step{display:flex;align-items:center;gap:8px}
.pf-step .pf-dot{width:32px;height:32px;flex:0 0 32px;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--border-control);background:var(--surface-2);color:var(--text-2);font-size:13px}
.pf-step .pf-slabel{display:flex;flex-direction:column;font-size:12px;font-weight:600;line-height:1.2}
.pf-step .pf-slabel small{font-weight:400;color:var(--text-2);font-size:11px}
.pf-step.done .pf-dot{background:var(--primary-aa);border-color:var(--primary-aa);color:#fff}
.pf-step.now .pf-dot{background:var(--surface);border:2px solid var(--primary-solid);color:var(--primary-solid);box-shadow:0 0 0 4px rgba(220,60,50,.12)}
.pf-line{flex:1;min-width:24px;height:2px;background:var(--border);margin:0 8px}
.pf-line.on{background:var(--primary-aa)}
.pf-guide{font-size:13px;color:var(--text);background:var(--surface-2);border:1px solid var(--border);border-radius:10px;padding:10px 14px;margin-bottom:24px;display:flex;align-items:center;gap:8px;overflow-wrap:anywhere}
.pf-guide i{color:var(--primary-solid);flex-shrink:0}
.pf-freeze{background:var(--alert-bg);border:1px solid var(--alert-text);border-radius:12px;padding:12px 14px;margin-bottom:16px;color:var(--alert-text);font-size:13px}
.pf-empty{text-align:center;padding:28px; display:none} /* S54: empty-state render เสมอ · display คุมด้วย :has() ด้านล่าง (แก้ empty-state ค้างตอน add การ์ดใบแรกผ่าน htmx partial) */
.pf-cardsarea:not(:has(#pf-tbody > tr:not(.pf-qadd))) .pf-empty{display:block} /* โชว์เฉพาะเมื่อ tbody ไม่มีแถวข้อมูล (เหลือแค่แถว pf-qadd) — reactive ทั้ง add/delete */
/* DEC-105 M1: paste sheet + convert picker (standalone <dialog> reuse .modal-box) */
.pf-dlg{padding:0;border:none;margin:auto;background:transparent;max-width:none;max-height:none;color:inherit}
.pf-dlg::backdrop{background:rgba(0,0,0,.4)}
.est-paste-count{font-size:13px;color:var(--text-2);display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.est-paste-count b{color:var(--text)}
.pf-paste-warn{color:var(--warn-text,var(--warm-text));font-weight:500}
.est-paste-preview{list-style:none;margin:0;padding:10px 12px;background:var(--surface-2);border:1px solid var(--border);border-radius:12px;max-height:180px;overflow-y:auto;font-size:13px;display:flex;flex-direction:column;gap:4px}
.est-paste-preview li{overflow-wrap:anywhere}
.est-paste-preview li.muted{color:var(--text-2);font-size:12px}
/* mini-table: grouped 2-row header + sticky first col/header + airier cells */
.pf-minitable{border-collapse:separate;border-spacing:0;min-width:1160px}
.pf-minitable td,.pf-minitable th{padding:10px 14px;border-bottom:1px solid var(--border)}
.pf-minitable thead th{position:sticky;top:0;z-index:2;background:var(--surface-2)}
.pf-minitable .pf-group{font-size:12px;font-weight:600;text-align:center;color:var(--text);padding:12px 14px}
.pf-minitable .pf-tier{font-size:11px;font-weight:600;text-transform:uppercase;color:var(--text-2);text-align:center}
.pf-minitable .col-name{position:sticky;left:0;z-index:3;background:var(--surface);min-width:200px;font-weight:500}
.pf-minitable thead .col-name{z-index:4;background:var(--surface-2)}
.pf-minitable tbody tr td{min-height:48px}
.pf-minitable .pf-cell{text-align:center;min-width:112px}
.pf-gsep{border-left:1px solid var(--border)}
.pf-rowmenu{width:44px}
.pf-qform{display:inline-flex;gap:4px;align-items:center;justify-content:flex-end}
.pf-qinput{width:72px;min-height:36px;padding:6px 8px;text-align:right}
.pf-qadd td{background:var(--surface-2)}
.pf-qadd i{color:var(--text-2);margin-right:8px}
.pf-qadd-input{border:none;background:none;font-family:inherit;font-size:13px;color:var(--text);width:100%;min-height:36px;outline:none}
.pf-qadd-input:focus{outline:none;box-shadow:inset 0 0 0 2px var(--primary-solid);border-radius:8px;background:var(--surface)} /* F1 (เอ SC 2.4.7): focus indicator แทน outline:none */
/* touch target (เต๋#3/เอ G9): dd-cell trigger ≥40px scoped pf-minitable (ไม่กระทบตารางอื่น) */
.pf-minitable .dd-cellbtn{min-height:40px}
/* N1 bulk bar (mockup จอ1) — โผล่เมื่อเลือก ≥1 */
.pf-bulkbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;background:color-mix(in srgb,var(--primary-solid) 8%,var(--surface));border:1px solid var(--primary-solid);border-radius:10px;padding:8px 12px;margin-bottom:16px}
.pf-bulkbar[hidden]{display:none} /* เต๋ fix: [hidden] ชนะ display:flex (bulk bar ซ่อนตอนเลือก 0) */
.pf-bulkbar .bulk-count{font-weight:600;font-size:13px}
.pf-minitable .pf-chk{width:44px;text-align:center}
/* N3: red-highlight cell ว่าง (dual-encode: alert-bg + border alert-text · default off ต่อ field) */
.pf-minitable .pf-cell-empty{background:var(--alert-bg);border-radius:8px}
.pf-minitable .pf-cell-empty .dd-cellbtn{border-color:var(--alert-text)}
.pf-minitable .pf-cell-empty .dd-label .tk-muted{color:var(--alert-text)}
/* N3 → ตารางหลัก WeTeam (S44): pf-cell-empty อยู่บน <td class="tbl-td"> (control ภายใน bg=transparent → เห็นทะลุ)
   hover-selector ต้องมี เพื่อชนะ .tbl-row:hover .tbl-td (0,4,0 > 0,3,0) — ไม่งั้น bg แดงหายตอน hover · td ไม่ใส่ radius (cell เต็ม, border ระหว่าง cell) */
.tbl-td.pf-cell-empty,
.tbl-row:hover .tbl-td.pf-cell-empty{background:var(--alert-bg)}
.tbl-td.pf-cell-empty .dd-cellbtn{border-color:var(--alert-text)}
.tbl-td.pf-cell-empty .tk-muted{color:var(--alert-text)}
/* sr-only (a11y) */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* mobile stacked-card (<768 · spec §3.3/§11 · เต๋#2/เอ G11): ตาราง 8-col → card ต่อ planning card */
@media (max-width:767px){
  .pf-minitable{min-width:0}
  .pf-minitable thead{display:none}
  .pf-minitable tbody,.pf-minitable tr,.pf-minitable td{display:block;width:auto}
  .pf-minitable tr{border:1px solid var(--border);border-radius:12px;margin:0 0 12px;padding:12px;background:var(--surface)}
  .pf-minitable tr.pf-qadd{padding:8px 12px}
  .pf-minitable td{border:none;padding:6px 0;min-height:0}
  .pf-minitable .col-name{position:static;font-size:15px;font-weight:600;border-bottom:1px solid var(--border);padding-bottom:8px;margin-bottom:6px}
  .pf-minitable .pf-cell{display:flex;justify-content:space-between;align-items:center;gap:12px}
  .pf-minitable .pf-cell::before{content:attr(data-label);font-size:12px;color:var(--text-2);font-weight:600;flex-shrink:0}
  .pf-minitable .c-num{display:flex;justify-content:flex-end;gap:8px}
  .pf-gsep{border-left:none}
  .pf-table-wrap{overflow-x:visible;border:none}
}
.pf-qadd-input::placeholder{color:var(--text-2)}
/* focus shell (Q-D standalone) — เต๋#4: topbar trim = mark+Share(page)+avatar · ซ่อน nav/switcher (ไม่แตะ topbar shared HTML) */
.page--focus{padding:32px;max-width:1280px;margin:0 auto;width:100%}
.shell--focus .topbar-nav,.shell--focus .module-switcher,.shell--focus .topnav-toggle,.shell--focus .ms-divider,.shell--focus .topbar-menu{display:none}
.pf-share{display:inline-flex;align-items:center;gap:6px}
/* เต๋ blocker fix: tablet only (768-1023) — mobile ≤767 กลับ stacked-card (rule ล่าง) ไม่ทับ */
@media (min-width:768px) and (max-width:1023px){.pf-minitable{min-width:920px}}

/* ============================================================================
   E4-4A — Report ภายใน (Plan-vs-Actual + Performance) — S44 · ซี
   spec: docs/design/planning-flow/report-4a-design-s44.md (ยู) §5 (DOM+สี+dual-encode) / §8 (reuse vs new) / §9-Q5
   Wegital DS: §3.0 Stat tier · §5 Waterfall/Bullet · §0.8 semantic-in-dataviz (สี=สถานะจริง+dual-encode บังคับ)
   reuse เต็ม: .stat-value/.stat-sub (S42) · .kpi-strip · .waterfall-chart/.waterfall-bar--{total,increase,decrease}
              .bullet-list/.bullet-row · .tbl--report (financial) · .badge.plan-planned/unplanned/adhoc · .empty-card
   ไม่แตะ report.go/report.html (เด เจ้าของ) · ไม่สร้าง token สีใหม่ (ใช้ semantic + -text เดิมทั้งหมด)
   ============================================================================ */

/* ---- §5 SECTION 1 · Hero stat strip (Quoted / Actual-hero / Variance แถวเดียว) ---- */
.stat-strip{ display:flex; flex-wrap:wrap; align-items:flex-end; gap:24px; }
.stat-cell{ display:flex; flex-direction:column; min-width:0; overflow-wrap:anywhere; }
/* hero (Actual) = .stat-value เดิมอยู่แล้ว (44px/700, S42) — ระบุซ้ำให้ชัดเจนในสโคปนี้ กันเพี้ยนถ้า base เปลี่ยนทีหลัง */
.stat-cell--hero .stat-value{ font-size:44px; }
/* stat รอง (Quoted/Variance) — เด่นน้อยกว่า hero ตาม spec §5 ("--sub = 28-32px") ยังคง weight/tabular-nums/สี
   จาก .stat-value เดิม (ประกาศคู่กันในเทมเพลต: class="stat-value stat-value--sub") */
.stat-value--sub{ font-size:32px; }

/* variance dual-encode (สี + icon คู่กันเสมอ, DS §0.8 — ห้ามพึ่งสีอย่างเดียว)
   ค่า data-variance ที่ template ต้องเซ็ต: "over" (เกินแผน) / "under" (ต่ำกว่าแผน) / "flat" (ตรงเป๊ะ ไม่มีลูกศร) */
.stat-value[data-variance="over"]{ color:var(--alert-text); }   /* เกินแผน */
.stat-value[data-variance="under"]{ color:var(--green-text); }  /* ต่ำกว่าแผน = ประหยัด = ดี */
.stat-value[data-variance="flat"]{ color:var(--text-2); }       /* ตรงแผนเป๊ะ — เด: ไม่ต้อง render <i> ลูกศร */

/* ---- §5 SECTION 2 · Waterfall cost-overrun variant (⭐ §9-Q5 — product-specific override, PMAgent เท่านั้น)
   ปัญหา: .waterfall-bar--increase เดิม fill=--green-text ออกแบบมาให้ WeFin P&L ("เพิ่ม=ดี" เงินเข้ามากขึ้น)
   แต่ report นี้ "เพิ่ม=แย่" (manday เกินแผน) → เขียวจะสื่อสารผิดทิศ (คนอ่านอาจตีความว่าดีทั้งที่คือเกินงาน)
   ❌ ห้ามแก้ .waterfall-bar--increase ตรง ๆ (WeFin P&L ใช้ร่วม จะเพี้ยนสีทันที — regression watch ยู §10)
   ✅ เพิ่ม modifier ใหม่แยก 2 ชนิด (คนละสาเหตุส่วนเกิน ต้องแยกสีให้ตีความได้):
      --overrun       = ตีราคาต่ำ (งานในแผนทำเกินที่ตีราคา — ความรับผิดชอบ PM/คนตีราคา)
      --overrun-scope = งานแทรกนอกแผน/scope creep (โทนเดียวกับ .badge.plan-unplanned ผูกความหมายเดียวกัน)
   contrast คำนวณจริง (WCAG relative-luminance, ไม่ปัด) ผ่าน 1.4.11 (≥3:1) ทั้ง light/dark ทุกพื้นเป็นไปได้:
     warm-text vs surface 5.93(L)/10.56(D) · vs surface-2 5.21/9.71 · vs bg 5.44/11.87
     cat-6     vs surface 4.30(L)/ 7.86(D) · vs surface-2 3.78/7.23 · vs bg 3.95/8.83
   flag: เสนอ เอส พิจารณา promote เป็น waterfall variant "cost-overrun" ใน Global DS ทีหลัง (ไม่บล็อกงานนี้) */
.waterfall-bar--overrun{ fill:var(--warm-text); }
.waterfall-bar--overrun-scope{ fill:var(--cat-6); }
/* legend dot/icon คู่สีให้ตรงแท่ง (pattern เดียวกับ .wf-legend-group--pos/--neg เดิม, dual-encode legend) */
.wf-legend-group--overrun .wf-legend-dot{ background:var(--warm-text); }
.wf-legend-group--overrun .wf-legend-item i{ color:var(--warm-text); }
.wf-legend-group--overrun-scope .wf-legend-dot{ background:var(--cat-6); }
.wf-legend-group--overrun-scope .wf-legend-item i{ color:var(--cat-6); }

/* ---- §5 SECTION 3 · Performance 2 การ์ด (Delivery / Estimation accuracy) — เล่าเรื่องเดียว ---- */
.perf-grid{ display:grid; grid-template-columns:1fr 1fr; gap:16px; align-items:stretch; margin:12px 0; }
.perf-card{ display:flex; flex-direction:column; gap:12px; background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:16px; }

/* verdict badge (icon+label) — 4 สถานะรวมครอบ 2 การ์ด: การ์ด A (Delivery) ใช้ good/warn · การ์ด B (Estimation)
   ใช้ good/bad/info · bg = tint 16% ทับ --surface (pattern เดียวกับ .badge.plan-unplanned) · fg = -text เสมอ
   คำนวณ contrast จริงทุกคู่ (WCAG relative-luminance, 16% ทับ --surface) — margin แคบสุด = bad/alert 4.61:1 (L) */
.perf-verdict{ display:inline-flex; align-items:center; gap:6px; width:fit-content; max-width:100%;
  padding:4px 10px; border-radius:999px; font-size:var(--fs-sm); font-weight:600; overflow-wrap:anywhere; }
.perf-verdict--good{ background:color-mix(in srgb, var(--green) 16%, var(--surface)); color:var(--green-text); }  /* 4.76:1 L / 6.88:1 D */
.perf-verdict--warn{ background:color-mix(in srgb, var(--warm) 16%, var(--surface)); color:var(--warm-text); }    /* 5.40:1 L / 7.45:1 D */
.perf-verdict--bad { background:color-mix(in srgb, var(--alert) 16%, var(--surface)); color:var(--alert-text); } /* 4.61:1 L / 4.93:1 D */
.perf-verdict--info{ background:color-mix(in srgb, var(--cold) 16%, var(--surface)); color:var(--cold-text); }    /* 5.63:1 L / 4.88:1 D */

/* explainer strip — ปิดบัญชี "ตีราคาต่ำ 5 + งานแทรก 6 = เกินแผน 11" กันเลขลอย (หัวใจ fix-list §1)
   reuse โทนเดียวกับ .fin-banner--info (พื้น --surface-2 + icon --secondary-text) */
.perf-explainer{ display:flex; align-items:flex-start; gap:8px; padding:10px 12px; border-radius:8px;
  font-size:var(--fs-sm); background:var(--surface-2); color:var(--text); margin-top:12px; overflow-wrap:anywhere; }
.perf-explainer i.fi{ flex-shrink:0; margin-top:2px; color:var(--secondary-text); }

@media (max-width:768px){
  .perf-grid{ grid-template-columns:1fr; }
}

/* ============================================================================
   Move picker + warning (DEC-105 Wave B): #2 ย้าย List→project · #3 ย้ายการ์ด→list
   picker เดียว (radio single-select · leaf ต่างกัน) — ยึด token light+dark · ❌ ไม่ hardcode สี
   ========================================================================== */
.mlp { display:flex; flex-direction:column; gap:8px; min-width:280px; }
.mlp-search { margin-bottom:2px; }
.mlp-body { display:flex; flex-direction:column; gap:2px; max-height:340px; overflow-y:auto; }
.mp-picker { border:1px solid var(--border); border-radius:12px; padding:6px; }
.mp-picker .mlp-body { max-height:300px; }
.mlp-group { display:flex; flex-direction:column; gap:2px; }
.mlp-group-head { display:flex; align-items:center; gap:6px; padding:8px 8px 4px; font-size:12px; font-weight:600; color:var(--text-2); }
.mlp-group-head i.fi { font-size:14px; }
.mlp-opt {
  display:flex; align-items:center; gap:10px; width:100%; min-height:44px;
  padding:8px 10px; border:none; background:transparent; border-radius:10px;
  cursor:pointer; text-align:left; color:var(--text); font-family:inherit;
}
.mlp-opt:hover { background:var(--surface-2); }
.mlp-opt:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.mlp-opt--on { background:color-mix(in srgb, var(--primary-solid) 9%, var(--surface)); }
.mlp-radio {
  display:inline-flex; align-items:center; justify-content:center;
  width:20px; height:20px; flex:none; border:2px solid var(--border-control);
  border-radius:999px; color:transparent; font-size:12px; transition:background .15s, border-color .15s;
}
.mlp-opt--on .mlp-radio { border-color:transparent; background:var(--primary-aa); color:#fff; }
.mlp-opt-text { display:flex; flex-direction:column; gap:2px; min-width:0; }
.mlp-opt-name { font-size:14px; overflow-wrap:anywhere; }
.mlp-opt-path { font-size:12px; color:var(--text-2); overflow-wrap:anywhere; }
.mlp-empty, .mlp-loading { padding:16px 8px; text-align:center; color:var(--text-2); font-size:13px; }
.mlp-foot { display:flex; gap:8px; justify-content:flex-end; padding-top:8px; border-top:1px solid var(--border); margin-top:4px; }
/* warning (#2 modal + #3 sheet): field/ค่า/จำนวนที่จะหาย */
.mlp-warn { gap:12px; }
.mlp-warn-list { margin:0; padding-left:18px; display:flex; flex-direction:column; gap:6px; font-size:13px; color:var(--text); }
.mlp-warn-list li { overflow-wrap:anywhere; }
.mlp-warn-keep { margin:0; font-size:12px; color:var(--text-2); overflow-wrap:anywhere; }
.mp-cur { font-size:13px; color:var(--text-2); overflow-wrap:anywhere; }
.mp-warn-lead { font-size:13px; font-weight:600; color:var(--text); margin:4px 0; overflow-wrap:anywhere; }

/* ===== S54 hotfix — global toast (surface swallowed htmx errors ที่ hx-swap="none" กลืนเงียบ) ===== */
.wg-toast { position:fixed; bottom:24px; left:50%; z-index:9999; max-width:min(92vw,440px);
  transform:translateX(-50%); opacity:1; pointer-events:none;
  background:var(--text,#1C1C1E); color:var(--bg,#F5F5F7); padding:11px 18px; border-radius:10px;
  font-size:13px; line-height:1.5; box-shadow:0 6px 20px rgba(0,0,0,.24);
  animation:wgToastIn .2s ease; } /* animate เฉพาะ transform — opacity ค้างที่ base:1 เสมอ (paused animation ใน tab ซ่อนไม่แตะ opacity → เห็นแน่นอน) */
@keyframes wgToastIn { from { transform:translateX(-50%) translateY(10px); } }
.wg-toast.wg-out { opacity:0; transform:translateX(-50%) translateY(8px); transition:opacity .2s ease, transform .2s ease; }
/* --alert เป็นสี fill สว่าง (fail AA เป็นตัวอักษร) → ใช้เป็น 'ขอบซ้าย' บ่งชี้ error บนพื้นเข้ม (อ่านชัด) ไม่ใช่พื้นหลัง */
.wg-toast--error { border-left:4px solid var(--alert,#FF453A); padding-left:14px; font-weight:500; }

/* ==================================================================
   S54 · Daily Task — Status (table) + Workload view (คน × วัน)
   ยึด Wegital DS: token เท่านั้น (❌ hardcode สี) · even-pixel · tier = -text (AA)
   ================================================================== */

/* ---- แท็บ Table | Workload (reuse .rpt-tabs/.seg-link) ---- */
.dy-tabs { max-width:320px; margin:16px 0; }

/* ---- Status column ในตาราง (chip + checkbox) ---- */
.tbl-th--dstatus, .tbl-td--dstatus { white-space:nowrap; }
.dy-cbx { display:inline-flex; vertical-align:middle; margin-right:6px; }
.dy-st { font-size:12px; font-weight:500; padding:2px 8px; border-radius:999px; }
.dy-st--todo { color:var(--warm-text); background:var(--warn-bg); }
.dy-st--done { color:var(--green-text); background:color-mix(in srgb, var(--green) 14%, transparent); }
.dy-overdue { margin-left:6px; font-size:11px; font-weight:600; color:var(--warm-text);
  background:var(--warn-bg); padding:2px 6px; border-radius:6px; }

/* ---- Workload toolbar ---- */
.wl-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin:8px 0 16px; }
.wl-scope { max-width:260px; margin:0; flex:0 0 auto; }
.wl-weeknav { display:flex; align-items:center; gap:6px; }
.wl-weeklbl { font-size:14px; font-weight:600; min-width:180px; text-align:center;
  overflow-wrap:anywhere; }
.wl-today { padding:8px 14px; min-height:40px; display:inline-flex; align-items:center; }
.wl-sum { margin-left:auto; display:inline-flex; align-items:center; gap:6px;
  font-size:13px; font-weight:500; color:var(--text-2); background:var(--surface-2);
  border:1px solid var(--border); border-radius:999px; padding:6px 14px; }
.wl-sum b { color:var(--text); }
.wl-note { display:flex; align-items:center; gap:8px; font-size:13px; color:var(--text-2);
  background:var(--surface-2); border:1px solid var(--border); border-radius:12px;
  padding:10px 14px; margin-bottom:12px; }
.wl-note i { color:var(--cold-text); }

/* ---- tier (รวม/คน · รวม/วัน · cell chip) — สี text-safe (AA) ---- */
.wl-tier--ok   .wl-cellsum, .wl-rowsum.wl-tier--ok,   .wl-colsum.wl-tier--ok,   .wl-foot-grand.wl-tier--ok,   .wl-sum.wl-tier--ok   { color:var(--green-text); }
.wl-tier--warn .wl-cellsum, .wl-rowsum.wl-tier--warn, .wl-colsum.wl-tier--warn, .wl-foot-grand.wl-tier--warn, .wl-sum.wl-tier--warn { color:var(--warm-text); }
.wl-tier--over .wl-cellsum, .wl-rowsum.wl-tier--over, .wl-colsum.wl-tier--over, .wl-foot-grand.wl-tier--over, .wl-sum.wl-tier--over { color:var(--alert-text); }
.wl-tier--over .wl-cellsum b, .wl-rowsum.wl-tier--over b, .wl-colsum.wl-tier--over b, .wl-foot-grand.wl-tier--over b { color:var(--alert-text); }

/* ---- desktop grid ---- */
.wl-scroll { overflow-x:auto; border:1px solid var(--border); border-radius:12px; background:var(--surface); }
.wl-grid { display:grid; grid-template-columns:148px repeat(7, minmax(112px,1fr)) 104px; min-width:900px; }
.wl-grid > div { border-right:1px solid var(--border); border-bottom:1px solid var(--border); }
.wl-corner, .wl-dhead, .wl-corner--r { position:sticky; top:0; z-index:2; background:var(--surface-2);
  font-size:12px; font-weight:600; color:var(--text-2); padding:8px; }
.wl-corner { left:0; z-index:3; }
.wl-corner--r { text-align:right; }
.wl-dhead { display:flex; flex-direction:column; align-items:center; gap:2px; text-align:center; }
.wl-dhead-d { color:var(--text); font-weight:600; }
.wl-dhead--today { background:color-mix(in srgb, var(--warm) 16%, transparent); }
.wl-dhead-tag { font-size:10px; font-weight:600; color:var(--warm-text); }
.wl-dhead-tag--hol { color:var(--cold-text); overflow-wrap:anywhere; }
.wl-off { background:var(--surface-2); }
.wl-off .wl-dhead-d, .wl-off.wl-dhead-d { color:var(--text-2); }

.wl-name { position:sticky; left:0; z-index:1; background:var(--surface);
  display:flex; align-items:center; gap:8px; padding:8px; font-size:13px; min-width:0; }
.wl-name-t { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.wl-name--me { background:color-mix(in srgb, var(--primary-solid) 6%, transparent); }
.wl-you { font-size:10px; font-weight:600; color:var(--primary-aa); background:color-mix(in srgb, var(--primary-solid) 12%, transparent);
  padding:1px 6px; border-radius:999px; flex-shrink:0; }

.wl-cell { padding:6px; min-height:64px; display:flex; flex-direction:column; gap:6px; }
.wl-cell--today { box-shadow:inset 2px 0 0 var(--warm); }
.wl-cell.wl-off { background:var(--surface-2); }
.wl-empty { color:var(--text-2); font-size:14px; margin:auto; }

.wl-cards { display:flex; flex-direction:column; gap:4px; }
.wl-chip { display:flex; align-items:center; gap:4px; font-size:12px; min-width:0;
  background:var(--surface-2); border:1px solid var(--border); border-radius:8px; padding:3px 6px; }
.wl-chip--todo { border-style:dashed; border-color:var(--border-control); }
.wl-chip--past { background:var(--warn-bg); border-color:color-mix(in srgb, var(--warm) 40%, transparent); }
.wl-chip .wl-cbx { flex:0 0 auto; margin:0; }
.wl-chip .wl-cbx .cbx-box { width:20px; height:20px; padding:0; }  /* DS §Checkbox default (dense grid · desktop ≥24px hit ผ่าน 2.5.8 — mobile ขยาย ≥44px) */
.wl-cbx-ro { flex:0 0 auto; width:20px; height:20px; display:inline-flex; align-items:center; justify-content:center;
  border-radius:5px; border:1px solid var(--border-control); color:var(--text-2); font-size:10px; }
.wl-cbx-ro.is-done { color:var(--green-text); border-color:color-mix(in srgb, var(--green) 40%, transparent); }
.wl-chip-t { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  color:var(--text); text-decoration:none; }
.wl-chip-t:hover { text-decoration:underline; }
.wl-chip--done .wl-chip-t { color:var(--text-2); }
.wl-chip-md { flex:0 0 auto; color:var(--text-2); font-weight:500; }
.wl-past { flex:0 0 auto; font-size:10px; font-weight:600; color:var(--warm-text);
  background:var(--warn-bg); padding:0 4px; border-radius:4px; }

/* cell footer: capacity bar + chip + mini-plan */
.wl-cellfoot { display:flex; align-items:center; gap:6px; margin-top:auto; }
.wl-cellfoot--off { color:var(--text-2); font-size:11px; }
.wl-offlbl { font-size:10px; color:var(--text-2); }
.wl-cap { position:relative; flex:1 1 auto; height:8px; min-width:24px; display:flex;
  background:var(--surface); border:1px solid var(--border-control); border-radius:999px; overflow:hidden; }
.wl-cap-done { height:100%; }
.wl-cap-todo { height:100%; }
.wl-tier--ok   .wl-cap-done { background:var(--green); }
.wl-tier--warn .wl-cap-done { background:var(--warm); }
.wl-tier--over .wl-cap-done { background:var(--alert); }
.wl-tier--ok   .wl-cap-todo { background:repeating-linear-gradient(135deg, color-mix(in srgb, var(--green) 34%, transparent) 0 3px, transparent 3px 6px); }
.wl-tier--warn .wl-cap-todo { background:repeating-linear-gradient(135deg, color-mix(in srgb, var(--warm) 40%, transparent) 0 3px, transparent 3px 6px); }
.wl-tier--over .wl-cap-todo { background:repeating-linear-gradient(135deg, color-mix(in srgb, var(--alert) 40%, transparent) 0 3px, transparent 3px 6px); }
.wl-cap--over { border-color:var(--alert); }
.wl-cap-tick { position:absolute; top:-1px; bottom:-1px; width:2px; background:var(--text-2); opacity:.6; }
.wl-cellsum { flex:0 0 auto; font-size:11px; font-weight:600; color:var(--text-2); white-space:nowrap; }
.wl-cellsum b { color:var(--text); }
.wl-cellsum-cap { color:var(--text-2); font-weight:400; }
.wl-cellsum--over i { margin-right:2px; }

.wl-plan { display:inline-flex; align-items:center; justify-content:center; gap:4px;
  width:100%; min-height:44px; margin:auto 0; font-size:12px; color:var(--text-2);
  background:transparent; border:1px dashed var(--border-control); border-radius:8px; cursor:pointer; }
.wl-plan:hover { color:var(--primary-aa); border-color:var(--primary-solid); background:color-mix(in srgb, var(--primary-solid) 5%, transparent); }
.wl-plan--mini { flex:0 0 auto; width:24px; height:24px; min-height:24px; padding:0; border-radius:6px; }
.wl-plan:focus-visible, .wl-cbx input:focus-visible + .cbx-box { outline:2px solid var(--primary-solid); outline-offset:2px; }

/* row sum / col sum / grand */
.wl-rowsum { position:sticky; right:0; background:var(--surface); text-align:right; padding:8px;
  font-size:12px; color:var(--text-2); display:flex; align-items:center; justify-content:flex-end; }
.wl-rowsum b { color:var(--text); }
.wl-foot-lbl, .wl-colsum, .wl-foot-grand { position:sticky; bottom:0; z-index:1; background:var(--surface-2);
  font-size:12px; font-weight:600; padding:8px; }
.wl-foot-lbl { left:0; z-index:2; color:var(--text-2); }
.wl-colsum { text-align:center; color:var(--text-2); }
.wl-colsum b, .wl-foot-grand b { color:var(--text); }
.wl-foot-grand { right:0; z-index:2; text-align:right; color:var(--text-2); }

/* ---- mobile stacked (≤768) ---- */
.wl-mobile { display:none; }
@media (max-width:768px) {
  /* (S56) .wl-scroll ซ่อนด้วยคลาสกลาง .tbl-hascards ที่ตัว wrapper ใน daily.html — ไม่ซ่อนจากที่นี่ */
  .wl-mobile { display:block; }
  .wl-toolbar { gap:8px; }
  .wl-sum { margin-left:0; width:100%; justify-content:center; }
  .wl-weeklbl { min-width:0; flex:1; }
  .wl-mday { border:1px solid var(--border); border-radius:12px; margin-bottom:12px; overflow:hidden; background:var(--surface); }
  .wl-mday-head { padding:10px 14px; background:var(--surface-2); font-weight:600; font-size:14px; }
  .wl-mday--today .wl-mday-head { background:color-mix(in srgb, var(--warm) 16%, transparent); }
  .wl-mday.wl-off .wl-mday-head { color:var(--text-2); }
  .wl-mrow { padding:10px 14px; border-top:1px solid var(--border); }
  .wl-mrow-name { display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-2); margin-bottom:6px; }
  .wl-mrow .wl-cell { padding:0; min-height:0; }
  .wl-mrow .wl-chip .cbx-box { width:24px; height:24px; }  /* touch ≥24 dense · label แถวรวม hit-area */
  .wl-mrow .wl-cbx { min-height:44px; align-items:center; }
}

/* ===== S54 Rate quick-wins (ซี) — quick-preset "มีผลวันนี้" + mini trend chart + Upcoming badge =====
   research: docs/design/research/historical-rate-cost-management-teardown-s54.md §ค (1)(2)(3)
   #1: reuse .cal-qbtn (ปุ่มลัดในปฏิทิน dpOpen) "นอก" popover — 44px/border/focus-visible ครบอยู่แล้ว ไม่ต้องสร้างใหม่
   #2: reuse pattern เดียวกับ .mrr-trend (templates/wefin/mrr.html, S39) — SVG server-render, role=img+aria-label */
.rt-eff-row { display:flex; gap:8px; flex-wrap:wrap; align-items:stretch; }
.rt-eff-row > .cal-qbtn { flex:0 0 auto; }
.rt-eff-row > .dd { flex:1 1 200px; min-width:0; }

.rt-trend svg { width:100%; height:auto; display:block; overflow:visible; }
.rt-trend-line { fill:none; stroke:var(--primary-solid); stroke-width:2; stroke-linejoin:round; }
.rt-trend-line--future { stroke-dasharray:5 4; opacity:.7; } /* ยังไม่มีผล — เส้นประจางกว่า แยกจากอดีต/ปัจจุบัน */
.rt-trend-dot { fill:var(--primary-solid); }
.rt-trend-dot--future { fill:var(--surface); stroke:var(--primary-solid); stroke-width:2; } /* จุดกลวง = future record */
.rt-trend-caption { display:flex; justify-content:space-between; gap:12px; font-size:12px; color:var(--text-2); margin-top:8px; flex-wrap:wrap; }
.rt-trend-caption i.fi { margin-right:4px; color:var(--text-2); }

/* Upcoming badge (future-dated record) — reuse recipe เดียวกับ .badge--staff (cold-text ผ่าน AA ทั้ง 2 theme
   อยู่แล้ว) แยกจาก .badge--off (เฉย ๆ/inactive) เพราะ "ยังไม่มีผล" คือ scheduled/info ไม่ใช่ปิดใช้งาน */
.badge--upcoming { color:var(--cold-text); background:color-mix(in srgb, var(--cold) 16%, transparent); }

/* ═══════════════════════════════════════════════════════════════════════════
   Pricing — Concept ③ Dual-Pane (S54 · เด) — ต้นทุนภายใน | ใบเสนอลูกค้า
   adapt จาก mockup pricing-mockup-1-3.html (DS-verified) · reuse token/.fin-seg/.fin-chip/.pf-freeze
   price-* = label:value stack · dualpane = 2 คอลัมน์ · save-chip = autosave status
   ═══════════════════════════════════════════════════════════════════════════ */
.price-card-unit { font-size:12px; font-weight:500; color:var(--text-2); }

/* ── price stack (label:value rows + subtotal/total จาก DS Table financial §4) ── */
.price-stack { display:flex; flex-direction:column; }
.price-row { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:8px 0; min-height:44px; flex-wrap:wrap; }
.price-row-label { display:flex; align-items:center; gap:8px; font-size:14px; color:var(--text); flex:1; min-width:0; overflow-wrap:anywhere; }
.price-row-note { font-size:12px; color:var(--text-2); font-weight:500; white-space:nowrap; }
.price-row-value { font-size:14px; font-weight:500; color:var(--text); text-align:right; white-space:nowrap;
  display:flex; align-items:center; gap:6px; justify-content:flex-end; }
.price-row-value--neg { color:var(--alert-text); }
/* P0 (verify browser 320px): .price-row-value{display:flex}(0,1,0) ทับ UA [hidden]{display:none}(0,0,1) → output ที่ hidden ยังโชว์ */
.price-row-value[hidden], .chip-auto[hidden], .price-unit[hidden], .price-output[hidden], .price-input[hidden], .price-pct-input[hidden] { display:none; }
.price-row--subtotal { font-weight:600; border-top:1px solid var(--border-control); margin-top:6px; padding-top:12px; }
.price-row--total { font-weight:600; border-top:1px solid var(--border-control);
  box-shadow:inset 0 3px 0 -2px var(--border-control); margin-top:6px; padding-top:12px; }
.price-row--total .price-row-label, .price-row--total .price-row-value { font-size:16px; }
.price-reactive-row .price-unit { font-size:13px; color:var(--text-2); font-weight:500; }
.price-output { font-weight:600; }

/* ── custom inputs (❌ ห้าม native number-spinner · text + inputmode=decimal) ── */
.price-pct-input { width:64px; text-align:right; font:inherit; font-size:14px; color:var(--text);
  background:var(--surface); border:1px solid var(--border-control); border-radius:8px; padding:8px; min-height:36px; }
.price-input { width:132px; text-align:right; font:inherit; font-size:14px; font-weight:600; color:var(--text);
  background:var(--surface); border:1px solid var(--border-control); border-radius:8px; padding:8px 10px; min-height:36px; }
.price-pct-input:focus, .price-input:focus { outline:none; border-color:var(--primary-solid);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--primary-solid) 10%, transparent); }
.price-pct-input:disabled, .price-input:disabled { background:var(--surface-2); color:var(--text-2); cursor:not-allowed; }

/* ── auto chip (informational · ต้นทุน labor/other/reverse readout) ── */
.chip-auto { display:inline-flex; align-items:center; gap:4px; font-size:11px; font-weight:500; color:var(--text-2);
  background:var(--surface-2); border-radius:999px; padding:3px 9px; white-space:nowrap; }
.chip-auto i.fi { font-size:11px; }

/* ── set-by / mode selectors ── */
.price-setby, .price-mode { display:flex; align-items:center; gap:12px; margin-bottom:14px; flex-wrap:wrap; }
.price-setby-label, .price-mode-label { font-size:12px; font-weight:500; color:var(--text-2); }

/* ── partial warn (การ์ดไม่มี assignee/rate) ── */
.price-row-warn { display:flex; align-items:flex-start; gap:8px; font-size:12px; color:var(--warm-text);
  background:var(--warn-bg); border-radius:10px; padding:10px 12px; margin:2px 0 10px; }
.price-row-warn i.fi { margin-top:1px; flex:0 0 auto; }
.price-row-warn .link-btn { color:var(--warm-text); font-weight:600; text-decoration:underline; font-size:12px; }

/* ── autosave save-chip (§6) ── */
.px-savebar { display:flex; justify-content:flex-end; max-width:1040px; margin:16px auto 0; }
.save-chip { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:500;
  color:var(--green-text); background:color-mix(in srgb, var(--green) 12%, transparent);
  border-radius:999px; padding:7px 14px; min-height:32px; transition:background .15s, color .15s; }
.save-chip[data-state="saving"], .save-chip[data-state="idle"] { color:var(--text-2); background:var(--surface-2); }
.save-chip[data-state="error"] { color:var(--alert-text); background:var(--alert-bg); }
.save-chip i.fi { font-size:13px; }

/* ── dual-pane (Concept ③) ── */
.dualpane { display:grid; grid-template-columns:1fr 1fr; gap:24px; max-width:1040px; margin:0 auto; align-items:start; }
.dualpane-col { background:var(--surface); border:1px solid var(--border); border-radius:var(--r); padding:20px;
  box-shadow:var(--shadow-card); display:flex; flex-direction:column; gap:16px; position:relative; min-width:0; }
.dualpane-col-head { display:flex; align-items:center; justify-content:space-between; gap:12px; }
.dualpane-col-head h4 { font-size:15px; font-weight:600; }
.dualpane-lockline { display:flex; align-items:center; gap:6px; font-size:12px; font-weight:500; color:var(--text-2);
  border-top:1px solid var(--border); padding-top:12px; margin-top:2px; }
.dualpane-col--cost .dualpane-lockline { color:var(--alert-text); }
.dualpane-actions { display:flex; gap:8px; flex-wrap:wrap; }
/* ปุ่ม "ซ่อนต้นทุน" — blur ฝั่งซ้าย (มุมลูกค้า · client-side view state) */
.dualpane-col--cost.is-hidden-view .price-stack,
.dualpane-col--cost.is-hidden-view .price-setby,
.dualpane-col--cost.is-hidden-view .price-mode { filter:blur(6px); pointer-events:none; user-select:none; }
.dualpane-hidden-overlay { position:absolute; inset:52px 12px 12px; display:none; flex-direction:column;
  align-items:center; justify-content:center; gap:8px; text-align:center; padding:20px;
  background:color-mix(in srgb, var(--surface) 85%, transparent); border-radius:12px; }
.dualpane-col--cost.is-hidden-view .dualpane-hidden-overlay { display:flex; }
.dualpane-hidden-overlay i.fi { font-size:22px; color:var(--text-2); }
.dualpane-hidden-overlay span { font-size:13px; color:var(--text-2); font-weight:500; max-width:240px; }

/* ── quote table (px-prefixed · isolate จาก global .tbl min-width:960) ── */
.px-tbl { width:100%; border-collapse:collapse; }
.px-th { text-align:left; font-size:12px; font-weight:500; color:var(--text-2); padding:0 4px 8px;
  border-bottom:1px solid var(--border-control); }
.px-th--num { text-align:right; }
.px-td { padding:10px 4px; font-size:14px; vertical-align:middle; }
.px-td--num { text-align:right; }
.px-trow--total .px-td { font-weight:600; font-size:16px; border-top:1px solid var(--border-control);
  box-shadow:inset 0 3px 0 -2px var(--border-control); padding-top:14px; }
.px-inline-pct { display:inline-flex; align-items:center; gap:6px; }

/* ── empty state ── */
.px-empty { max-width:640px; margin:24px auto; text-align:center; display:flex; flex-direction:column;
  align-items:center; gap:8px; padding:40px 24px; }
.px-empty i.fi { font-size:28px; color:var(--text-2); }
.px-empty h3 { font-size:16px; font-weight:600; }
.px-empty p { font-size:13px; color:var(--text-2); max-width:380px; }
.px-empty .btn-primary { margin-top:8px; }

/* mobile: ยุบ 1 คอลัมน์ (ยู §8 · กลายเป็น Concept ①) */
@media (max-width:767px) { .dualpane { grid-template-columns:1fr; } }
@media (max-width:400px) {
  .price-row { flex-direction:column; align-items:flex-start; gap:6px; }
  .price-row-value { justify-content:flex-start; }
}

/* ============================================================================
   WeDoc (Product 3 · S55) — ศูนย์ออกเอกสารส่งลูกค้า
   กฎที่ยึด (Wegital DS): สีจาก CSS var เท่านั้น (dark mode ทำงานอัตโนมัติ) · Even-Pixel ·
   ❌ ไม่มีแถบสีขอบซ้าย · chip = tint bg + fg ตัวแปร -text (ไม่ใช่พื้นสีเต็ม+ตัวขาว) ·
   touch target ≥44px · ข้อความไทยใส่ overflow-wrap:anywhere
   ============================================================================ */
:root, [data-theme="dark"] {
  /* tint พื้น 16% แบบเดียวกับ --warn-bg (composite เหนือ surface เอง) */
  --wd-green-bg:color-mix(in srgb, var(--green) 16%, transparent);
  --wd-cold-bg:color-mix(in srgb, var(--cold) 16%, transparent);
}
.app-card-ic--doc { background:var(--cat-5); }

/* ---- chip สถานะเอกสาร (dual-encode: icon + label + สี) ---- */
.wd-chip { display:inline-flex; align-items:center; gap:6px; }
.wd-chip--muted { background:var(--surface-2); color:var(--text-2); }
.wd-chip--cold  { background:var(--wd-cold-bg); color:var(--cold-text); }
.wd-chip--warm  { background:var(--warn-bg); color:var(--warm-text); }
.wd-chip--green { background:var(--wd-green-bg); color:var(--green-text); }
.wd-chip--alert { background:var(--alert-bg); color:var(--alert-text); }

/* ---- ตารางรายการเอกสาร ---- */
.wd-tbl { min-width:880px; }
.wd-link { font-weight:600; color:var(--text); text-decoration:none; overflow-wrap:anywhere; }
.wd-link:hover { color:var(--primary-solid); text-decoration:underline; }
.wd-sub { display:block; font-size:11px; color:var(--text-2); overflow-wrap:anywhere; }
.wd-missing { display:inline-flex; align-items:center; gap:4px; color:var(--alert-text); font-weight:500; font-size:13px; }
.wd-complete { display:inline-flex; align-items:center; gap:4px; color:var(--green-text); font-weight:500; font-size:13px; }
/* ---- การ์ดรายการเอกสารบนมือถือ (≤768px ที่ตาราง .tbl-hascards ถูกซ่อน — reuse .tbl-mcard ของ Table view) ---- */
.tbl-mcard .wd-chip { flex-shrink:0; align-self:flex-start; }
/* .rpt-bar เดิมตั้ง flex:1 + max-width:120px (ออกแบบมาให้อยู่ในแถว flex แนวนอนของ report) — ใน .tbl-mcard
   ที่เป็น flex column แกนหลักคือ "แนวตั้ง" flex:1 จะไปยืดความสูงแทน → ปลด flex + ให้กว้างเต็มการ์ด */
.wd-mbar { flex:none; max-width:none; width:100%; }
/* ≤768px: .card ที่ห่อรายการอยู่แล้ว = การ์ดซ้อนการ์ด (ลูกเป็น .tbl-mcard ที่มีกรอบ/พื้นเองทุกใบ)
   → ปลดกรอบ/พื้น/padding ของ .card ทิ้ง คืนความกว้าง 34px ให้จอ 375px (343→377 ใช้เต็ม content box)
   desktop ≥769px ไม่แตะ (ตาราง .tbl-scroll ยังอยู่ในการ์ดเหมือนเดิม — กฎ S31 คนละ code path) */
@media (max-width:768px) {
  .wd-listcard { background:none; border:0; border-radius:0; padding:0; }
}

/* ---- หน้าสร้างคำขอ: การ์ดเลือกชนิดเอกสาร (radio ซ่อน · pattern เดียวกับ .cbx ของ DS) ---- */
.wd-step { font-size:14px; font-weight:600; color:var(--text); padding:0 0 8px; }
.wd-typegrid { display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:12px; }
.wd-typecard { position:relative; display:flex; align-items:flex-start; gap:10px; padding:14px;
  min-height:44px; border:1px solid var(--border); border-radius:var(--r); background:var(--surface);
  cursor:pointer; transition:border-color .12s, background .12s; }
.wd-typecard:hover { background:var(--surface-2); }
.wd-typecard input { position:absolute; opacity:0; pointer-events:none; }
.wd-typecard-tick { flex-shrink:0; width:22px; height:22px; border-radius:999px; border:1px solid var(--border-control);
  display:flex; align-items:center; justify-content:center; font-size:12px; color:transparent; background:var(--surface); }
.wd-typecard input:checked ~ .wd-typecard-tick { background:var(--primary-aa); border-color:transparent; color:#fff; }
.wd-typecard:has(input:checked) { border-color:var(--primary-solid); background:var(--surface-2); }
.wd-typecard input:focus-visible ~ .wd-typecard-tick { outline:2px solid var(--primary-solid); outline-offset:2px; }
.wd-typecard-body { display:flex; flex-direction:column; gap:4px; min-width:0; }
.wd-typecard-name { font-size:15px; font-weight:600; overflow-wrap:anywhere; }
.wd-typecard-meta { font-size:11px; color:var(--text-2); overflow-wrap:anywhere; }

/* ---- หน้ารายละเอียด: 2 คอลัมน์ (checklist | ปุ่ม/เวอร์ชัน) ---- */
.wd-dual { display:grid; grid-template-columns:minmax(0,1.7fr) minmax(0,1fr); gap:16px; align-items:start; }
.wd-pane { min-width:0; }
.wd-side { display:flex; flex-direction:column; gap:16px; min-width:0; }
.wd-side-actions { display:flex; flex-direction:column; gap:8px; margin-top:10px; }
.wd-btn-block { width:100%; justify-content:center; }
.wd-sec-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin-bottom:12px; }
.wd-sec-title { font-size:16px; font-weight:600; display:inline-flex; align-items:center; gap:8px; }
.wd-sec-cnt { font-size:12px; color:var(--text-2); }
.wd-ro-note { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2); }

/* ---- checklist ต่อทีม ---- */
.wd-team { border:1px solid var(--border); border-radius:var(--r); padding:12px; margin-bottom:12px; }
.wd-team-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.wd-team-name { font-size:14px; font-weight:600; }
.wd-team-stat { text-align:right; }
.wd-team-actions { display:flex; flex-wrap:wrap; gap:8px; margin:10px 0; }
.wd-secline { display:flex; justify-content:space-between; gap:8px; margin-top:10px;
  padding-bottom:4px; border-bottom:1px solid var(--border); }
.wd-secline-name { font-size:12px; font-weight:600; color:var(--text-2); }
.wd-fieldlist { list-style:none; margin:6px 0 0; padding:0; display:flex; flex-direction:column; gap:4px; }
.wd-fielditem { display:flex; justify-content:space-between; gap:12px; padding:6px 0; font-size:13px;
  border-bottom:1px dashed var(--border); }
.wd-fielditem-lbl { display:inline-flex; align-items:center; gap:6px; min-width:0; overflow-wrap:anywhere; }
.wd-fielditem-val { color:var(--text-2); text-align:right; min-width:0; overflow-wrap:anywhere; }
.wd-fielditem--missing .wd-fielditem-lbl { color:var(--alert-text); }
.wd-fielditem--hidden { color:var(--text-2); font-size:12px; }
.wd-copybox { margin:8px 0; display:flex; flex-direction:column; gap:6px; }
.wd-copytext { width:100%; font-family:inherit; font-size:12px; line-height:1.6; padding:10px;
  border:1px solid var(--border); border-radius:var(--r); background:var(--surface-2); color:var(--text);
  overflow-wrap:anywhere; }

/* ---- เวอร์ชัน + audit ---- */
.wd-verlist, .wd-auditlist { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:8px; }
.wd-veritem { display:flex; align-items:center; justify-content:space-between; gap:12px; font-size:13px; }
.wd-audititem { font-size:12px; border-bottom:1px dashed var(--border); padding-bottom:6px; }
.wd-audit-ev { font-weight:600; }

/* ---- ฟอร์ม schema-driven ---- */
.wd-teamtabs { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:12px; }
.wd-tab { display:inline-flex; align-items:center; gap:6px; min-height:44px; padding:0 14px;
  border:1px solid var(--border); border-radius:999px; background:var(--surface); color:var(--text-2);
  font-size:13px; text-decoration:none; }
.wd-tab:hover { background:var(--surface-2); color:var(--text); }
.wd-tab--active { border-color:var(--primary-solid); color:var(--primary-solid); font-weight:600; }
.wd-tab-badge { background:var(--alert-bg); color:var(--alert-text); border-radius:999px; padding:0 8px; font-size:11px; }
.wd-sticky { position:sticky; top:8px; z-index:5; display:flex; flex-wrap:wrap; align-items:center;
  justify-content:space-between; gap:12px; margin-bottom:16px; }
.wd-progress { display:flex; align-items:center; gap:10px; font-size:13px; color:var(--text-2); min-width:0; }
.wd-progress .rpt-bar { max-width:220px; }
.wd-progress-pct { font-weight:600; color:var(--text); }
.wd-actions { display:flex; flex-wrap:wrap; align-items:center; gap:8px; }
.wd-saving { font-size:12px; color:var(--text-2); }
.wd-ok { display:flex; align-items:center; gap:8px; background:var(--wd-green-bg); color:var(--green-text);
  border-radius:var(--r); padding:12px 16px; font-size:14px; font-weight:500; margin-bottom:12px; }
.wd-errsum { align-items:flex-start; margin-bottom:16px; }
.wd-errlist { margin:6px 0 0 18px; padding:0; font-weight:400; font-size:13px; line-height:1.7; }
.wd-errhint { color:var(--text-2); }
/* คำเตือน "กดแล้วของเดิมหาย" (WeDoc S56.2: สร้างร่างใหม่ = ไฟล์ร่างเดิมลงถังขยะ + การแก้ใน Docs หาย)
   โทน warn ไม่ใช่ alert (ไม่ใช่ error · เป็นผลข้างเคียงที่ต้องรู้ก่อนกด) · ไม่มีแถบสีขอบซ้าย (DS §0 ข้อ 9) */
.wd-caution { display:flex; align-items:flex-start; gap:8px; background:var(--warn-bg); color:var(--warm-text);
  border-radius:var(--r); padding:10px 12px; font-size:13px; font-weight:500; overflow-wrap:anywhere; }
.wd-caution i { margin-top:2px; }

/* เหตุที่ล็อก/ย้ายไฟล์ในไดรฟ์ไม่สำเร็จ (WeDoc · S56) — ขึ้นบรรทัดใหม่ ไม่ปนกับประโยคหลัก */
.wd-lockwhy { display:block; margin-top:4px; font-weight:400; font-size:13px; color:var(--text-2); }
.wd-sec { margin-bottom:16px; }
.wd-hidden-note { display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-2);
  background:var(--surface-2); border-radius:var(--r); padding:10px 12px; margin-bottom:12px; }
.wd-field { margin-bottom:16px; }
.wd-field--err .input-wrap input, .wd-field--err .wd-ta { border-color:var(--alert-text); }
.wd-err { color:var(--alert-text); display:flex; align-items:center; gap:4px; }
.wd-ta { width:100%; min-height:96px; padding:10px 12px; font-family:inherit; font-size:14px; line-height:1.6;
  color:var(--text); background:var(--surface); border:1px solid var(--border-control);
  border-radius:var(--r); overflow-wrap:anywhere; resize:vertical; }
.wd-ta:focus-visible { outline:2px solid var(--primary-solid); outline-offset:2px; }
.wd-num { text-align:right; }

/* ---- ตารางกรอกข้อมูล list<object> = แถวการ์ด (ไม่ใช่ inline edit 11 คอลัมน์) ---- */
.wd-listfield { border:1px solid var(--border); border-radius:var(--r); padding:12px; margin-bottom:16px; }
.wd-listfield--err { border-color:var(--alert-text); }
.wd-listfield-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px; }
.wd-listfield-title { font-size:14px; font-weight:600; }
.wd-listfield-cnt { font-size:11px; color:var(--text-2); }
.wd-rows { display:flex; flex-direction:column; gap:12px; margin:10px 0; }
.wd-row { border:1px solid var(--border); border-radius:var(--r); padding:12px; background:var(--surface-2); }
.wd-row-head { display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
.wd-row-no { font-size:12px; font-weight:600; color:var(--text-2); }
.wd-row-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:12px; }
.wd-row-grid .wd-field { margin-bottom:0; }
.wd-addrow { width:100%; justify-content:center; }

@media (max-width:1023px) {
  .wd-dual { grid-template-columns:1fr; }
}
@media (max-width:767px) {
  .wd-sticky { position:static; }
  .wd-row-grid { grid-template-columns:1fr; }
  .wd-progress .rpt-bar { max-width:none; }
  .wd-tbl { min-width:0; }
  .wd-fielditem { flex-direction:column; gap:2px; }
  .wd-fielditem-val { text-align:left; }
}
/* ==========================================================================
   grouped Table (N4/B2 — S55, คิด audit B1/H1/B2)
   List view ถูกซ่อนทั้งแอป → grouping ต้อง "ยกมาครบ" ที่ Table: หัวกลุ่มเป็นแถวเต็มความกว้าง
   ใน <table> เดิม (thead/คอลัมน์/checkbox/bulk ไม่เปลี่ยน) · 1 <tbody class="grp"> ต่อกลุ่ม
   reuse look ของ .grp-head/.grp-caret/.grp-count + JS grp-collapse.js เดิมทั้งชุด (ไม่ประดิษฐ์ใหม่)
   ========================================================================== */
/* .grp ของ list view = flex column — ทับกลับเป็น row-group เมื่ออยู่ในตาราง (ไม่งั้น layout พัง) */
.tbl tbody.grp { display:table-row-group; }
.tbl .tbl-grprow > .tbl-td--grp { padding:6px 8px 2px; background:var(--surface); border-bottom:none; }
.tbl tbody.grp:first-of-type .tbl-grprow > .tbl-td--grp { padding-top:2px; }
/* ยุบกลุ่ม = ซ่อนแถวลูก (หัวกลุ่มยังอยู่ + caret หมุนผ่าน .grp--collapsed .grp-caret เดิม) */
.tbl tbody.grp--collapsed tr.tbl-row,
.tbl tbody.grp--collapsed tr.tbl-addrow { display:none; }
/* R1 (S55): แถว "เพิ่มการ์ด" ท้ายกลุ่ม (เฉพาะจัดกลุ่มตาม List) — reuse .grp-add ของ list view เดิม
   ทั้งชุด (dashed border + hover coral) แค่จัดระยะให้เข้ากับ cell ตาราง */
.tbl .tbl-addrow > .tbl-td--addrow { padding:6px 8px 10px; border-bottom:none; }
.tbl .tbl-addrow:hover > .tbl-td--addrow { background:transparent; }
/* M-D (S55 · คิด verify รอบ 2): หัวกลุ่มอยู่ใน <td colspan="99"> → width:100% = กว้างเท่าตารางทั้งใบ
   (~2,000px ที่ ~20 คอลัมน์) · caret+ชื่อติดซ้าย ส่วน .grp-count{margin-left:auto} ไปอยู่ขวาสุดของ
   "ตาราง" (ไม่ใช่ขวาของจอ) → เลื่อนตารางแนวนอนแล้วเห็นแค่กล่องเปล่ากับเลขนับ ชื่อกลุ่มหลุดนอกจอ
   แก้ 2 ชั้น: (1) เนื้อในหัวกลุ่ม sticky ชิดซ้าย scrollport (ClickUp ทำ group header sticky)
   → ชื่อ+จำนวนติดตาตลอดการเลื่อน (2) count ชิดชื่อ (ไม่ push ไปขวาสุด) — 8px = even-pixel
   z-index 4 = สูงกว่า sticky cb(3)/title(2) เพราะหัวกลุ่มเลื่อนทับคอลัมน์เหล่านั้นตอน scroll
   (พื้น .grp-head = --surface-2 ทึบ → ไม่มีอะไรโผล่ลอด) */
.tbl .tbl-grprow .grp-head { position:sticky; left:8px; z-index:4; width:max-content; max-width:100%; }
.tbl .tbl-grprow .grp-count { margin-left:8px; }
/* M-C (S55): กลุ่มบนการ์ดมือถือ (≤768px ที่ตาราง .tbl-hascards ถูกซ่อน) — reuse .grp/.grp-head/.grp-count/
   .grp--collapsed เดิมทั้งชุด (ยุบกลุ่มได้เหมือน desktop) แค่ปลด indent 32px ของ .grp-body
   ที่ออกแบบไว้ให้ list_row จอกว้าง (จอแคบต้องใช้พื้นที่เต็ม) */
.tbl-mobile .grp-body { padding:8px 0 4px; }

/* breadcrumb ใน page-bar (project-level full view) — คืน "Projects ›" ที่มีใน .page-head เดิม */
.page-bar-head .pb-crumb { flex-shrink:0; }
.page-bar-head .pb-crumb-sep { display:inline-flex; align-items:center; color:var(--text-2); font-size:14px; }
@media (max-width:767px) {
  /* จอแคบ: ตัด breadcrumb ทิ้ง (ชื่อโปรเจค + count สำคัญกว่า — drawer ใช้กลับได้อยู่แล้ว) */
  .page-bar-head .pb-crumb, .page-bar-head .pb-crumb-sep { display:none; }
}
