/*
 * AD Integration Gateway — design system
 *
 * ทุกหน้าในระบบประกอบจากคลาสในไฟล์นี้เท่านั้น ห้ามเขียนสไตล์เฉพาะหน้า
 * เพื่อให้หน้าจอที่เพิ่มภายหลังมีหน้าตาและพฤติกรรมเหมือนหน้าเดิมโดยอัตโนมัติ
 *
 * โทน: enterprise admin — เรียบ อ่านง่าย ไม่มีอนิเมชันเกินจำเป็น
 */

:root {
    --color-bg: #f4f6f9;
    --color-surface: #ffffff;
    --color-border: #dfe3e8;
    --color-border-strong: #808fa3;

    --color-text: #1f2933;
    --color-text-muted: #5f6b77;
    --color-text-inverse: #ffffff;

    --color-primary: #12507b;
    --color-primary-dark: #0d3d5e;
    --color-primary-light: #e7f0f7;

    --color-success: #1a7b4c;
    --color-success-light: #e6f4ec;
    --color-danger: #b3261e;
    --color-danger-light: #fdecea;
    --color-warning: #956400;
    --color-warning-light: #fff4d6;
    --color-info: #1a5f9c;
    --color-info-light: #e8f1fa;
    --color-neutral: #5c6773;
    --color-neutral-light: #eef0f2;

    /*
     * พื้นผิวและสีตัวอักษรที่ "ขึ้นกับธีม"
     *
     * เดิมค่าพวกนี้ถูกเขียนเป็นเลขฐานสิบหกติดไว้ในกฎแต่ละข้อ เช่นพื้นหัวตาราง #f7f8fa
     * หรือสีตัวอักษรของกล่องแจ้งเตือน #14603c ผลคือพอสลับเป็นโหมดมืดแล้วยังเป็นสีเดิม
     * กลายเป็นตัวหนังสือเข้มบนพื้นเข้ม ซึ่งอ่านไม่ออกจริง ๆ ไม่ใช่แค่ดูแปลกตา
     *
     * ย้ายมาเป็น token เพื่อให้กฎข้อเดียวใช้ได้ทั้งสองธีม
     */
    --color-surface-alt: #f7f8fa;      /* หัวตาราง ท้ายการ์ด แถบเครื่องมือ */
    --color-surface-hover: #f2f4f7;    /* hover ของปุ่มรอง แถวตาราง แท็บ */
    --color-surface-sunken: #e9edf1;   /* ชิปขั้นตอน พื้นที่พักสายตา */

    /* สีตัวอักษรบนพื้นทึบ — โหมดมืดใช้สีเข้มเพราะพื้นกลายเป็นสีสว่าง */
    --color-on-primary: #ffffff;
    --color-on-danger: #ffffff;

    /* กล่องแจ้งเตือน: ขอบและตัวอักษรแยกจากสีพื้น เพื่อคุมคอนทราสต์ได้ทั้งสองธีม */
    --color-success-border: #bde0cb;
    --color-success-text: #14603c;
    --color-danger-border: #f2c2be;
    --color-danger-text: #8c1d18;
    --color-warning-border: #f0dca6;
    --color-warning-text: #7a5200;
    --color-info-border: #bcd6ee;
    --color-info-text: #144b7c;

    --radius: 6px;          /* input และ button */
    --radius-sm: 4px;
    --radius-lg: 8px;       /* card, drawer, modal */
    --radius-pill: 999px;   /* badge แบบ pill */

    --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);
    --shadow-md: 0 4px 12px rgba(16, 24, 40, .08);

    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 40px;
    --space-8: 48px;

    /*
     * มาตราตัวอักษร — ประกาศไว้ที่เดียวเพื่อให้หน้าใหม่ไม่ต้องเดาขนาด
     * ภาษาไทยมีสระและวรรณยุกต์ซ้อนบน-ล่าง ขนาดต่ำกว่า 12px จึงเริ่มอ่านผิดตัว
     */
    --font-display: 28px;
    --font-h1: 24px;
    --font-h2: 20px;
    --font-h3: 16px;
    --font-body-lg: 16px;
    --font-body: 15px;
    --font-body-sm: 13px;
    --font-caption: 12px;

    --sidebar-width: 248px;
    --header-height: 56px;

    /* เนื้อหากว้างเกินนี้แล้วสายตาไล่จากหัวตารางไปคอลัมน์สุดท้ายไม่ไหว */
    --content-max: 1600px;

    /*
     * สีบนพื้นน้ำเงินของแถบเมนู
     *
     * เดิมหัวหมวดใช้ขาวโปร่ง 45% ซึ่งได้คอนทราสต์ราว 3.6:1 ต่ำกว่าเกณฑ์ WCAG AA (4.5:1)
     * คนสายตาปกติในห้องสว่างยังพอทน แต่บนจอโน้ตบุ๊กที่ปรับแสงต่ำจะอ่านแทบไม่ออก
     */
    --sidebar-text: rgba(255, 255, 255, .88);
    --sidebar-text-muted: rgba(255, 255, 255, .72);
    --sidebar-accent: #8fc1e6;

    /* วงแหวนโฟกัสต้องเห็นชัดบนทุกพื้นหลัง ทั้งพื้นขาวและพื้นน้ำเงินของเมนู */
    --focus-ring: #2b8ae0;

    /*
     * ค่าที่เหลือซึ่งต่างกันตามธีม ย้ายมาเป็น token เพื่อให้กฎของ component
     * ไม่ต้องรู้เลยว่าตอนนี้เป็นธีมไหน
     */
    --color-knob: #ffffff;                     /* ลูกบิดสวิตช์ ต้องตัดกับรางทั้งตอนเปิดและปิด */
    --color-skeleton-sheen: #f7f8f9;           /* แถบไล่สีของโครงร่างระหว่างรอข้อมูล */
    --color-overlay-soft: rgba(0, 0, 0, .08);  /* พื้นจางทับบนสีเดิม */

    /* บอกเบราว์เซอร์ให้วาด scrollbar และ control ของระบบให้เข้ากับธีม */
    color-scheme: light;

    --font-sans: "Segoe UI", "Sarabun", "Noto Sans Thai", Tahoma, system-ui, -apple-system, sans-serif;
    --font-mono: "Cascadia Mono", Consolas, "Courier New", monospace;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-sans);
    /*
     * 15px ไม่ใช่ 14px เพราะภาษาไทยมีสระและวรรณยุกต์ซ้อนบน-ล่าง
     * ที่ขนาดเล็กกว่านี้ส่วนที่ซ้อนกันจะเริ่มติดกันจนอ่านผิดตัวได้
     */
    font-size: 15px;
    line-height: 1.6;
}

a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/*
 * วงแหวนโฟกัสสำหรับการใช้งานด้วยคีย์บอร์ด
 *
 * :focus-visible แสดงเฉพาะตอนใช้ Tab ไม่ขึ้นตอนคลิกเมาส์ จึงไม่รบกวนสายตา
 * แต่คนที่ใช้คีย์บอร์ดอย่างเดียวจะรู้เสมอว่าตอนนี้อยู่ตรงไหน
 */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/*
 * ลิงก์ข้ามไปยังเนื้อหา — ซ่อนไว้จนกว่าจะถูกโฟกัส
 * ผู้ที่ใช้คีย์บอร์ดหรือโปรแกรมอ่านหน้าจอจะได้ไม่ต้องไล่ Tab ผ่านเมนู 20 กว่ารายการทุกหน้า
 */
.skip-link {
    position: absolute;
    left: var(--space-3);
    top: -60px;
    z-index: 100;
    background: var(--color-surface);
    color: var(--color-primary);
    border: 2px solid var(--focus-ring);
    border-radius: var(--radius);
    padding: var(--space-2) var(--space-4);
    font-weight: 600;
    transition: top .12s ease-out;
}
.skip-link:focus { top: var(--space-3); text-decoration: none; }

h1, h2, h3, h4 { margin: 0 0 var(--space-3); font-weight: 600; line-height: 1.35; }
h1 { font-size: var(--font-h1); }
h2 { font-size: var(--font-h2); }
h3 { font-size: var(--font-h3); }
h4 { font-size: var(--font-body); }
p { margin: 0 0 var(--space-3); }

code, pre, .mono { font-family: var(--font-mono); font-size: var(--font-body-sm); }
pre {
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: var(--space-3);
    overflow-x: auto;
}

/* ---------------------------------------------------------------- layout */

.app-shell { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-width);
    flex: 0 0 var(--sidebar-width);
    background: var(--color-primary-dark);
    color: var(--sidebar-text);
    display: flex;
    flex-direction: column;

    /* ตรึงไว้เสมอ เดิมเลื่อนหน้าลงแล้วเมนูหายไปด้วย ต้องเลื่อนกลับขึ้นบนสุดถึงจะย้ายหน้าได้ */
    position: sticky;
    top: 0;
    height: 100vh;
}

.sidebar__brand {
    height: var(--header-height);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0 var(--space-4);
    color: #fff;
    font-weight: 600;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
}

.sidebar__nav {
    flex: 1 1 auto;
    min-height: 0;
    padding: var(--space-3) 0;
    overflow-y: auto;
    scrollbar-gutter: stable;
}
.sidebar__section {
    padding: var(--space-4) var(--space-4) var(--space-1);
    font-size: 12px;
    letter-spacing: .06em;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--sidebar-text-muted);
}
.sidebar__link {
    display: block;
    padding: var(--space-2) var(--space-4);
    color: inherit;
    border-left: 3px solid transparent;
}
.sidebar__link:hover { background: rgba(255, 255, 255, .07); text-decoration: none; color: #fff; }
.sidebar__link.is-active {
    background: rgba(255, 255, 255, .14);
    border-left-color: var(--sidebar-accent);
    color: #fff;
    font-weight: 600;
}
.sidebar__link.is-disabled { opacity: .45; pointer-events: none; }
.sidebar__badge {
    float: right;
    font-size: 12px;
    background: rgba(255, 255, 255, .16);
    border-radius: 10px;
    padding: 0 6px;
}

.main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    height: var(--header-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: 0 var(--space-5);
}
.topbar__status { display: flex; gap: var(--space-4); align-items: center; margin-left: auto; }
.topbar__user { display: flex; align-items: center; gap: var(--space-3); }

/*
 * จำกัดความกว้างของเนื้อหาบนจอกว้างมาก
 *
 * ตารางที่ยืดเต็มจอ 27 นิ้วทำให้สายตาต้องกวาดจากคอลัมน์แรกไปคอลัมน์สุดท้ายไกลเกินไป
 * จนอ่านผิดแถว ส่วน margin: auto ทำให้เนื้อหาอยู่กลางแทนที่จะชิดซ้ายเป็นแถบยาว
 */
.content {
    padding: var(--space-5);
    flex: 1 1 auto;
    width: 100%;
    max-width: var(--content-max);
    margin: 0 auto;
}

.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}
.page-header__subtitle { color: var(--color-text-muted); margin: 0; max-width: 78ch; }

/*
 * แถบปุ่มของหัวหน้า — ปุ่มอันตรายต้องไม่ติดกับปุ่มหลัก
 * ระยะห่างที่ต่างกันคือสิ่งเดียวที่กันการกดพลาดได้จริงเมื่อมือกำลังรีบ
 */
.page-header__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.page-header__actions > .btn--danger,
.page-header__actions > form:last-child .btn--danger { margin-left: var(--space-4); }

/* ----------------------------------------------------------------- cards */

.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);

    /* การ์ดใช้เส้นขอบเป็นหลัก เงาเก็บไว้ให้สิ่งที่ลอยอยู่จริงเท่านั้น */
    box-shadow: none;
}
/*
 * หัวการ์ดใช้ได้ทั้งกับ <div> และหัวข้อจริง (h2/h3)
 * หน้าที่ยาวมากควรใช้หัวข้อจริง เพื่อให้กระโดดอ่านทีละหัวข้อได้
 */
.card__header {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
    font-size: var(--font-h3);
    font-weight: 600;
}

/* หัวข้อของส่วนในหน้ารายละเอียดที่ยาว — คั่นกลุ่มการ์ดหลายใบเข้าด้วยกัน */
.page-section {
    margin: var(--space-6) 0 var(--space-3);
    font-size: var(--font-h2);
}
.page-section:first-of-type { margin-top: 0; }
.card__body { padding: var(--space-4); }
.card__footer {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--color-border);
    background: var(--color-surface-alt);
}

.grid { display: grid; gap: var(--space-4); }
.grid--kpi { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

.kpi__label { color: var(--color-text-muted); font-size: var(--font-body-sm); margin-bottom: var(--space-1); }
.kpi__value { font-size: 26px; font-weight: 600; line-height: 1.2; }
.kpi__hint { color: var(--color-text-muted); font-size: var(--font-caption); margin-top: var(--space-1); }

/* ไม่มีตัวอักษรใดในระบบเล็กกว่า 12px — เกณฑ์ขั้นต่ำที่ภาษาไทยยังอ่านออก */

/* ---------------------------------------------------------------- badges */

.badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: var(--radius-pill);
    font-size: var(--font-caption);
    font-weight: 600;
    line-height: 1.7;
    white-space: nowrap;
}
.badge--success { background: var(--color-success-light); color: var(--color-success); }
.badge--danger  { background: var(--color-danger-light);  color: var(--color-danger); }
.badge--warning { background: var(--color-warning-light); color: var(--color-warning); }
.badge--info    { background: var(--color-info-light);    color: var(--color-info); }
.badge--neutral { background: var(--color-neutral-light); color: var(--color-neutral); }

/*
 * ป้ายสภาพแวดล้อม — ต้องแยกออกจากกันได้ในพริบตา
 * prod ใช้พื้นทึบไม่ใช่พื้นจาง เพราะเป็นที่เดียวที่การกดผิดมีผลกับข้อมูลจริง
 */
.badge-env-dev  { background: var(--color-neutral-light); color: var(--color-neutral); }
.badge-env-uat  { background: var(--color-warning-light); color: var(--color-warning); }
.badge-env-prod { background: var(--color-danger);        color: var(--color-on-danger); }

/*
 * แถบเตือนเต็มความกว้างใต้ header
 *
 * ใช้กับสถานะที่ผู้ใช้ต้องรู้ตลอดเวลาไม่ว่าจะอยู่หน้าไหน ไม่ใช่แค่หน้าใดหน้าหนึ่ง
 * เช่นโหมดจำลองที่คำสั่งทั้งหมดไม่ถูกเขียนลง Active Directory จริง
 */
.banner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-5);
    border-bottom: 1px solid;
    font-size: 14px;
}
.banner__title { font-weight: 600; }
.banner--warning {
    background: var(--color-warning-light);
    border-bottom-color: var(--color-warning-border);
    color: var(--color-warning-text);
}
.banner--warning a { color: var(--color-warning-text); text-decoration: underline; }

.status-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: middle;
}
.status-dot--ok { background: var(--color-success); }
.status-dot--warn { background: var(--color-warning); }
.status-dot--down { background: var(--color-danger); }

/* --------------------------------------------------------------- buttons */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: 7px 16px;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.btn[disabled] { opacity: .55; cursor: not-allowed; }

/*
 * ปุ่มหลัก: ตัวอักษรต้องอ้าง --color-on-primary ไม่ใช่ --color-text-inverse
 *
 * ในโหมดมืด --color-primary กลายเป็นฟ้าสว่าง ตัวอักษรขาวบนพื้นนั้นได้คอนทราสต์
 * ราว 2.3:1 ซึ่งต่ำกว่าเกณฑ์ AA มาก ส่วน --color-on-primary สลับเป็นสีเข้มให้เอง
 *
 * ตอน hover พื้นเปลี่ยนเป็น --color-primary-dark ซึ่งเข้มทั้งสองธีม
 * ตัวอักษรตรงนั้นจึงยังเป็นสีขาวได้ตามเดิม
 */
.btn--primary { background: var(--color-primary); color: var(--color-on-primary); }
.btn--primary:hover { background: var(--color-primary-dark); color: var(--color-text-inverse); }
.btn--secondary { border-color: var(--color-border-strong); }
.btn--secondary:hover { background: var(--color-surface-hover); }
.btn--danger { background: var(--color-danger); color: var(--color-on-danger); }
.btn--block { width: 100%; }
.btn--sm { padding: 4px 10px; font-size: 13px; }

/* ----------------------------------------------------------------- forms */

.form-group { margin-bottom: var(--space-4); }

/*
 * ตัวเลือกแบบมีคำอธิบายใต้หัวข้อ
 *
 * ใช้กับการตัดสินใจที่ผู้ใช้ต้องรู้ผลของแต่ละทางก่อนเลือก เช่นวิธีตั้งรหัสผ่าน
 * ครั้งแรกของบัญชีใหม่ ถ้าเขียนเป็นตัวเลือกเปล่า ๆ ผู้ดูแลจะเลือกตามความเคยชิน
 * โดยไม่รู้ว่าอีกทางปลอดภัยกว่าเพราะอะไร
 */
.form-choice {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) 0;
    cursor: pointer;
}

.form-choice input { margin-top: 3px; flex: 0 0 auto; }
.form-choice .form-hint { display: block; margin-top: 2px; }
.form-label { display: block; font-weight: 600; margin-bottom: var(--space-1); }
.form-label .required { color: var(--color-danger); }

.form-control {
    display: block;
    width: 100%;
    padding: 7px 10px;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
    font: inherit;
}
.form-control:focus {
    border-color: var(--color-primary);
    outline: 2px solid var(--color-primary-light);
    outline-offset: 0;
}
.form-control.is-invalid { border-color: var(--color-danger); }

.form-hint { color: var(--color-text-muted); font-size: var(--font-caption); margin-top: var(--space-1); }
.form-error { color: var(--color-danger); font-size: var(--font-body-sm); margin-top: var(--space-1); font-weight: 600; }

.form-row { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.form-check { display: flex; align-items: flex-start; gap: var(--space-2); }
.form-check input { margin-top: 4px; }

/* ---------------------------------------------------------------- alerts */

.alert {
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-4);
}
.alert--success { background: var(--color-success-light); border-color: var(--color-success-border); color: var(--color-success-text); }
.alert--danger  { background: var(--color-danger-light);  border-color: var(--color-danger-border);  color: var(--color-danger-text); }
.alert--warning { background: var(--color-warning-light); border-color: var(--color-warning-border); color: var(--color-warning-text); }
.alert--info    { background: var(--color-info-light);    border-color: var(--color-info-border);    color: var(--color-info-text); }
.alert__title { font-weight: 600; margin-bottom: var(--space-1); }
.alert ul { margin: var(--space-2) 0 0; padding-left: 20px; }

/* ---------------------------------------------------------------- tables */

.table-wrapper { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td {
    padding: 9px var(--space-3);
    border-bottom: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
}
.table th {
    background: var(--color-surface-alt);
    font-weight: 600;
    white-space: nowrap;
}
.table tbody tr:hover { background: var(--color-surface-hover); }
.table td.is-numeric, .table th.is-numeric { text-align: right; }

.empty-state { padding: var(--space-6); text-align: center; color: var(--color-text-muted); }

/* ------------------------------------------------------- auth + install */

.centered-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: var(--color-bg);
}
.centered-page__inner { width: 100%; max-width: 420px; }
.centered-page__inner--wide { max-width: 760px; }

.brand-mark {
    text-align: center;
    margin-bottom: var(--space-4);
}
.brand-mark__title { font-size: 20px; font-weight: 600; }
.brand-mark__subtitle { color: var(--color-text-muted); font-size: 13px; }

.divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: 12px;
    margin: var(--space-4) 0;
}
.divider::before, .divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--color-border);
}

.btn--microsoft {
    border-color: var(--color-border-strong);
    background: var(--color-surface);
}
.btn--microsoft:hover { background: var(--color-surface-hover); }

/* ขั้นตอนของตัวติดตั้ง */
.steps { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-4); }
.step-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 12px;
    border-radius: var(--radius-pill);
    background: var(--color-surface-sunken);
    color: var(--color-text-muted);
    font-size: var(--font-caption);
}
.step-pill.is-current { background: var(--color-primary); color: var(--color-on-primary); }
.step-pill.is-done { background: var(--color-success-light); color: var(--color-success); }

.check-list { list-style: none; margin: 0; padding: 0; }
.check-list li {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--color-border);
}
.check-list li:last-child { border-bottom: 0; }
.check-list__icon { flex: 0 0 20px; font-weight: 700; }
.check-list__icon--pass { color: var(--color-success); }
.check-list__icon--fail { color: var(--color-danger); }
.check-list__icon--warn { color: var(--color-warning); }
.check-list__body { flex: 1 1 auto; }
.check-list__name { font-weight: 600; }
.check-list__detail { color: var(--color-text-muted); font-size: 13px; }

/* ------------------------------------------------------------- แผนภูมิ */

/*
 * กราฟทั้งหมดวาดด้วย CSS ล้วน ไม่พึ่ง library ภายนอก
 * เพราะระบบต้องใช้งานได้บนเครือข่ายภายในที่ไม่มีอินเทอร์เน็ต
 * และการลด dependency ช่วยลดพื้นที่เสี่ยงด้านความปลอดภัยไปด้วย
 */
.bar-chart {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    height: 150px;
    padding-top: var(--space-2);
}
.bar-chart__item {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    min-width: 0;
}
.bar-chart__bar {
    width: 100%;
    max-width: 28px;
    background: var(--color-primary);
    border-radius: 2px 2px 0 0;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}
.bar-chart__bar-failed {
    width: 100%;
    background: var(--color-danger);
    border-radius: 2px 2px 0 0;
}
.bar-chart__label {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

.meter {
    height: 8px;
    background: var(--color-neutral-light);
    border-radius: 4px;
    overflow: hidden;
    margin-top: 4px;
}
.meter__fill {
    height: 100%;
    background: var(--color-primary);
}

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline li {
    position: relative;
    padding: 0 0 var(--space-4) var(--space-5);
    border-left: 2px solid var(--color-border);
}
.timeline li:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline li::before {
    content: "";
    position: absolute;
    left: -6px;
    top: 4px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-primary);
}
.timeline li.is-failed::before { background: var(--color-danger); }
.timeline li.is-info::before { background: var(--color-neutral); }
.timeline li.is-skipped::before { background: var(--color-border-strong); }
.timeline__title { font-weight: 600; }
.timeline__meta { color: var(--color-text-muted); font-size: 12px; }

.tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--color-border);
    margin-bottom: var(--space-4);
    flex-wrap: wrap;
}
.tabs a {
    padding: var(--space-2) var(--space-4);
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    color: var(--color-text-muted);
}
.tabs a:hover { background: var(--color-surface-hover); text-decoration: none; }
.tabs a.is-active {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text);
    font-weight: 600;
    margin-bottom: -1px;
}

.key-reveal {
    background: var(--color-warning-light);
    border: 1px solid var(--color-warning-border);
    border-radius: var(--radius-sm);
    padding: var(--space-3);
    word-break: break-all;
}

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: flex-end;
    margin-bottom: var(--space-4);
}
.filter-bar .form-group { margin-bottom: 0; min-width: 150px; }

.text-muted { color: var(--color-text-muted); }
.text-small { font-size: var(--font-body-sm); }
.text-caption { font-size: var(--font-caption); }
.text-right { text-align: right; }
.mt-0 { margin-top: 0; }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.flex-gap { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* ------------------------------------------------------------ responsive */

@media (max-width: 900px) {
    .app-shell { flex-direction: column; }
    .sidebar { width: 100%; flex: 0 0 auto; }
    .sidebar__nav { display: flex; flex-wrap: wrap; padding: var(--space-2); }
    .sidebar__section { display: none; }
    .sidebar__link { border-left: 0; border-radius: var(--radius-sm); }
    .content { padding: var(--space-4); }
    .topbar { padding: 0 var(--space-4); flex-wrap: wrap; height: auto; min-height: var(--header-height); }
}

@media print {
    .sidebar, .topbar, .btn { display: none !important; }
    body { background: #fff; }
}

/* ---------------------------------------------- empty state และกล่องยืนยัน */

/*
 * สถานะ "ไม่มีข้อมูล" ที่บอกทางไปต่อ
 * เดิมหลายหน้าขึ้นคำว่าไม่มีข้อมูลลอย ๆ บางหน้าซ้ำสองครั้งในกล่องเดียว
 */
.empty-state {
    text-align: center;
    padding: var(--space-6) var(--space-4);
    color: var(--color-text-muted);
}
.empty-state__icon {
    font-size: 28px;
    line-height: 1;
    margin-bottom: var(--space-3);
    color: var(--color-border-strong);
}
.empty-state__title {
    font-size: 17px;
    font-weight: 600;
    color: var(--color-text);
    margin: 0 0 var(--space-2);
}
.empty-state__message { margin: 0 0 var(--space-4); }

/*
 * กล่องยืนยัน — ใช้กับการกระทำที่ย้อนกลับไม่ได้เท่านั้น
 * ถ้าใช้พร่ำเพรื่อผู้ใช้จะกดผ่านโดยไม่อ่าน แล้วด่านนี้จะไม่เหลือความหมาย
 */
body.has-modal { overflow: hidden; }

.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, .45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    z-index: 100;
}
.modal {
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: var(--space-5);
    width: 100%;
    max-width: 460px;
}
.modal__title { font-size: var(--font-h2); margin: 0 0 var(--space-3); }
.modal__target {
    background: var(--color-neutral-light);
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
    margin: 0 0 var(--space-3);
    word-break: break-all;
}
/*
 * ข้อความในกล่องยืนยันบางครั้งเป็นรายการหลายบรรทัด เช่นสรุปว่าจะเปลี่ยนค่าอะไรบ้าง
 * pre-line ทำให้ขึ้นบรรทัดใหม่ตามที่ส่งมา โดยยังตัดคำอัตโนมัติเมื่อบรรทัดยาวเกิน
 */
.modal__message {
    color: var(--color-text-muted);
    margin: 0 0 var(--space-4);
    white-space: pre-line;
    max-height: 40vh;
    overflow-y: auto;
}
.modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.confirm-form { display: inline; }

/* ------------------------------------------------------- segmented control */

/*
 * ใช้แทน checkbox เมื่อทางเลือกมีผลต่างกันมาก เช่นทดลองกับของจริง
 * checkbox เล็ก ๆ สื่อไม่ได้เลยว่าการติ๊กหมายถึงการแตะข้อมูลจริงขององค์กร
 */
.segmented {
    display: inline-flex;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    overflow: hidden;
}
.segmented__option { position: relative; }
.segmented__option input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}
.segmented__option span {
    display: block;
    padding: var(--space-2) var(--space-4);
    font-weight: 600;
    background: var(--color-surface);
    color: var(--color-text-muted);
}
.segmented__option input:checked + span {
    background: var(--color-primary);
    color: var(--color-on-primary);
}
.segmented__option input:checked + span.is-live {
    background: var(--color-danger);
    color: var(--color-on-danger);
}
.segmented__option input:focus-visible + span {
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
}

/* ฟอร์มที่กำลังจะมีผลกับข้อมูลจริง ต้องเห็นได้ทันทีว่าไม่ใช่การทดลอง */
.form-live-warning {
    border: 2px solid var(--color-danger);
    border-radius: var(--radius);
    padding: var(--space-4);
}

/* ===================================================================== */
/*  Toast — แจ้งผลของการกระทำที่เกิดขึ้นแล้ว                              */
/* ===================================================================== */
/*
 * ใช้กับผลที่เกิดขึ้นแล้วและไม่ต้องให้ผู้ใช้ตัดสินใจอะไรต่อ เช่น "ลบกฎแล้ว"
 * สิ่งที่ต้องอ่านก่อนตัดสินใจยังใช้ .alert ในหน้าเหมือนเดิม เพราะ toast หายไปเอง
 */
.toast-region {
    position: fixed;
    right: var(--space-4);
    bottom: var(--space-4);
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 380px;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-left: 4px solid var(--color-neutral);
    border-radius: var(--radius);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    animation: toast-in .16s ease-out;
}

.toast--success { border-left-color: var(--color-success); }
.toast--warning { border-left-color: var(--color-warning); }
.toast--danger  { border-left-color: var(--color-danger); }

.toast__body { flex: 1 1 auto; min-width: 0; }
.toast__title { font-weight: 600; }

.toast__close {
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: 17px;
    line-height: 1;
    padding: 0 2px;
}

@keyframes toast-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: none; }
}

/* ===================================================================== */
/*  Drawer — แผงเลื่อนจากขวาสำหรับฟอร์มเพิ่มและแก้ไข                      */
/* ===================================================================== */
/*
 * เหตุผลที่ใช้ drawer แทนการวางฟอร์มไว้บนสุดของหน้า: ฟอร์มที่อยู่บนสุดดันรายการจริง
 * ลงไปใต้จอ ผู้ใช้ที่เข้ามาเพื่อ "ดู" ต้องเลื่อนผ่านฟอร์มที่ไม่ได้จะใช้ทุกครั้ง
 */
.drawer-overlay {
    position: fixed;
    inset: 0;
    z-index: 50;
    background: rgba(16, 24, 40, .45);
    display: flex;
    justify-content: flex-end;
}

.drawer {
    width: min(560px, 100%);
    height: 100%;
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    display: flex;
    flex-direction: column;
    animation: drawer-in .18s ease-out;
}

.drawer__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.drawer__title { margin: 0; font-size: var(--font-h2); }
.drawer__body { padding: var(--space-4); overflow-y: auto; flex: 1 1 auto; }

.drawer__footer {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    padding: var(--space-4);
    border-top: 1px solid var(--color-border);
}

.drawer__close {
    border: 0;
    background: none;
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    color: var(--color-text-muted);
}

@keyframes drawer-in {
    from { transform: translateX(16px); opacity: .6; }
    to   { transform: none; opacity: 1; }
}

body.has-drawer { overflow: hidden; }

/* ===================================================================== */
/*  ตัวเลือกช่วงเวลา                                                      */
/* ===================================================================== */
.date-range {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
}

.date-range__presets { display: flex; gap: var(--space-1); }

.date-range__preset {
    border: 1px solid var(--color-border-strong);
    background: var(--color-surface);
    color: var(--color-text);
    border-radius: var(--radius-sm);
    padding: 5px 10px;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.date-range__preset.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
}

.date-range__custom { display: flex; gap: var(--space-2); }

/* ===================================================================== */
/*  Chip multi-select — แทนช่องที่ให้พิมพ์ค่าคั่นด้วยจุลภาค                */
/* ===================================================================== */
/*
 * ค่าที่ส่งไปฝั่งเซิร์ฟเวอร์ยังเป็นสตริงคั่นด้วยจุลภาคเหมือนเดิม
 * ตัวนี้เป็นเพียงชั้นแสดงผลที่เขียนค่ากลับเข้า input ตัวจริงที่ซ่อนอยู่
 */
.chip-field {
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius);
    padding: var(--space-2);
    background: var(--color-surface);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    min-height: 40px;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    background: var(--color-primary-light);
    color: var(--color-primary-dark);
    border-radius: 999px;
    padding: 2px 4px 2px 10px;
    font-size: 13px;
    font-weight: 600;
}

.chip__remove {
    border: 0;
    background: none;
    cursor: pointer;
    color: inherit;
    font-size: 15px;
    line-height: 1;
    padding: 0 4px;
}

.chip-field__options {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-2);
}

/* ===================================================================== */
/*  Toggle switch                                                        */
/* ===================================================================== */
/*
 * position: relative เพื่อให้ checkbox ที่ซ่อนไว้ข้างในอยู่ในกรอบของ label
 *
 * checkbox ถูกซ่อนด้วย position: absolute ถ้า label ไม่ได้เป็น positioned ancestor
 * มันจะไปยึดตำแหน่งกับ viewport แทนที่จะยึดกับ label ของตัวเอง
 *
 * (ตอนแรกผมเข้าใจว่านี่เป็นต้นเหตุที่หน้า /actions รายงาน scrollWidth 722px
 * บนจอ 390px แต่วัดแล้วไม่ใช่ — หน้านั้นเลื่อนซ้ายขวาไม่ได้จริง ค่าที่เห็นเป็น
 * ควิร์กของ Chrome ที่นับ sticky th ในกรอบเลื่อน กฎนี้เก็บไว้เพราะถูกต้องอยู่แล้ว
 * ไม่ใช่เพราะแก้อาการนั้น)
 */
.switch { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; position: relative; }
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }

.switch__track {
    width: 38px;
    height: 22px;
    border-radius: 999px;
    background: var(--color-border-strong);
    position: relative;
    transition: background .12s ease-in-out;
    flex: 0 0 auto;
}

.switch__track::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-knob);
    transition: transform .12s ease-in-out;
}

.switch input:checked + .switch__track { background: var(--color-success); }
.switch input:checked + .switch__track::after { transform: translateX(16px); }

.switch input:focus-visible + .switch__track {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* แถบลอยด้านล่างเมื่อแก้หลายแถวแล้วยังไม่บันทึก */
.save-bar {
    position: sticky;
    bottom: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--color-primary-dark);
    color: var(--color-text-inverse);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    margin-top: var(--space-4);
}

.save-bar[hidden] { display: none; }

/*
 * เมื่อมีของค้างยังไม่ได้บันทึก แถบจะมีแถบสีเหลืองคาดไว้ด้านซ้าย
 * เป็นสัญญาณที่ไม่ใช่ตัวอักษร สำหรับคนที่กวาดสายตาผ่านโดยไม่ได้อ่าน
 * ส่วนข้อความในแถบบอกจำนวนอยู่แล้ว จึงไม่ได้พึ่งสีเพียงอย่างเดียว
 */
.save-bar--dirty { border-left: 4px solid var(--color-warning); }
.save-bar__actions { display: flex; gap: var(--space-2); }

/* ===================================================================== */
/*  ตาราง — หัวตารางตรึง เรียงได้ ตัวเลขชิดขวา                            */
/* ===================================================================== */
.table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

.table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.table th.is-sortable { cursor: pointer; white-space: nowrap; }

.table th.is-sortable::after {
    content: "\2195";
    margin-left: 6px;
    color: var(--color-text-muted);
    font-size: 12px;
}

.table th.is-sortable[aria-sort="ascending"]::after { content: "\2191"; color: var(--color-text); }
.table th.is-sortable[aria-sort="descending"]::after { content: "\2193"; color: var(--color-text); }

/* ปุ่มคัดลอกรหัส แสดงเมื่อชี้ที่แถว เพื่อไม่ให้ตารางรกตลอดเวลา */
.copyable { display: inline-flex; align-items: center; gap: var(--space-1); }

.copyable__btn {
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: 13px;
    padding: 0 2px;
    opacity: 0;
    transition: opacity .1s ease-in-out;
}

tr:hover .copyable__btn,
.copyable__btn:focus-visible { opacity: 1; }

/* ===================================================================== */
/*  Skeleton — ใช้ระหว่างรอข้อมูลที่โหลดด้วย JavaScript                    */
/* ===================================================================== */
.skeleton {
    background: linear-gradient(90deg, var(--color-neutral-light) 25%, var(--color-skeleton-sheen) 37%, var(--color-neutral-light) 63%);
    background-size: 400% 100%;
    animation: skeleton-shimmer 1.2s ease-in-out infinite;
    border-radius: var(--radius-sm);
    height: 14px;
}

@keyframes skeleton-shimmer {
    from { background-position: 100% 50%; }
    to   { background-position: 0 50%; }
}

/* ===================================================================== */
/*  ผู้ช่วย AI — ปุ่มลอยมุมขวาล่างและหน้าต่างแชต                          */
/* ===================================================================== */
/*
 * อยู่เหนือเนื้อหาแต่ต่ำกว่ากล่องยืนยัน (100) และแผงด้านข้าง (50) ไม่ได้
 * จึงใช้ 45 — กล่องยืนยันที่เปิดจากหน้าต้องบังผู้ช่วยได้เสมอ
 * toast ถูกยกขึ้นเหนือปุ่มลอยเพื่อไม่ให้ทับกัน
 */
.assistant-fab {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    z-index: 45;
    width: 52px;
    height: 52px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--radius-pill);
    background: var(--color-primary);
    color: var(--color-on-primary);
    box-shadow: var(--shadow-md);
    cursor: pointer;
}
.assistant-fab:hover { background: var(--color-primary-dark); color: var(--color-text-inverse); }
.assistant-fab:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
.assistant-fab[aria-expanded="true"] { display: none; }

body:has(.assistant-fab) .toast-region { bottom: 88px; }

.assistant {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    z-index: 45;
    width: 400px;
    max-width: calc(100vw - 2 * var(--space-4));
    height: 600px;
    max-height: calc(100vh - 2 * var(--space-5));
    display: flex;
    flex-direction: column;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    animation: toast-in .16s ease-out;
}
.assistant[hidden] { display: none; }

.assistant__header {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
}
.assistant__heading { flex: 1 1 auto; min-width: 0; }
.assistant__title { margin: 0; font-size: var(--font-h2); }
.assistant__model { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.assistant__close {
    display: inline-flex;
    padding: var(--space-1);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
}
.assistant__close:hover { background: var(--color-surface-hover); color: var(--color-text); }

.assistant__log {
    flex: 1 1 auto;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-surface-alt);
}

.assistant__msg {
    max-width: 88%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-lg);
    line-height: 1.55;
    overflow-wrap: anywhere;
}
.assistant__msg--user {
    align-self: flex-end;
    background: var(--color-primary);
    color: var(--color-on-primary);
    white-space: pre-wrap;
}
.assistant__msg--ai {
    align-self: flex-start;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
}
.assistant__msg--error {
    align-self: flex-start;
    border: 1px solid var(--color-danger-border);
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}
.assistant__msg p { margin: 0 0 var(--space-2); }
.assistant__msg p:last-child,
.assistant__msg ul:last-child { margin-bottom: 0; }
.assistant__msg ul { margin: 0 0 var(--space-2); padding-left: var(--space-5); }

.assistant__meta { margin-top: var(--space-1); font-size: 12px; color: var(--color-text-muted); }

.assistant__typing { color: var(--color-text-muted); font-style: italic; }

.assistant__suggestions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-2);
}
.assistant__suggestion {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-pill);
    background: var(--color-surface);
    color: var(--color-primary);
    font: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}
.assistant__suggestion:hover { background: var(--color-primary-light); }

.assistant__form {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4) var(--space-2);
    border-top: 1px solid var(--color-border);
}
.assistant__input { flex: 1 1 auto; resize: none; min-height: 44px; max-height: 140px; }
.assistant__send { flex: 0 0 auto; }

.assistant__notice { margin: 0; padding: 0 var(--space-4) var(--space-3); line-height: 1.4; }

/* จอเล็ก — หน้าต่างแชตเต็มจอ เพราะกว้าง 400px ไม่เหลือที่ให้เนื้อหาอยู่แล้ว */
@media (max-width: 640px) {
    .assistant-fab { right: var(--space-4); bottom: var(--space-4); }
    .assistant {
        right: 0;
        bottom: 0;
        width: 100%;
        max-width: 100%;
        height: 100%;
        max-height: 100%;
        border-radius: 0;
    }
}

/*
 * ผู้ที่ตั้งค่าระบบปฏิบัติการว่าลดการเคลื่อนไหว มักตั้งเพราะการเคลื่อนไหวทำให้เวียนหัว
 * ไม่ใช่เพราะไม่ชอบ จึงต้องปิดให้จริง ไม่ใช่แค่ทำให้เร็วขึ้น
 */
@media (prefers-reduced-motion: reduce) {
    .skeleton,
    .toast,
    .drawer,
    .assistant { animation: none; }
}

/* ===================================================================== */
/*  โหมดมืด — ใช้ token ชุดเดียวกัน ไม่มีการเขียนสีซ้ำในแต่ละหน้า          */
/* ===================================================================== */
/*
 * ทำโดยเปลี่ยนค่า token ที่ราก ไม่ได้เขียนกฎใหม่ให้แต่ละ component
 * ดังนั้น component ที่เพิ่มในอนาคตจะได้โหมดมืดไปด้วยโดยไม่ต้องทำอะไรเพิ่ม
 * เงื่อนไขเดียวคือห้ามเขียนค่าสีตรง ๆ ในหน้า ต้องใช้ token เสมอ
 *
 * ตามการตั้งค่าของระบบปฏิบัติการผู้ใช้ ไม่มีสวิตช์ในแอป เพราะคนที่ต้องการโหมดมืด
 * มักตั้งไว้ที่ระดับเครื่องอยู่แล้ว และสวิตช์ในแอปที่ไม่ตรงกับเครื่องสร้างความสับสนกว่า
 */
/*
 * ค่าของโหมดมืด
 *
 * ประกาศรายการเดียวกันสองที่ เพราะ CSS ธรรมดารวมสองเงื่อนไขนี้ไว้ในกฎเดียวไม่ได้
 * (อันหนึ่งเป็น attribute บน <html> อีกอันอยู่ใน media query)
 *
 *   :root[data-theme="dark"]                      ผู้ใช้กดเลือกมืดเอง
 *   @media(dark) :root:not([data-theme="light"])  ตามเครื่อง และไม่ได้บังคับสว่างไว้
 *
 * ทั้งสองชุดต้องมีรายการ token เหมือนกันเสมอ มีเทสใน UiContractTest คุมไว้
 * และเพราะทุกความต่างระหว่างธีมถูกย้ายมาเป็น token หมดแล้ว component ที่เพิ่ม
 * ในอนาคตจึงได้โหมดมืดไปด้วยโดยไม่ต้องแตะบล็อกนี้
 */
:root[data-theme="dark"] {

    --color-bg: #12161b;
    --color-surface: #1a1f26;
    --color-border: #2c343d;
    --color-border-strong: #5b6b7d;

    --color-text: #e6e9ed;
    --color-text-muted: #9aa5b1;

    /* สีหลักต้องสว่างขึ้นบนพื้นมืด ไม่งั้นลิงก์กับปุ่มจะจมไปกับพื้น */
    --color-primary: #4a9fd8;
    --color-primary-dark: #17384f;
    --color-primary-light: #16303f;

    --color-success: #5cc48d;
    --color-success-light: #14301f;
    --color-danger: #f2857c;
    --color-danger-light: #331815;
    --color-warning: #e0b44b;
    --color-warning-light: #302611;
    --color-info: #6bb2e8;
    --color-info-light: #14263a;
    --color-neutral: #a3adb8;
    --color-neutral-light: #242b33;

    /*
     * พื้นผิวรองต้อง "สว่างขึ้น" จากพื้นหลัง ไม่ใช่มืดลงเหมือนในโหมดสว่าง
     * ถ้าคัดลอกค่าของโหมดสว่างมาตรง ๆ หัวตารางจะกลายเป็นแถบขาวกลางหน้าจอมืด
     */
    --color-surface-alt: #202730;
    --color-surface-hover: #262e38;
    --color-surface-sunken: #2a323c;

    /* พื้นทึบในโหมดมืดเป็นสีสว่าง ตัวอักษรบนพื้นนั้นจึงต้องเป็นสีเข้ม */
    --color-on-primary: #0d1419;
    --color-on-danger: #2a0f0c;

    /*
     * กล่องแจ้งเตือน: พื้นเข้ม ตัวอักษรจึงต้องใช้สีสถานะที่สว่าง
     * ไม่ใช่สีเข้มชุดเดียวกับโหมดสว่าง ซึ่งจะได้คอนทราสต์ราว 1.3:1 คืออ่านไม่ออก
     */
    --color-success-border: #245c3d;
    --color-success-text: #7fd6a8;
    --color-danger-border: #6b2f29;
    --color-danger-text: #f6a8a1;
    --color-warning-border: #5c4a1c;
    --color-warning-text: #eac876;
    --color-info-border: #24496b;
    --color-info-text: #8ec6f0;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 4px 14px rgba(0, 0, 0, .5);

    /* ค่าที่เหลือซึ่งต่างกันตามธีม — คู่กับที่ประกาศไว้ในโหมดสว่าง */
    --color-knob: #e6e9ed;
    --color-skeleton-sheen: #2c343d;
    --color-overlay-soft: rgba(255, 255, 255, .12);

    color-scheme: dark;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {

        --color-bg: #12161b;
        --color-surface: #1a1f26;
        --color-border: #2c343d;
        --color-border-strong: #5b6b7d;

        --color-text: #e6e9ed;
        --color-text-muted: #9aa5b1;

        /* สีหลักต้องสว่างขึ้นบนพื้นมืด ไม่งั้นลิงก์กับปุ่มจะจมไปกับพื้น */
        --color-primary: #4a9fd8;
        --color-primary-dark: #17384f;
        --color-primary-light: #16303f;

        --color-success: #5cc48d;
        --color-success-light: #14301f;
        --color-danger: #f2857c;
        --color-danger-light: #331815;
        --color-warning: #e0b44b;
        --color-warning-light: #302611;
        --color-info: #6bb2e8;
        --color-info-light: #14263a;
        --color-neutral: #a3adb8;
        --color-neutral-light: #242b33;

        /*
         * พื้นผิวรองต้อง "สว่างขึ้น" จากพื้นหลัง ไม่ใช่มืดลงเหมือนในโหมดสว่าง
         * ถ้าคัดลอกค่าของโหมดสว่างมาตรง ๆ หัวตารางจะกลายเป็นแถบขาวกลางหน้าจอมืด
         */
        --color-surface-alt: #202730;
        --color-surface-hover: #262e38;
        --color-surface-sunken: #2a323c;

        /* พื้นทึบในโหมดมืดเป็นสีสว่าง ตัวอักษรบนพื้นนั้นจึงต้องเป็นสีเข้ม */
        --color-on-primary: #0d1419;
        --color-on-danger: #2a0f0c;

        /*
         * กล่องแจ้งเตือน: พื้นเข้ม ตัวอักษรจึงต้องใช้สีสถานะที่สว่าง
         * ไม่ใช่สีเข้มชุดเดียวกับโหมดสว่าง ซึ่งจะได้คอนทราสต์ราว 1.3:1 คืออ่านไม่ออก
         */
        --color-success-border: #245c3d;
        --color-success-text: #7fd6a8;
        --color-danger-border: #6b2f29;
        --color-danger-text: #f6a8a1;
        --color-warning-border: #5c4a1c;
        --color-warning-text: #eac876;
        --color-info-border: #24496b;
        --color-info-text: #8ec6f0;

        --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
        --shadow-md: 0 4px 14px rgba(0, 0, 0, .5);

        /* ค่าที่เหลือซึ่งต่างกันตามธีม — คู่กับที่ประกาศไว้ในโหมดสว่าง */
        --color-knob: #e6e9ed;
        --color-skeleton-sheen: #2c343d;
        --color-overlay-soft: rgba(255, 255, 255, .12);

        color-scheme: dark;
    }
}

/* ===================================================================== */
/*  ขนาดหน้าจอแท็บเล็ตและมือถือ                                          */
/* ===================================================================== */
/*
 * แถบเมนูกลายเป็น drawer ที่เปิดด้วยปุ่มบนแถบบน ตารางเลื่อนแนวนอนในกรอบของตัวเอง
 * เกณฑ์ที่ใช้ทดสอบคือ 768px (แท็บเล็ต) และ 390px (มือถือ)
 */
/*
 * ปุ่มเปิดเมนูกับปุ่มกางตัวกรองมีไว้ให้จอแคบ บนเดสก์ท็อปจึงซ่อนเป็นค่าเริ่มต้น
 *
 * กฎนี้ต้องอยู่ "ก่อน" media query ที่เปิดให้เห็น เพราะทั้งสองมี specificity
 * เท่ากัน (0,1,0) ตัวที่อยู่ทีหลังในไฟล์จึงชนะ
 *
 * เดิมกฎนี้อยู่ท้ายบล็อก responsive คือหลัง media query ผลคือมันชนะทุกขนาดจอ
 * ปุ่มเปิดเมนูจึงถูกซ่อนตลอด และเพราะแถบเมนูที่ <=900px ถูกเลื่อนออกนอกจอ
 * (translateX(-100%)) ผู้ใช้บนมือถือกับแท็บเล็ตจึงเปิดเมนูไม่ได้เลย
 * วัดจากเบราว์เซอร์จริงที่ 390px และ 768px: ปุ่มมีขนาด 0x0 ส่วนแถบเมนูขอบขวาอยู่ที่ x=0
 *
 * ส่วนปุ่มกางตัวกรองก็โดนแบบเดียวกัน ทำให้ที่ <=560px ตัวกรองถูกซ่อนโดยไม่มีทางกางออกมา
 */
.nav-toggle,
.filter-toggle { display: none; }

@media (max-width: 900px) {
    .app-shell { display: block; }

    .sidebar {
        position: fixed;
        inset: 0 auto 0 0;
        z-index: 55;
        transform: translateX(-100%);
        transition: transform .18s ease-out;
    }

    body.has-nav .sidebar { transform: none; }

    body.has-nav::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 54;
        background: rgba(16, 24, 40, .45);
    }

    .nav-toggle { display: inline-flex; }
    .content { padding: var(--space-4); }
    .topbar { flex-wrap: wrap; row-gap: var(--space-2); }
    .topbar__status { flex-wrap: wrap; }
}

@media (max-width: 560px) {
    .page-header { flex-direction: column; align-items: flex-start; }
    .grid--kpi { grid-template-columns: 1fr 1fr; }
    .drawer { width: 100%; }
    .toast-region { left: var(--space-3); right: var(--space-3); max-width: none; }

    /* ตัวกรองยุบเป็นปุ่มเดียว กดแล้วค่อยกาง */
    .filter-bar[data-collapsible] { display: none; }
    .filter-bar[data-collapsible].is-open { display: grid; }
    .filter-toggle { display: inline-flex; }
}


/* ===================================================================== */
/*  โครงหน้า — แถบเมนู แถบบน breadcrumb และเมนูผู้ใช้                      */
/* ===================================================================== */

/*
 * ข้อความที่มีไว้ให้โปรแกรมอ่านหน้าจออ่านเท่านั้น
 * ใช้ clip แทน display:none เพราะสิ่งที่ display:none จะไม่ถูกอ่านออกมาเลย
 */
.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;
}

.icon { flex: 0 0 auto; vertical-align: -3px; }

/* ------------------------------------------------------ แถบเมนูด้านซ้าย */

.sidebar__brand { justify-content: space-between; }
.sidebar__brand-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.sidebar__collapse {
    border: 0;
    background: none;
    color: var(--sidebar-text-muted);
    cursor: pointer;
    padding: var(--space-1);
    display: inline-flex;
    transition: transform .15s ease-in-out;
}

.sidebar__collapse:hover { color: #fff; }

.sidebar__link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-top: 6px;
    padding-bottom: 6px;
}

@media (max-width: 900px) {
    .sidebar__link {
        padding-top: var(--space-2);
        padding-bottom: var(--space-2);
    }
}

.sidebar__label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* เดิมใช้ float ซึ่งใช้ร่วมกับ flex ไม่ได้ */
.sidebar__badge { float: none; margin-left: auto; }

.sidebar__icon { display: inline-flex; color: var(--sidebar-accent); }
.sidebar__link.is-active .sidebar__icon { color: #fff; }

/*
 * โหมดย่อ — เหลือเฉพาะไอคอน
 *
 * ค่าที่ผู้ใช้เลือกถูกจำไว้ในเบราว์เซอร์ และคลาสถูกใส่จาก JavaScript
 * ถ้า JavaScript ไม่ทำงาน เมนูจะกางอยู่เสมอซึ่งเป็นสถานะที่ใช้งานได้ปกติ
 */
body.nav-collapsed { --sidebar-width: 64px; }
body.nav-collapsed .sidebar__brand-text,
body.nav-collapsed .sidebar__label,
body.nav-collapsed .sidebar__section,
body.nav-collapsed .sidebar__badge { display: none; }

body.nav-collapsed .sidebar__brand { padding: 0; justify-content: center; }
body.nav-collapsed .sidebar__link { justify-content: center; padding: var(--space-3) 0; }
body.nav-collapsed .sidebar__collapse { transform: rotate(180deg); }

/*
 * หมวดในโหมดย่อถูกซ่อน จึงต้องมีเส้นคั่นแทน ไม่งั้นเมนู 16 รายการจะกลายเป็น
 * แถวไอคอนยาวที่ไม่มีการแบ่งกลุ่มอะไรเลย
 */
body.nav-collapsed .sidebar__section + .sidebar__link {
    border-top: 1px solid rgba(255, 255, 255, .12);
}

/* ------------------------------------------------------------ แถบบน */

.breadcrumb {
    display: flex;
    align-items: center;
    min-width: 0;
    font-size: var(--font-body-sm);
}

.breadcrumb__list {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.breadcrumb__muted { color: var(--color-text-muted); }
.breadcrumb__sep { color: var(--color-border-strong); }
.breadcrumb__current {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar__search {
    position: relative;
    margin-left: auto;
    max-width: 380px;
    flex: 1 1 260px;
}

.topbar__search .form-control { padding-left: 34px; padding-right: 64px; }

.topbar__search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--color-text-muted);
    display: inline-flex;
    pointer-events: none;
}

.topbar__kbd {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 0 5px;
    background: var(--color-bg);
    pointer-events: none;
}

.topbar__status { margin-left: 0; }

.topbar__health { display: inline-flex; align-items: center; gap: var(--space-2); color: inherit; }
.topbar__health:hover { text-decoration: none; color: var(--color-primary); }

/* ------------------------------------------------------- เมนูผู้ใช้ */

.user-menu { position: relative; }
.user-menu > summary { list-style: none; cursor: pointer; }
.user-menu > summary::-webkit-details-marker { display: none; }

.user-menu__trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius);
}

.user-menu__trigger:hover { background: var(--color-neutral-light); }

.user-menu__avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--color-primary);
    color: var(--color-on-primary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 13px;
    flex: 0 0 auto;
}

.user-menu__panel {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 30;
    min-width: 240px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md);
    overflow: hidden;
}

.user-menu__identity { padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--color-border); }

.user-menu__item {
    display: block;
    width: 100%;
    text-align: left;
    padding: var(--space-2) var(--space-4);
    border: 0;
    background: none;
    font: inherit;
    color: var(--color-text);
    cursor: pointer;
}

.user-menu__item:hover { background: var(--color-neutral-light); text-decoration: none; }
.user-menu__item--danger { color: var(--color-danger); }

@media (max-width: 900px) {
    .user-menu__name { display: none; }
    .topbar__search { flex-basis: 100%; order: 5; max-width: none; }
}

/* ช่องที่ต้องกว้างกว่าปกติ เช่น URL — เดิมเขียน style ติดไว้ในหน้า */
.form-group--wide { min-width: 320px; }

/* ===================================================================== */
/*  ลำดับเหตุการณ์ — บอกผลด้วยไอคอนและสี ไม่ใช่สีอย่างเดียว                */
/* ===================================================================== */
/*
 * WCAG 1.4.1 ห้ามสื่อความหมายด้วยสีเพียงอย่างเดียว และในทางปฏิบัติ จุดสีเล็ก ๆ
 * ที่ต่างกันนิดเดียวก็ถูกกวาดตาผ่านแม้แต่คนที่แยกสีได้ปกติ
 */
.timeline li { padding-left: var(--space-6); }

.timeline li::before { display: none; }

.timeline__mark {
    position: absolute;
    left: -12px;
    top: 0;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    line-height: 1;
    background: var(--color-neutral-light);
    color: var(--color-neutral);
    border: 2px solid var(--color-surface);
}

.timeline li.is-success .timeline__mark { background: var(--color-success-light); color: var(--color-success); }
.timeline li.is-warning .timeline__mark { background: var(--color-warning-light); color: var(--color-warning); }
.timeline li.is-failed .timeline__mark  { background: var(--color-danger-light);  color: var(--color-danger); }
.timeline li.is-info .timeline__mark    { background: var(--color-info-light);    color: var(--color-info); }

/* เส้นของขั้นที่เป็นคำเตือนและล้มเหลวเน้นให้เห็นว่าช่วงนั้นมีเรื่อง */
.timeline li.is-warning { border-left-color: var(--color-warning); }
.timeline li.is-failed { border-left-color: var(--color-danger); }

.timeline__title { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }

/* ===================================================================== */
/*  แถบสรุปด้านบนของหน้ารายละเอียด                                        */
/* ===================================================================== */
.summary-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    padding: var(--space-3) var(--space-4);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.summary-bar__item { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

.summary-bar__label {
    color: var(--color-text-muted);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* ===================================================================== */
/*  กล่อง JSON — พับได้ คัดลอกได้ และเน้นฟิลด์ที่เปลี่ยน                   */
/* ===================================================================== */
.json-block { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; }

.json-block__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--color-neutral-light);
    border-bottom: 1px solid var(--color-border);
}

.json-block__actions { display: flex; gap: var(--space-2); }

.json-block__code {
    margin: 0;
    padding: var(--space-3);
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.55;
}

/* พับไว้ก่อนแล้วไล่สีจางลงตรงขอบ ให้เห็นว่ายังมีเนื้อหาต่ออยู่ข้างล่าง */
.json-block.is-collapsible .json-block__code {
    max-height: 340px;
    overflow-y: hidden;
    mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
    -webkit-mask-image: linear-gradient(to bottom, #000 78%, transparent 100%);
}

.json-block.is-collapsible.is-open .json-block__code {
    max-height: none;
    overflow-y: visible;
    mask-image: none;
    -webkit-mask-image: none;
}

.json-block__mark {
    background: var(--color-warning-light);
    color: inherit;
    display: inline-block;
    width: 100%;
}

/* ===================================================================== */
/*  รายละเอียดทางเทคนิคที่พับเก็บไว้                                      */
/* ===================================================================== */
/*
 * ค่าทางเทคนิคยังต้องมีให้ผู้ดูแลใช้ตอนไล่ปัญหา แต่ไม่ควรเป็นสิ่งแรกที่ทุกคนเห็น
 * ใช้ <details> เพราะเปิดปิดได้เองโดยไม่ต้องใช้ JavaScript และรองรับคีย์บอร์ดมาในตัว
 */
.tech-details { margin-top: var(--space-3); border-top: 1px solid var(--color-border); padding-top: var(--space-2); }

.tech-details > summary {
    cursor: pointer;
    color: var(--color-text-muted);
    font-size: 13px;
}

.tech-details > summary:hover { color: var(--color-text); }

.tech-details__list {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-1) var(--space-3);
    margin: var(--space-2) 0 0;
    font-size: 13px;
}

.tech-details__list dt { color: var(--color-text-muted); }
.tech-details__list dd { margin: 0; word-break: break-word; }

/* ป้ายสถานะในการ์ด KPI — เดิมกำหนดขนาดด้วย style ติดในหน้า */
.kpi__value--status { font-size: 18px; }

/* ===================================================================== */
/*  แดชบอร์ด                                                             */
/* ===================================================================== */
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* กล่อง "ต้องจัดการ" ต้องเห็นก่อนอย่างอื่นเสมอเมื่อมีของค้าง */
.card--attention { border-left: 4px solid var(--color-warning); }

.action-list { list-style: none; margin: 0; padding: 0; }
.action-list li + li { border-top: 1px solid var(--color-border); }

.action-list__link {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    color: var(--color-text);
}

.action-list__link:hover { text-decoration: none; background: var(--color-neutral-light); }
.action-list__label { font-weight: 600; }
.action-list__go { margin-left: auto; color: var(--color-text-muted); font-size: 20px; line-height: 1; }

/* แนวโน้มเทียบช่วงก่อนหน้า — สีตัดสินจากความหมาย ไม่ใช่จากทิศของลูกศร */
.kpi__trend { display: flex; align-items: center; gap: var(--space-1); }
.kpi__trend--good { color: var(--color-success); }
.kpi__trend--bad { color: var(--color-danger); }
.kpi__trend--neutral { color: var(--color-text-muted); }

.chart { display: flex; gap: var(--space-3); }

.chart__axis {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    height: 140px;
    font-size: 12px;
    color: var(--color-text-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

/*
 * เส้นกริดจาง ๆ ให้เทียบความสูงของแท่งกับแกน Y ได้
 * ใช้ background ของพื้นที่กราฟแทนการวาดเส้นทีละเส้น
 */
.chart__plot {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 140px;
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    border-bottom: 1px solid var(--color-border);
    background-image: repeating-linear-gradient(
        to top,
        transparent 0,
        transparent 69px,
        var(--color-border) 69px,
        var(--color-border) 70px
    );
}

.chart__col { display: flex; flex-direction: column; justify-content: flex-end; align-items: center; flex: 1 1 0; min-width: 10px; }

.chart__bar {
    width: 100%;
    max-width: 34px;
    background: var(--color-primary);
    border-radius: 2px 2px 0 0;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

.chart__bar-failed { width: 100%; background: var(--color-danger); border-radius: 2px 2px 0 0; }

.chart__label {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: var(--space-1);
    font-variant-numeric: tabular-nums;
}

.chart-legend { display: flex; gap: var(--space-4); font-size: 13px; font-weight: 400; }
.chart-legend__item { display: inline-flex; align-items: center; gap: var(--space-2); }

.chart-legend__swatch { width: 12px; height: 12px; border-radius: 2px; display: inline-block; }
.chart-legend__swatch--ok { background: var(--color-primary); }
.chart-legend__swatch--fail { background: var(--color-danger); }

/* ตัวเลือกจำนวนแถวต่อหน้า */
.per-page { display: inline-flex; align-items: center; gap: var(--space-1); }

.per-page__option {
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    color: var(--color-text-muted);
}

.per-page__option:hover { border-color: var(--color-border-strong); text-decoration: none; }

.per-page__option.is-active {
    background: var(--color-primary-light);
    border-color: var(--color-primary);
    color: var(--color-primary);
    font-weight: 600;
}

/* ความกว้างคอลัมน์ที่ใช้ซ้ำ — เดิมเขียนเป็น style ติดในหน้า */
.col-40 { width: 40%; }

/* เส้นทาง OU แบบอ่านง่าย — ตัวคั่นจางลงเพื่อให้ชื่อแต่ละชั้นเด่นกว่า */
.ou-path { word-break: break-word; }

/* ===================================================================== */
/*  หน้าเข้าสู่ระบบ                                                       */
/* ===================================================================== */
.text-center { text-align: center; }

/*
 * ทางเลือกรองในการเข้าสู่ระบบ
 * ใช้ <details> เพื่อให้พับได้เองโดยไม่ต้องใช้ JavaScript และยังกางได้ด้วยคีย์บอร์ด
 */
.login-alt { margin-top: var(--space-4); border-top: 1px solid var(--color-border); padding-top: var(--space-3); }

.login-alt__summary {
    cursor: pointer;
    color: var(--color-primary);
    font-weight: 600;
    list-style: none;
    text-align: center;
}

.login-alt__summary::-webkit-details-marker { display: none; }
.login-alt[open] .login-alt__summary { margin-bottom: var(--space-4); }

.password-field { position: relative; }
.password-field .form-control { padding-right: 68px; }

.password-field__toggle {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--color-primary);
    font: inherit;
    font-size: 13px;
    padding: 4px 6px;
}

/* โครงสร้างหน้ายืนยันตัวใช้ร่วมกันทั้ง login และเปลี่ยนรหัสผ่าน */
.auth-card__body { padding: var(--space-6); }
.auth-card__heading { margin-bottom: var(--space-5); text-align: center; }
.auth-card__title { margin-bottom: var(--space-1); font-size: 24px; }
.auth-card__description { margin: 0; color: var(--color-text-muted); font-size: 14px; }
.login-password { margin-top: var(--space-5); }
.break-all { overflow-wrap: anywhere; word-break: break-word; }
.text-uppercase { text-transform: uppercase; }

/* permission matrix ใช้ token แทนสีที่ฝังใน view เพื่อให้รองรับ dark mode */
.permission-matrix__role,
.permission-matrix__value { text-align: center !important; }
.permission-matrix__code { font-weight: 400; }
.permission-matrix__group { background: var(--color-neutral-light) !important; }
.permission-matrix__granted { color: var(--color-success); font-weight: 700; }

/* stepper เป็น ordered list ที่อ่านได้ทั้งด้วยตาและ screen reader */
.steps { list-style: none; margin-top: 0; padding-left: 0; }
.step-pill__number {
    display: inline-flex;
    width: 20px;
    height: 20px;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--color-overlay-soft);
    font-size: 11px;
    font-weight: 700;
}
.step-pill.is-current .step-pill__number { background: rgba(255, 255, 255, .2); }

/* ขนาดควบคุมขั้นต่ำที่กดได้แม่นยำขึ้น โดยไม่ทำให้ตารางแน่นเกินไป */
.btn { min-height: 40px; }
.form-control { min-height: 40px; }
.password-field__toggle { min-width: 44px; min-height: 36px; }

@media (max-width: 560px) {
    .centered-page { align-items: flex-start; padding: var(--space-4); }
    .centered-page__inner { margin: auto 0; }
    .auth-card__body { padding: var(--space-5); }
    .btn:not(.btn--sm), .form-control { min-height: 44px; }

    /* detail table เก่าบางจุดไม่มี wrapper: กัก scroll ไว้ใน card ไม่ให้ทั้งหน้าล้น */
    .card__body > .table { display: block; max-width: 100%; overflow-x: auto; }

    .steps { flex-wrap: nowrap; overflow-x: auto; padding-bottom: var(--space-2); }
    .step-pill { flex: 0 0 auto; }
    .permission-matrix__role { min-width: 132px; }
}

/*
 * ไม่ต้องมีบล็อกแก้สีเฉพาะโหมดมืดตรงนี้อีกแล้ว
 *
 * เดิมพื้นผิวอย่างหัวตาราง ท้ายการ์ด และช่องกรอกถูกเขียนสีสว่างติดไว้ในกฎหลัก
 * แล้วค่อยเขียนทับอีกทีในโหมดมืด ซึ่งพลาดไปหลายจุด (แถว hover ของตาราง ปุ่มรอง
 * แท็บ ชิปขั้นตอน กล่องแจ้งเตือน) ตอนนี้กฎหลักใช้ token ที่สลับค่าตามธีมเองแล้ว
 */

/* ===================================================================== */
/*  หน้าเอกสาร API                                                        */
/* ===================================================================== */
.docs { display: grid; grid-template-columns: 200px 1fr; gap: var(--space-5); align-items: start; }

.docs__toc {
    position: sticky;
    top: calc(var(--header-height) + var(--space-4));
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.docs__toc-title {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    margin-bottom: var(--space-1);
}

.docs__toc a { padding: 4px 6px; border-radius: var(--radius-sm); color: var(--color-text); }
.docs__toc a:hover { background: var(--color-neutral-light); text-decoration: none; }

.docs__body { min-width: 0; }
.docs__search { max-width: 280px; flex: 1 1 200px; }

/* หัวข้อของแต่ละส่วนต้องไม่ถูกแถบบนที่ตรึงอยู่บังเมื่อกดจากสารบัญ */
.docs__body section { scroll-margin-top: calc(var(--header-height) + var(--space-4)); }

/* บล็อกโค้ดพร้อมปุ่มคัดลอก */
.code-block { border: 1px solid var(--color-border); border-radius: var(--radius); overflow: hidden; margin-bottom: var(--space-3); }

.code-block__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-3);
    background: var(--color-neutral-light);
    border-bottom: 1px solid var(--color-border);
}

.code-block__code {
    margin: 0;
    padding: var(--space-3);
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.55;
}

/* แท็บของตัวอย่างโค้ด ใช้ปุ่มเพราะไม่ได้เปลี่ยนหน้า */
.code-tabs .tabs button {
    padding: var(--space-2) var(--space-4);
    border: 1px solid transparent;
    border-bottom: 0;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: none;
    color: var(--color-text-muted);
    font: inherit;
    cursor: pointer;
}

.code-tabs .tabs button:hover { background: var(--color-neutral-light); }

.code-tabs .tabs button.is-active {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text);
    font-weight: 600;
}

/*
 * ตัวเลือกภาษาของตัวอย่างทั้งหน้า
 *
 * อยู่ที่เดียวแทนที่จะให้กดเลือกในทุกกล่อง เพราะคนอ่านคนหนึ่งใช้ภาษาเดียวทั้งงาน
 * การให้เขากดซ้ำทุกกล่องคือความรำคาญที่ไม่มีเหตุผลรองรับ
 */
.docs__langs { display: flex; flex-wrap: wrap; gap: var(--space-1); }

.docs__lang {
    padding: 4px 10px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text-muted);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.docs__lang:hover { background: var(--color-surface-hover); }

.docs__lang.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
    font-weight: 600;
}

.docs__group {
    margin: var(--space-5) 0 var(--space-3);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
    font-size: 15px;
}

/*
 * endpoint แบบพับเก็บได้
 *
 * ใช้ <details> เพราะเปิดปิดได้เองโดยไม่ต้องใช้ JavaScript และรองรับคีย์บอร์ด
 * มาในตัว ที่สำคัญคือเบราว์เซอร์กาง <details> ให้เองเมื่อสั่งค้นหาในหน้า
 * ผู้อ่านจึงกด Ctrl+F หา endpoint ที่ยังพับอยู่เจอ
 */
.endpoint {
    margin-bottom: var(--space-3);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    scroll-margin-top: calc(var(--header-height) + var(--space-4));
}

.endpoint:target { border-color: var(--color-primary); }

.endpoint__summary {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3);
    cursor: pointer;
    list-style: none;
}

.endpoint__summary::-webkit-details-marker { display: none; }
.endpoint__summary:hover { background: var(--color-surface-hover); }
.endpoint__path { flex: 0 1 auto; }
.endpoint__title { flex: 1 1 200px; color: var(--color-text-muted); font-size: 14px; }
.endpoint__status { flex: 0 0 auto; }

.endpoint__body {
    padding: var(--space-3);
    border-top: 1px solid var(--color-border);
}

.endpoint__body h4 {
    margin: var(--space-4) 0 var(--space-2);
    font-size: 14px;
}

.endpoint__body h4:first-of-type { margin-top: var(--space-3); }

.endpoint__meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--space-1) var(--space-3);
    margin: 0 0 var(--space-3);
}

.endpoint__meta dt { color: var(--color-text-muted); font-size: 13px; }
.endpoint__meta dd { margin: 0; font-size: 13px; }

/* ตารางประกอบคำอธิบาย ต้องอ่านเร็วและกินที่น้อยกว่าตารางข้อมูลหลัก */
.table--compact th, .table--compact td { padding: 6px 10px; font-size: 13px; }

.nowrap { white-space: nowrap; }

@media (max-width: 900px) {
    .docs { grid-template-columns: 1fr; }
    .docs__toc { position: static; flex-direction: row; flex-wrap: wrap; }
    .docs__toc-title { width: 100%; }
}

/* สถานะความถูกต้องของ JSON ใน Playground */
.json-status { font-weight: 600; }
.json-status--ok { color: var(--color-success); }
.json-status--bad { color: var(--color-danger); font-weight: 400; }

/* ตัวกรองที่กำลังใช้อยู่ */
.filter-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.chip__key { color: var(--color-text-muted); font-weight: 400; }

/* ===================================================================== */
/*  ชุดปุ่มให้ครบระดับความสำคัญ                                            */
/* ===================================================================== */
/*
 * เดิมมีแค่ primary / secondary / danger ทำให้หน้าที่มีปุ่มหลายปุ่ม เช่นแถวในตาราง
 * ต้องใช้ secondary กับทุกอย่าง สายตาจึงแยกไม่ออกว่าอันไหนสำคัญกว่ากัน
 *
 * tertiary = ปุ่มขอบจาง ใช้กับการกระทำรองในแถว
 * ghost    = ไม่มีพื้นไม่มีขอบ ใช้กับการกระทำที่ทำบ่อยแต่ไม่สำคัญ เช่นคัดลอก
 * icon     = ปุ่มไอคอนล้วน ต้องมี aria-label เสมอ เพราะไม่มีข้อความให้เดา
 */
.btn--tertiary {
    border-color: var(--color-border);
    color: var(--color-text-muted);
}
.btn--tertiary:hover { background: var(--color-surface-hover); color: var(--color-text); }

.btn--ghost {
    background: none;
    border-color: transparent;
    color: var(--color-primary);
}
.btn--ghost:hover { background: var(--color-primary-light); }

.btn--icon {
    padding: 0;
    width: 40px;
    min-width: 40px;
    border-color: var(--color-border);
}
.btn--icon.btn--sm { width: 32px; min-width: 32px; min-height: 32px; }

/*
 * สถานะกำลังส่ง — ปุ่มบอกเองว่าระบบรับเรื่องไปแล้ว
 *
 * ถ้าไม่บอก ผู้ใช้ที่เจอเครือข่ายช้าจะกดซ้ำ เพราะหน้าจอไม่ได้ขยับอะไรเลย
 * ตัวกันการส่งซ้ำจริงอยู่ที่ disabled และ Idempotency-Key ฝั่งเซิร์ฟเวอร์
 */
.btn.is-loading { position: relative; }
.btn.is-loading::before {
    content: "";
    width: 14px;
    height: 14px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: btn-spin .6s linear infinite;
}

@keyframes btn-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .btn.is-loading::before { animation: none; opacity: .6; }
}

/* ===================================================================== */
/*  สรุปข้อผิดพลาดของฟอร์ม                                                */
/* ===================================================================== */
/*
 * ฟอร์มยาวที่บันทึกไม่ผ่านแล้วขึ้นข้อความไว้ใต้ช่องที่ผิดอย่างเดียว มีปัญหาสองข้อ
 * คือผู้ใช้ไม่รู้ว่ามีกี่จุดต้องแก้ และถ้าช่องนั้นอยู่นอกจอก็ไม่เห็นเลยว่าเกิดอะไรขึ้น
 *
 * กล่องนี้รวมทุกข้อไว้บนสุด แต่ละข้อเป็นลิงก์กระโดดไปที่ช่องนั้น
 * และถูกโฟกัสอัตโนมัติเมื่อหน้าโหลด เพื่อให้โปรแกรมอ่านหน้าจออ่านให้ฟังทันที
 */
.error-summary { border-width: 2px; }
.error-summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.error-summary ol { margin: var(--space-2) 0 0; padding-left: var(--space-5); }
.error-summary a { color: inherit; text-decoration: underline; }

/* ===================================================================== */
/*  ตาราง — ชื่อกำกับ และคอลัมน์ที่ต้องอ่านออกบนมือถือ                     */
/* ===================================================================== */
/*
 * ทุกตารางต้องมีชื่อกำกับ ผู้ใช้โปรแกรมอ่านหน้าจอมักสั่ง "ไล่ดูตารางทั้งหมดในหน้า"
 * ถ้าทุกตารางไม่มีชื่อก็ไม่รู้ว่าอันไหนคืออันไหน
 *
 * caption ถูกซ่อนจากสายตาเพราะหัวข้อของการ์ดบอกเรื่องเดียวกันอยู่แล้ว
 * แต่ไม่ซ่อนจากโปรแกรมอ่านหน้าจอ
 */
.table > caption {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
}

.table > caption.is-visible {
    position: static;
    width: auto;
    height: auto;
    margin: 0 0 var(--space-2);
    clip: auto;
    white-space: normal;
    text-align: left;
    font-size: var(--font-body-sm);
    color: var(--color-text-muted);
}

/*
 * ตารางรายละเอียด (ชื่อค่า / ค่า) ในหน้ารายละเอียด
 * บนมือถือคอลัมน์ซ้ายกินที่จนคอลัมน์ขวาเหลือไม่กี่ตัวอักษร จึงเรียงเป็นสองบรรทัดแทน
 */
.table--detail th { width: 38%; }

/*
 * หัวแถวเก็บ "เนื้อหา" ไม่ใช่ชื่อคอลัมน์สั้น ๆ จึงต้องตัดบรรทัดได้
 * ไม่งั้นคำอธิบายสิทธิ์ยาว ๆ จะดันตารางให้กว้างจนต้องเลื่อนทั้งที่ไม่จำเป็น
 */
.table th[scope="row"] { white-space: normal; font-weight: 600; }

@media (max-width: 560px) {
    .table--detail,
    .table--detail tbody,
    .table--detail tr,
    .table--detail th,
    .table--detail td { display: block; width: auto; }

    .table--detail th {
        border-bottom: 0;
        padding-bottom: 0;
        white-space: normal;
        background: none;
        color: var(--color-text-muted);
        font-size: var(--font-body-sm);
    }

    .table--detail td { padding-top: var(--space-1); }
}

/* ===================================================================== */
/*  โซนอันตราย                                                            */
/* ===================================================================== */
/*
 * การกระทำที่ย้อนกลับไม่ได้ต้องอยู่แยกจากปุ่มที่ใช้ทุกวัน ไม่ใช่เรียงต่อกันในแถวเดียว
 * ระยะห่างกับกรอบสีคือสิ่งที่กันการกดพลาดได้จริง คำเตือนตัวเล็กไม่ช่วย
 */
.danger-zone {
    border: 1px solid var(--color-danger-border);
    border-radius: var(--radius-lg);
    background: var(--color-danger-light);
    padding: var(--space-4);
    margin-top: var(--space-6);
}

.danger-zone__title {
    margin: 0 0 var(--space-1);
    color: var(--color-danger-text);
    font-size: var(--font-h3);
}

.danger-zone__description { color: var(--color-danger-text); margin: 0 0 var(--space-3); }
.danger-zone__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* ===================================================================== */
/*  สารบัญภายในหน้ารายละเอียดที่ยาวมาก                                     */
/* ===================================================================== */
.page-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
}

.page-nav a {
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius);
    color: var(--color-text-muted);
    font-size: var(--font-body-sm);
    font-weight: 600;
}

.page-nav a:hover { background: var(--color-surface-hover); color: var(--color-text); text-decoration: none; }

/* หัวข้อที่ถูกกระโดดไปหาต้องไม่โดนแถบบนที่ตรึงอยู่บัง */
[data-section] { scroll-margin-top: calc(var(--header-height) + var(--space-5)); }

@media (max-width: 900px) {
    .page-nav { flex-wrap: nowrap; overflow-x: auto; }
    .page-nav a { flex: 0 0 auto; }
}

/* ===================================================================== */
/*  ตารางสิทธิ์ในแบบรายการ สำหรับจอแคบ                                     */
/* ===================================================================== */
/*
 * บนมือถือ ตารางสิทธิ์หลายคอลัมน์เลื่อนไปมาแล้วจำไม่ได้ว่าคอลัมน์ไหนคือบทบาทอะไร
 * จึงสลับเป็น "เลือกบทบาท แล้วดูสิทธิ์เป็นรายการ" ซึ่งอ่านทีละเรื่องได้จบ
 */
.matrix-list { display: none; }

.matrix-list__role {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    margin-bottom: var(--space-3);
}
.matrix-list__role > summary {
    padding: var(--space-3) var(--space-4);
    font-weight: 600;
    cursor: pointer;
    border-radius: var(--radius-lg);
}
.matrix-list__role[open] > summary {
    border-bottom: 1px solid var(--color-border);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.matrix-list__group {
    padding: var(--space-3) var(--space-4) 0;
    font-size: var(--font-body-sm);
    color: var(--color-text-muted);
}
.matrix-list__items { list-style: none; margin: 0; padding: 0 var(--space-4) var(--space-3); }
.matrix-list__items li { display: flex; gap: var(--space-2); padding: var(--space-1) 0; }

@media (max-width: 900px) {
    .matrix-list { display: block; }
    .matrix-table { display: none; }
}

/* ===================================================================== */
/*  ช่องกรอกที่มีข้อผิดพลาด                                                */
/* ===================================================================== */
/*
 * สีขอบแดงอย่างเดียวไม่พอ คนตาบอดสีแดง-เขียวราว 8% ของผู้ชายจะไม่เห็นความต่าง
 * จึงเพิ่มความหนาของขอบเข้าไปด้วย เพื่อให้แยกออกโดยไม่ต้องพึ่งสี
 */
.form-control.is-invalid,
.form-control[aria-invalid="true"] { border-color: var(--color-danger); border-width: 2px; }

/* ป้ายบอกว่าช่องไหนไม่บังคับ ชัดกว่าการปล่อยให้เดาจากการไม่มีดอกจัน */
.form-label__optional { font-weight: 400; color: var(--color-text-muted); font-size: var(--font-body-sm); }

/* ===================================================================== */
.actions-split { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.actions-split__danger { margin-left: var(--space-5); }

@media (max-width: 560px) {
    /* บนมือถือใช้เส้นคั่นแทนระยะห่าง เพราะพื้นที่แนวนอนไม่พอจะเว้น */
    .actions-split { flex-direction: column; align-items: stretch; }
    .actions-split__danger {
        margin-left: 0;
        margin-top: var(--space-3);
        padding-top: var(--space-3);
        border-top: 1px solid var(--color-border);
    }
}

/* ===================================================================== */
/*  กลุ่มสิทธิ์ของ API client                                             */
/* ===================================================================== */
/*
 * fieldset ของเบราว์เซอร์มาพร้อมขอบและระยะที่ไม่เข้ากับฟอร์มอื่นในระบบ
 * จึงล้างออกแล้วคุมระยะเอง ส่วน legend ยังทำหน้าที่เป็นหัวข้อของกลุ่มตามเดิม
 */
.scope-group {
    border: 0;
    margin: 0 0 var(--space-4);
    padding: 0;
    min-width: 0;
}

.scope-group > legend { padding: 0; }

.scope-group__title {
    margin: var(--space-3) 0 var(--space-1);
    font-size: var(--font-body-sm);
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.scope-group__options {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--space-2);
}

.scope-group__code {
    display: block;
    font-size: var(--font-caption);
    color: var(--color-text-muted);
}

/* ช่องค้นหาในแถบตัวกรอง — เดิมกำหนดความกว้างด้วย style ติดในหน้า */
.form-group--grow { min-width: 260px; flex: 1 1 240px; }

/* ===================================================================== */
/*  ระยะกดบนมือถือ                                                        */
/* ===================================================================== */
/*
 * WCAG 2.2 §2.5.8 กำหนดขั้นต่ำ 24×24 CSS px ส่วน 44×44 เป็นเกณฑ์ระดับ AAA
 * ที่นิ้วโป้งใช้งานได้จริงโดยไม่ต้องเล็ง ระบบนี้ใช้เกณฑ์หลังบนจอสัมผัส
 * เพราะปุ่มส่วนใหญ่ทำสิ่งที่ย้อนกลับไม่ได้ การกดพลาดจึงแพงกว่าพื้นที่ที่เสียไป
 */
@media (max-width: 560px) {
    .btn--sm { min-height: 44px; }

    .copyable__btn,
    .chip__remove,
    .password-field__toggle {
        min-width: 44px;
        min-height: 44px;
    }

    /* ปุ่มหลักของหน้าเต็มความกว้าง กดได้โดยไม่ต้องเล็ง */
    .page-header__actions { width: 100%; }
    .page-header__actions > .btn:not(.btn--sm) { flex: 1 1 100%; }
}

/* ===================================================================== */
/*  โลโก้ของระบบ                                                          */
/* ===================================================================== */
/*
 * ทุก <img> ของโลโก้ประกาศ width/height ไว้ใน HTML แล้ว (components/brand-logo.php)
 * กฎในส่วนนี้จึงมีหน้าที่แค่จัดวางและกันภาพล้นกรอบบนจอแคบ ไม่ใช่กำหนดขนาดซ้ำ
 * ถ้าเขียนขนาดไว้ทั้งสองที่ วันที่เปลี่ยนขนาดจะต้องไปแก้สองแห่งและมักลืมแห่งหนึ่ง
 */
.brand-logo { max-width: 100%; height: auto; }
.brand-logo--mark { flex: 0 0 auto; }

/*
 * มุมซ้ายบนเป็นลิงก์กลับหน้าแรก ซึ่งเป็นสิ่งที่ผู้ใช้เว็บคาดหวังอยู่แล้ว
 * ชื่อระบบยาวกว่าความกว้างของแถบเมนูได้ จึงต้องตัดด้วยจุดสามจุดไม่ให้ดันปุ่มย่อตกบรรทัด
 */
.sidebar__home {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    flex: 1 1 auto;
    color: #fff;
}
.sidebar__home:hover,
.sidebar__home:focus { color: #fff; text-decoration: none; }

body.nav-collapsed .sidebar__home { justify-content: center; }

/* หน้าที่ยังไม่เข้าสู่ระบบและตัวติดตั้ง — โลโก้อยู่กลางคอลัมน์เดียวกับการ์ด */
.brand-mark__logo { margin: 0 auto var(--space-3); }
.brand-mark__icon { margin: 0 auto var(--space-2); }

/*
 * ภาพรวมในเอกสาร API — ข้อความกับภาพเรียงข้างกันบนจอกว้าง
 * บนจอแคบภาพลงไปอยู่ใต้ข้อความ ไม่ซ่อนทิ้ง เพราะเป็นภาพที่อธิบายเส้นทางของคำขอ
 */
.docs__overview {
    display: flex;
    align-items: center;
    gap: var(--space-5);
}
.docs__overview-text { flex: 1 1 340px; min-width: 0; }
.docs__overview-art { flex: 0 0 auto; }

@media (max-width: 900px) {
    .docs__overview { flex-direction: column-reverse; align-items: flex-start; }
    .docs__overview-art { align-self: center; }
}

/* ===================================================================== */
/*  ปุ่มสลับธีม                                                           */
/* ===================================================================== */
/*
 * สามสถานะ: ตามระบบ · สว่าง · มืด
 *
 * ที่ต้องมี "ตามระบบ" ด้วยไม่ใช่แค่สองสถานะ เพราะคนที่ตั้ง Windows ให้สลับ
 * สว่าง-มืดตามเวลาของวัน จะเสียพฤติกรรมนั้นไปทันทีถ้าระบบบังคับค่าตายตัวให้
 *
 * ปุ่มเดียวกดวนสามสถานะ เพื่อไม่ให้แถบบนแน่นไปกว่านี้ ไอคอนบอกสถานะปัจจุบัน
 * ส่วนชื่อที่โปรแกรมอ่านหน้าจออ่านบอกทั้งสถานะปัจจุบันและสิ่งที่จะเกิดเมื่อกด
 */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    min-height: 36px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
}

.theme-toggle:hover {
    background: var(--color-surface-hover);
    color: var(--color-text);
    border-color: var(--color-border);
}

.theme-toggle:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/*
 * ไอคอนทั้งสามซ้อนอยู่ในปุ่มเดียว แล้วโชว์ทีละตัวตามสถานะ
 * วิธีนี้ทำให้ไม่ต้องสร้าง SVG ใหม่ด้วย JavaScript ตอนกด
 * และถ้าสคริปต์ไม่ทำงาน ปุ่มก็ยังแสดงไอคอน "ตามระบบ" อย่างถูกต้อง
 */
.theme-toggle__icon { display: none; }
.theme-toggle[data-theme-state="auto"]  .theme-toggle__icon--auto,
.theme-toggle[data-theme-state="light"] .theme-toggle__icon--light,
.theme-toggle[data-theme-state="dark"]  .theme-toggle__icon--dark { display: inline-flex; }

@media (max-width: 560px) {
    .theme-toggle { width: 44px; height: 44px; min-height: 44px; }
}

/* ===================================================================== */
/*  ตัวระบุยาว ๆ ต้องตัดบรรทัดได้                                          */
/* ===================================================================== */
/*
 * ค่าอย่าง Distinguished Name หรือพาธไฟล์เป็นสตริงยาวที่ไม่มีช่องว่างให้ตัด
 * เบราว์เซอร์จึงถือเป็นคำเดียวและไม่ยอมหักบรรทัด ผลคือมันดันความกว้างของหน้า
 * ทั้งหน้าให้เลื่อนซ้ายขวาบนมือถือ ซึ่งพาแถบเมนูกับหัวข้อหลุดออกนอกจอไปด้วย
 *
 * วัดจากเบราว์เซอร์จริงที่จอ 390px: หน้า /ad/scripts ล้น 71px และ
 * /transactions/2 ล้น 8px ทั้งสองมาจาก <span class="mono"> ตัวเดียว
 *
 * ใช้ anywhere ไม่ใช่ break-word เพราะ anywhere มีผลกับการคำนวณความกว้าง
 * ขั้นต่ำของ flex/grid ด้วย ซึ่งเป็นจุดที่ทำให้หน้าล้นจริง
 *
 * ไม่กระทบบล็อกโค้ด เพราะ pre ในบล็อกเหล่านั้นมี overflow-x ของตัวเองอยู่แล้ว
 *
 * ใช้ break-word ไม่ใช่ anywhere
 *
 * anywhere มีผลกับการคำนวณความกว้างขั้นต่ำด้วย ซึ่งทำให้คอลัมน์รหัสคำขอในตาราง
 * หดลงเหลือความกว้างหนึ่งตัวอักษร แล้วหักบรรทัดทีละตัว (วัดได้ 18px กว้าง 208px สูง)
 * break-word หักคำยาวเมื่อจำเป็นเท่านั้น โดยไม่ไปแตะความกว้างขั้นต่ำของตาราง
 */
.mono { overflow-wrap: break-word; }

/*
 * ตัวระบุที่เป็นลูกของ flex ต้องยอมหดต่ำกว่าความกว้างของคำตัวเอง
 *
 * flex item มี min-width: auto มาโดยปริยาย จึงไม่ยอมแคบกว่าคำที่ยาวที่สุดข้างใน
 * ค่า DN อย่าง OU=EMPLOYEE,OU=Information & Technology,... จึงดันแถบสรุปให้ล้นจอ
 * ตรงนี้ต้องสั่ง min-width: 0 เป็นรายจุด ไม่ใช่เหวี่ยง anywhere ทั้งระบบ
 */
.summary-bar__item > .mono,
.tech-details__list dd .mono { min-width: 0; }

/* ===================================================================== */
/*  ขนาดเป้าที่กดได้ขั้นต่ำ                                                */
/* ===================================================================== */
/*
 * WCAG 2.2 §2.5.8 (AA) กำหนดขั้นต่ำ 24x24 CSS px
 *
 * วัดจากเบราว์เซอร์จริงแล้วพบว่าลิงก์ "สถานะระบบ" บนแถบบนสูงเพียง 21px
 * และปุ่มพับข้อมูล (<summary>) ทุกตัวสูง 21px เช่นกัน ทั้งสองต่ำกว่าเกณฑ์
 *
 * บนจอสัมผัสขยายเป็น 44px ซึ่งเป็นขนาดที่นิ้วโป้งกดได้โดยไม่ต้องเล็ง
 * ใช้ pointer: coarse ไม่ใช่ความกว้างจอ เพราะเกณฑ์นี้ขึ้นกับอุปกรณ์ที่ใช้ชี้
 * ไม่ใช่ขนาดหน้าต่าง (จอสัมผัสขนาดใหญ่ก็ต้องการเป้าใหญ่เหมือนกัน)
 */
summary { min-height: 24px; }

.topbar__health {
    min-height: 24px;
    padding: var(--space-1) 0;
}

.tech-details > summary,
.login-alt__summary {
    display: flex;
    align-items: center;
    min-height: 24px;
}

@media (pointer: coarse) {
    summary { min-height: 44px; }

    /* ปุ่มเปิดเมนูวัดได้ 38px กว้าง ซึ่งแคบกว่าเกณฑ์นิ้วโป้ง */
    .nav-toggle { min-width: 44px; }

    .topbar__health,
    .tech-details > summary,
    .login-alt__summary { min-height: 44px; }

    /*
     * checkbox กับ radio ของระบบปฏิบัติการวาดมาที่ 13x13 ขยายไม่ได้ตรง ๆ
     * แต่ label ที่ครอบอยู่เป็นเป้าที่กดได้จริง จึงขยาย label ให้ถึงเกณฑ์แทน
     */
    .form-check {
        min-height: 44px;
        align-items: center;
    }

    .form-check input { width: 20px; height: 20px; }
}

/* ===================================================================== */
/*  หน้าควบคุมเครื่อง worker                                              */
/* ===================================================================== */
/*
 * การ์ดของแต่ละเครื่องต้องบอกสถานะได้ตั้งแต่ก่อนอ่านตัวหนังสือ เพราะหน้านี้ถูกเปิด
 * ค้างไว้บนจอที่คนมองผ่าน ๆ ไม่ได้นั่งอ่านทีละบรรทัด
 *
 * ใช้ "แถบสีด้านซ้าย" ไม่ใช่การย้อมพื้นทั้งใบ — พื้นสีทำให้ตัวหนังสือในการ์ด
 * อ่านยากลงทันทีในโหมดมืด และทำให้การ์ดสามใบที่สถานะต่างกันดูเหมือนคนละระบบ
 * สีไม่ใช่ตัวบอกความหมายตัวเดียวอยู่แล้ว เพราะมีทั้งจุดสถานะและป้ายข้อความกำกับ
 */
.worker-card { border-left: 3px solid var(--color-border-strong); }
.worker-card--ok { border-left-color: var(--color-success); }
.worker-card--warn { border-left-color: var(--color-warning); }
.worker-card--down { border-left-color: var(--color-danger); }

.worker-card__summary {
    margin: 0 0 var(--space-3);
    color: var(--color-text-muted);
}

/*
 * ตัวเลขห้าค่าที่ตอบว่า "ตอนนี้เครื่องนี้เป็นยังไง"
 *
 * auto-fit ทำให้การ์ดที่ถูกวางคู่กันบนจอกว้างกับที่วางเดี่ยวบนมือถือ ใช้ markup
 * ชุดเดียวกันได้โดยไม่ต้องมี breakpoint ของตัวเอง
 */
.worker-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: var(--space-3);
    margin: 0;
}

.worker-metrics__item { min-width: 0; }

.worker-metrics dt {
    color: var(--color-text-muted);
    font-size: var(--font-caption);
    margin-bottom: 2px;
}

.worker-metrics__value {
    margin: 0;
    font-weight: 600;
    word-break: break-word;
}

/* สีของค่าที่ผิดปกติ ใช้ token ชุดเดียวกับป้ายสถานะทั้งระบบ */
.worker-metrics__value--warn { color: var(--color-warning); }
.worker-metrics__value--down { color: var(--color-danger); }

/*
 * แถวปุ่มคำสั่ง — แต่ละปุ่มถูกห่อด้วย <form> ของตัวเองเพราะต้องมี CSRF token
 * แยกใบ การจัดแนวจึงต้องเกิดที่ตัวห่อ ไม่ใช่ที่ตัวปุ่ม
 */
.worker-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-4);
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-border);
}

.worker-actions form { margin: 0; }
.worker-actions .btn { gap: var(--space-1); }

/* กลุ่มที่สองเป็นต่อจากกลุ่มแรก ไม่ต้องมีเส้นคั่นซ้อนอีกชั้น */
.worker-actions + .worker-actions {
    border-top: 0;
    margin-top: var(--space-2);
    padding-top: 0;
}

/*
 * หัวข้อกำกับกลุ่มปุ่ม — กินความกว้างเต็มแถวเพื่อดันปุ่มลงบรรทัดใหม่เสมอ
 * ถ้าปล่อยให้อยู่บรรทัดเดียวกับปุ่ม มันจะดูเหมือนปุ่มที่กดไม่ได้อีกอัน
 */
.worker-actions__label {
    flex: 0 0 100%;
    color: var(--color-text-muted);
    font-size: var(--font-caption);
    margin-bottom: calc(var(--space-1) * -1);
}

/* ===================================================================== */
/*  เอกสาร API ฉบับพิมพ์ / บันทึกเป็น PDF                                 */
/* ===================================================================== */

/*
 * หน้านี้ถูกออกแบบให้ผลลัพธ์ที่ออกมาเป็นกระดาษ ไม่ใช่หน้าจอที่บังเอิญพิมพ์ได้
 *
 * เลือกใช้เครื่องพิมพ์ของเบราว์เซอร์แทนการสร้าง PDF ฝั่งเซิร์ฟเวอร์ เพราะการ
 * ฝังฟอนต์ไทยลงไลบรารีสร้าง PDF ของ PHP ให้ผลตัดคำผิดและเพิ่มของที่ต้องดูแล
 * อีกชุด ทั้งที่เบราว์เซอร์จัดบรรทัดภาษาไทยได้ถูกต้องอยู่แล้ว
 */
.print-doc {
    max-width: 920px;
    margin: 0 auto;
    padding: var(--space-5);
    background: var(--color-surface);
    color: var(--color-text);
}

.print-toolbar {
    position: sticky;
    top: 0;
    z-index: 5;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3);
    margin-bottom: var(--space-5);
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.print-cover {
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-5);
    border-bottom: 3px solid var(--color-primary);
}

.print-cover h1 { margin: 0 0 var(--space-2); }
.print-cover__subtitle { margin: 0 0 var(--space-4); color: var(--color-text-muted); }

.print-facts { width: 100%; max-width: 560px; border-collapse: collapse; }

.print-facts th,
.print-facts td { padding: 4px 0; font-size: 13px; text-align: left; vertical-align: top; }

.print-facts th { width: 150px; color: var(--color-text-muted); font-weight: 600; }

.print-note { margin-top: var(--space-4); font-size: 12px; color: var(--color-text-muted); }

.print-section { margin-bottom: var(--space-6); }

.print-section h2 {
    margin-top: 0;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--color-border);
}

.print-group { margin: var(--space-5) 0 var(--space-3); color: var(--color-primary); }

.print-endpoint {
    padding: var(--space-4);
    margin-bottom: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.print-endpoint h4 {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
}

.print-endpoint h5 {
    margin: var(--space-4) 0 var(--space-2);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
}

.print-endpoint__summary { margin: 0 0 var(--space-3); }

.print-method {
    display: inline-block;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    background: var(--color-primary);
    color: var(--color-on-primary);
    font-size: 12px;
    font-weight: 700;
}

.print-notes { margin: 0 0 var(--space-3); padding-left: 1.2em; font-size: 13px; }

.print-table {
    width: 100%;
    margin-bottom: var(--space-3);
    border-collapse: collapse;
    font-size: 12.5px;
}

.print-table th,
.print-table td {
    padding: 5px 8px;
    border: 1px solid var(--color-border);
    text-align: left;
    vertical-align: top;
}

.print-table thead th { background: var(--color-surface-alt); }
.print-table__group td { background: var(--color-surface-sunken); font-weight: 700; }
.print-table--facts th { width: 150px; background: var(--color-surface-alt); }

/*
 * โค้ดในไฟล์ PDF ต้องตัดบรรทัดเอง ไม่ใช่เลื่อนแนวนอน
 * เพราะสิ่งที่ล้นออกนอกขอบกระดาษจะหายไปจากไฟล์โดยไม่มีอะไรบอก
 */
.print-code {
    margin: 0 0 var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface-alt);
    font-family: var(--font-mono);
    font-size: 11.5px;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
}

.print-code__label { margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--color-text-muted); }

@page { margin: 14mm 12mm; }

@media print {
    .print-toolbar { display: none !important; }
    .print-doc { max-width: none; padding: 0; }

    /*
     * กรอบเลื่อนแนวนอนมีไว้สำหรับจอแคบ บนกระดาษมันกลายเป็นตัวตัดคอลัมน์ขวาสุดทิ้ง
     * โดยไม่มีอะไรบอก คนที่ได้ไฟล์ไปจึงไม่มีทางรู้ว่ามีคอลัมน์ที่เขาไม่ได้เห็น
     */
    .print-doc .table-wrapper { overflow: visible; }

    /* ใบปะหน้าอยู่หน้าเดียว ส่วนที่เหลือเริ่มหน้าใหม่ */
    .print-cover { page-break-after: always; }

    /* หัวข้อที่ตกอยู่ท้ายหน้าโดยไม่มีเนื้อหาตามมาคือหน้าที่เสียไปเปล่า ๆ */
    .print-section h2,
    .print-endpoint h4,
    .print-endpoint h5 { page-break-after: avoid; }

    .print-endpoint,
    .print-code { page-break-inside: avoid; }

    .print-table { page-break-inside: auto; }
    .print-table tr { page-break-inside: avoid; }
    .print-table thead { display: table-header-group; }

    /* ขีดเส้นใต้ลิงก์ในกระดาษไม่ได้ช่วยอะไร เพราะกดไม่ได้ */
    a { color: inherit; text-decoration: none; }
}

/*
 * เอกสารเริ่มงานของ client — ใช้โครงเดียวกับเอกสาร API ฉบับพิมพ์
 * เพิ่มเฉพาะสามอย่างที่หน้านั้นไม่มี
 */
.print-callout {
    padding: 8pt 10pt;
    margin: 0 0 var(--space-3);
    background: var(--color-info-light);
    border-left: 3px solid var(--color-info);
    border-radius: var(--radius-sm);
}

/* แถวรหัสที่ให้คัดลอกไปวาง ต้องอ่านทีละตัวได้ไม่ติดกันเป็นพรืด */
.print-codes {
    padding: 8pt 10pt;
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    line-height: 2;
    word-spacing: 4px;
}

.print-checklist { list-style: none; padding-left: 0; }
.print-checklist li { margin-bottom: 4pt; }

/* ------------------------------------------------- ผู้ใช้ใน AD (/directory) */

/*
 * สองช่อง: ต้นไม้ OU ทางซ้ายติดจอไว้ระหว่างเลื่อนรายการ · รายการกินที่ที่เหลือทั้งหมด
 * รายละเอียดเปิดในแผงด้านขวามาตรฐาน (.drawer) จึงไม่ต้องแบ่งที่ให้คอลัมน์ที่สาม
 * ซึ่งเคยบีบทั้งต้นไม้และตารางจนชื่อ OU กับชื่อคนตกบรรทัด
 */
.directory {
    display: grid;
    grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
    gap: var(--space-4);
    align-items: start;
}

.directory__tree {
    position: sticky;
    top: var(--space-4);
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 2 * var(--space-4));
}

.directory__tree-header {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.directory__tree-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
}

.directory__tree-legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--space-1) var(--space-3);
}

.directory__tree-legend > span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* ---------------------------------------------------------- ต้นไม้ */

.ou-tree,
.ou-tree__children {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ou-tree__children {
    margin-left: 9px;
    padding-left: 10px;
    border-left: 1px solid var(--color-border);
}

/*
 * ลูกศรพับ/กางเป็น summary แยกจากปุ่มชื่อ OU โดยตั้งใจ
 * คลิกชื่อ = ดูรายการใน OU นั้น · คลิกลูกศร = กาง OU ย่อย สองอย่างนี้ต้องไม่ปนกัน
 * summary ถูกวางทับขอบซ้ายของแถวชื่อ OU จึงไม่กินความสูงเพิ่มตอนพับอยู่
 */
.ou-tree__item {
    position: relative;
}

.ou-tree__item > details > summary {
    list-style: none;
}

.ou-tree__item > details > summary::-webkit-details-marker {
    display: none;
}

.ou-tree__toggle {
    position: absolute;
    left: 0;
    top: 4px;
    width: 20px;
    height: 24px;
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.ou-tree__toggle::before {
    content: "";
    position: absolute;
    left: 7px;
    top: 7px;
    border-left: 6px solid var(--color-text-muted);
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    transition: transform 0.15s ease;
}

.ou-tree__item > details[open] > .ou-tree__toggle::before {
    transform: rotate(90deg);
}

.ou-tree__toggle:hover {
    background: var(--color-surface-hover);
}

.ou-tree__toggle:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

.ou-tree__node {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    min-height: 32px;
    padding: 4px var(--space-2) 4px 24px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text);
    font: inherit;
    font-size: var(--font-body-sm);
    text-align: left;
    cursor: pointer;
}

.ou-tree__node:hover {
    background: var(--color-surface-hover);
}

.ou-tree__node:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

/* info-light/info-text แทน primary-light/primary-dark เพราะคู่หลังแทบมองไม่เห็นในโหมดมืด */
.ou-tree__node.is-selected {
    background: var(--color-info-light);
    color: var(--color-info-text);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--color-primary);
}

.ou-tree__name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ou-tree__count {
    flex: 0 0 auto;
    min-width: 28px;
    padding: 0 6px;
    border-radius: var(--radius-pill);
    background: var(--color-surface-alt);
    color: var(--color-text-muted);
    font-size: var(--font-caption);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.ou-tree__node.is-selected .ou-tree__count {
    background: var(--color-surface);
    color: var(--color-info-text);
}

.ou-tree__flag {
    flex: 0 0 auto;
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-warning);
}

/* ---------------------------------------------------------- ค้นหา */

.directory__search {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 160px auto;
    gap: var(--space-2) var(--space-3);
    align-items: end;
    margin-bottom: var(--space-3);
}

.directory__search .form-group {
    margin-bottom: 0;
}

.directory__search-options {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
}

.directory__search-options .form-check {
    margin: 0;
}

.directory__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-2) 0 var(--space-3);
    border-top: 1px solid var(--color-border);
}

.directory__scope {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    font-size: var(--font-body-sm);
}

.directory__scope-chip {
    max-width: 100%;
    min-width: 0;
    background: var(--color-info-light);
    color: var(--color-info-text);
    border: 1px solid var(--color-info-border);
}

.directory__scope-chip [data-scope-name] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-right: 6px;
}

.directory__status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--color-text-muted);
}

/* จุดกะพริบบอกว่ายังรอเครื่อง worker อยู่ — ไม่ใช่หน้าค้าง */
.directory__status.is-pending::before {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-primary);
    animation: directory-pulse 1s ease-in-out infinite;
}

@keyframes directory-pulse {
    0%, 100% { opacity: .25; }
    50%      { opacity: 1; }
}

/* ---------------------------------------------------------- ตาราง */

.directory__table td,
.directory__table th {
    vertical-align: middle;
}

.directory__table tbody tr:hover {
    background: var(--color-surface-hover);
}

.directory__sub {
    display: block;
    color: var(--color-text-muted);
    font-size: var(--font-caption);
}

/* ชื่อในตารางและในแผงรายละเอียดเป็นปุ่ม (เปิดรายละเอียดในหน้าเดิม) แต่ดูเหมือนลิงก์ */
.directory__open {
    padding: 0;
    border: 0;
    background: none;
    color: var(--color-primary);
    font: inherit;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    overflow-wrap: anywhere;
}

.directory__open:hover {
    color: var(--color-primary-dark);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.directory__open:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* ------------------------------------------------- แผงรายละเอียด */

.directory-detail__hero {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-2);
    border-bottom: 1px solid var(--color-border);
}

.directory-detail__avatar {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--color-info-light);
    color: var(--color-info-text);
    font-size: 18px;
    font-weight: 700;
}

.directory-detail__who {
    min-width: 0;
}

.directory-detail__name {
    margin: 0;
    font-size: 17px;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.directory-detail__meta {
    margin: 2px 0 6px;
    color: var(--color-text-muted);
    font-size: var(--font-body-sm);
    overflow-wrap: anywhere;
}

.directory-detail__badges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
}

.directory-detail__section {
    margin: var(--space-4) 0 var(--space-2);
    font-size: var(--font-caption);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.directory-detail__fields {
    display: grid;
    grid-template-columns: minmax(110px, 36%) minmax(0, 1fr);
    gap: 0;
    margin: 0;
}

.directory-detail__fields dt,
.directory-detail__fields dd {
    margin: 0;
    padding: 7px 0;
    border-bottom: 1px solid var(--color-border);
    font-size: var(--font-body-sm);
}

.directory-detail__fields dt {
    padding-right: var(--space-3);
    color: var(--color-text-muted);
}

.directory-detail__fields dd {
    overflow-wrap: anywhere;
}

.directory-detail__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.directory-detail__list li {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    padding: 6px 0;
    border-bottom: 1px solid var(--color-border);
    font-size: var(--font-body-sm);
}

.directory-detail__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

/* ---------------------------------------------------------- จอแคบ */

@media (max-width: 1100px) {
    .directory {
        grid-template-columns: minmax(240px, 280px) minmax(0, 1fr);
    }

    .directory__search {
        grid-template-columns: minmax(0, 1fr) 140px;
    }

    .directory__search-submit {
        grid-column: 1 / -1;
    }
}

@media (max-width: 900px) {
    .directory {
        grid-template-columns: minmax(0, 1fr);
    }

    .directory__tree {
        position: static;
        max-height: 50vh;
    }
}

@media (max-width: 560px) {
    .directory__search {
        grid-template-columns: minmax(0, 1fr);
    }

    .directory-detail__fields {
        grid-template-columns: minmax(0, 1fr);
    }

    .directory-detail__fields dt {
        padding-bottom: 0;
        border-bottom: 0;
    }
}

/* ระหว่างค้นรอบใหม่ ผลเดิมยังอยู่ให้อ่านต่อได้ แต่จางลงเพื่อบอกว่ากำลังจะถูกแทนที่ */
.directory [data-directory-results].is-loading {
    opacity: .5;
    transition: opacity .15s ease;
}
