@font-face {
  font-family: 'Google Sans Flex'; font-style: normal; font-weight: 300 800; font-display: swap;
  src: url('/assets/fonts/qq-skkn-gsf-vn.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Google Sans Flex'; font-style: normal; font-weight: 300 800; font-display: swap;
  src: url('/assets/fonts/qq-skkn-gsf-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}
@font-face {
  font-family: 'Newsreader'; font-style: italic; font-weight: 400 600; font-display: swap;
  src: url('/assets/fonts/qq-skkn-newsreader-it-vn.woff2') format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: 'Newsreader'; font-style: italic; font-weight: 400 600; font-display: swap;
  src: url('/assets/fonts/qq-skkn-newsreader-it-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122;
}
@font-face {
  font-family: 'Geist Mono'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('/assets/fonts/qq-skkn-geistmono-regular.woff2') format('woff2');
}

:root {
  /* A1 — Bảng màu (Tone Xanh Học Thuật), nguồn 05.3.qq-design-system.md */
  --color-primary: #004780;
  --color-primary-rgb: 0, 71, 128;
  --color-primary-hover: #003660;
  --color-primary-fg: var(--color-primary); /* text/icon/viền — đổi riêng ở dark, --color-primary GIỮ NGUYÊN mọi theme (chỉ dùng làm nền nút, luôn kèm chữ trắng) */
  --color-heading: var(--color-primary); /* h1-h4 dùng biến này, KHÔNG dùng --color-text-primary trực tiếp */
  --color-secondary: #f0f4f8;
  --color-bg-primary: #ffffff;
  --color-bg-secondary: #f5f8fa;
  --color-bg-tertiary: #e6eff7;
  --color-sidebar-bg: #eef3f8;
  --color-bg-dark: #0a131c;
  --color-text-primary: #0a131c;
  --color-text-secondary: #202f3e;
  --color-text-muted: #506275;
  --color-text-min: #a0b2c6; /* CHỈ divider, KHÔNG BAO GIỜ dùng cho chữ ở bất kỳ theme nào — 2.17:1, fail AA */
  --color-border: #d0dfed;
  --color-accent-academic: #1d70b8;
  --color-accent-sky: #7eb1d6;
  --color-focus-ring: var(--color-accent-academic);
  --color-glass-bg: rgba(255, 255, 255, 0.82);
  --color-glass-border: rgba(var(--color-primary-rgb), 0.08);

  --color-warning-bg: #fbf3e2; --color-warning-text: #8a5a12;
  --color-info-bg: #e3ecf5;    --color-info-text: #155a94;
  --color-danger-bg: #fdf2f2;  --color-danger-text: #c53030;
  --color-success-bg: #e5f3ec; --color-success-text: #1a7a4c;

  --shadow-card: 0 40px 90px -40px rgba(var(--color-primary-rgb), 0.03);
  --shadow-btn-primary: 0 10px 24px -10px rgba(var(--color-primary-rgb), 0.5);
  --radius-pill: 9999px;
  --radius-card-outer: 22px;
  --radius-card-inner: 14px;
  --radius-data: 10px; /* input/select/table — KHÔNG dùng pill */
}

@media (prefers-color-scheme: dark) {
  /* :not([data-theme="light"]) — nếu người dùng đã CHỦ ĐỘNG chọn theme sáng (nút toggle ghi
     data-theme="light" vào <html>), phải thắng tuyệt đối sở thích Dark Mode của hệ điều hành.
     Thiếu điều kiện loại trừ này, khối dưới vẫn khớp (chỉ dựa vào prefers-color-scheme, không
     biết gì về lựa chọn thủ công) và ĐÈ các biến chỉ được định nghĩa lại ở đây mà KHÔNG được
     định nghĩa lại trong :root[data-theme="light"] (điển hình: --color-heading) — hậu quả là
     toàn bộ h1-h4 và mọi chỗ dùng --color-heading mất màu xanh thương hiệu, ngả xám/đen dù
     giao diện đang hiển thị theme sáng, chỉ vì máy người dùng bật Dark Mode ở hệ điều hành. */
  :root:not([data-theme="light"]) {
    --color-primary-hover: #0a5a9c; --color-primary-fg: #9cc9e8; --color-primary-rgb: 126, 177, 214;
    --color-secondary: #16273a; --color-bg-primary: #0a131c; --color-bg-secondary: #101c29;
    --color-bg-tertiary: #16273a; --color-sidebar-bg: #142231; --color-bg-dark: #000000; --color-text-primary: #eef3f8;
    --color-heading: var(--color-text-primary); --color-text-secondary: #dbe7f0; --color-text-muted: #aec2d4;
    --color-text-min: #35516e; --color-border: #24374d; --color-accent-academic: #4d9bd6;
    --color-warning-bg: #3a2c10; --color-warning-text: #e8b559;
    --color-info-bg: #142a3d;   --color-info-text: #9cc9e8;
    --color-danger-bg: #3a1616; --color-danger-text: #ef7272;
    --color-success-bg: #123625; --color-success-text: #4ad991;
    --shadow-card: 0 40px 90px -40px rgba(0, 0, 0, 0.5);
    --shadow-btn-primary: 0 10px 24px -10px rgba(0, 0, 0, 0.6);
    --color-glass-bg: rgba(10, 19, 28, 0.75); --color-glass-border: rgba(255, 255, 255, 0.07);
  }
}
:root[data-theme="dark"] { 
    --color-primary-hover: #0a5a9c; --color-primary-fg: #9cc9e8; --color-primary-rgb: 126, 177, 214;
    --color-secondary: #16273a; --color-bg-primary: #0a131c; --color-bg-secondary: #101c29;
    --color-bg-tertiary: #16273a; --color-sidebar-bg: #142231; --color-bg-dark: #000000; --color-text-primary: #eef3f8;
    --color-heading: var(--color-text-primary); --color-text-secondary: #dbe7f0; --color-text-muted: #aec2d4;
    --color-text-min: #35516e; --color-border: #24374d; --color-accent-academic: #4d9bd6;
    --color-warning-bg: #3a2c10; --color-warning-text: #e8b559;
    --color-info-bg: #142a3d;   --color-info-text: #9cc9e8;
    --color-danger-bg: #3a1616; --color-danger-text: #ef7272;
    --color-success-bg: #123625; --color-success-text: #4ad991;
    --shadow-card: 0 40px 90px -40px rgba(0, 0, 0, 0.5);
    --shadow-btn-primary: 0 10px 24px -10px rgba(0, 0, 0, 0.6);
    --color-glass-bg: rgba(10, 19, 28, 0.75); --color-glass-border: rgba(255, 255, 255, 0.07);
}
:root[data-theme="light"] {
  --color-primary-hover: #003660; --color-primary-fg: var(--color-primary); --color-primary-rgb: 0, 71, 128;
  --color-secondary: #f0f4f8; --color-bg-primary: #ffffff; --color-bg-secondary: #f5f8fa;
  --color-bg-tertiary: #e6eff7; --color-sidebar-bg: #eef3f8; --color-bg-dark: #0a131c; --color-text-primary: #0a131c;
  --color-text-secondary: #202f3e; --color-text-muted: #506275; --color-text-min: #a0b2c6;
  --color-border: #d0dfed; --color-accent-academic: #1d70b8;
  --color-warning-bg: #fbf3e2; --color-warning-text: #8a5a12;
  --color-info-bg: #e3ecf5;   --color-info-text: #155a94;
  --color-danger-bg: #fdf2f2; --color-danger-text: #c53030;
  --color-success-bg: #e5f3ec; --color-success-text: #1a7a4c;
  --shadow-card: 0 40px 90px -40px rgba(var(--color-primary-rgb), 0.03);
  --shadow-btn-primary: 0 10px 24px -10px rgba(var(--color-primary-rgb), 0.5);
  --color-glass-bg: rgba(255, 255, 255, 0.82); --color-glass-border: rgba(var(--color-primary-rgb), 0.08);
}

/* Base Reset & Typography */
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
    font-family: 'Google Sans Flex', system-ui, -apple-system, sans-serif;
    background-color: var(--color-bg-secondary);
    color: var(--color-text-primary);
    line-height: 1.625;
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
}
@media (max-width: 768px) {
    body { font-size: 14px; }
}

h1, h2, h3, h4, h5, h6 {
    color: var(--color-heading);
    margin-bottom: 0.5em;
}
h1 { font-size: clamp(24px, 2.4vw, 30px); font-weight: 700; line-height: 1.2; letter-spacing: -0.01em; }
h2 { font-size: clamp(20px, 2vw, 24px); font-weight: 700; line-height: 1.25; }
h3 { font-size: 18px; font-weight: 600; }
h4 { font-size: 16px; font-weight: 600; }
.qq-skkn-hero-title { font-size: clamp(38px, 6vw, 72px); font-weight: 800; line-height: 1.02; letter-spacing: -0.04em; }
.qq-skkn-accent-text {
    font-family: 'Newsreader', serif;
    font-style: italic;
    font-weight: 400;
}
.qq-skkn-numeric {
    font-family: 'Geist Mono', monospace;
    font-variant-numeric: tabular-nums;
}

/* Utilities */
.qq-skkn-text-center { text-align: center; }
.qq-skkn-text-muted { color: var(--color-text-muted); }
.qq-skkn-text-primary { color: var(--color-text-primary); }
.qq-skkn-mb-3 { margin-bottom: 1rem; }
.qq-skkn-mb-4 { margin-bottom: 1.5rem; }
.qq-skkn-mt-4 { margin-top: 1.5rem; }

/* SVG Icon */
.qq-skkn-icon {
    width: 24px;
    height: 24px;
    display: inline-block;
    vertical-align: middle;
}
.qq-skkn-icon-sm { width: 18px; height: 18px; }

/* Buttons */
.qq-skkn-btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: 8px;
    white-space: nowrap;   /* MỚI — chặn chữ trong nút xuống dòng */
    padding: 10px 20px;
    border-radius: var(--radius-pill);
    font-family: 'Google Sans Flex', sans-serif;
    font-weight: 600;
    font-size: 15px;
    cursor: pointer;
    border: none;
    text-decoration: none;
    transition: all 0.2s ease;
}
.qq-skkn-btn:active {
    transform: scale(0.96);
}
.qq-skkn-btn-primary {
    background-color: var(--color-primary);
    color: #ffffff;
    box-shadow: var(--shadow-btn-primary);
}
.qq-skkn-btn-primary:hover {
    background-color: var(--color-primary-hover);
    color: #ffffff;
}
.qq-skkn-btn-secondary {
    background-color: var(--color-bg-primary);
    color: var(--color-text-primary);
    border: 1px solid var(--color-border);
}
.qq-skkn-btn-secondary:hover {
    background-color: var(--color-secondary);
    transform: translateY(-1px);
}

@keyframes pulse-glow {
    0% { box-shadow: 0 0 0 0 rgba(var(--color-primary-rgb), 0.4); }
    70% { box-shadow: 0 0 0 10px rgba(var(--color-primary-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--color-primary-rgb), 0); }
}
.qq-skkn-btn-pulse {
    animation: pulse-glow 2s infinite;
}

/* Badges / Pills */
.qq-skkn-pill {
    display: inline-flex; align-items: center; gap: 6px;
    white-space: nowrap;   /* MỚI — chặn chữ trong pill xuống dòng */
    padding: 4px 12px;
    border-radius: var(--radius-pill);
    font-size: 13px;
    font-weight: 600;
}
.qq-skkn-pill-grade {
    min-width: 120px;
    justify-content: center;
}
.qq-skkn-pill-top {
    background-color: var(--color-primary);
    color: #ffffff;
}
.qq-skkn-pill-success {
    background-color: var(--color-success-bg);
    color: var(--color-success-text);
}
.qq-skkn-pill-warning {
    background-color: var(--color-warning-bg);
    color: var(--color-warning-text);
}
.qq-skkn-pill-danger {
    background-color: var(--color-danger-bg);
    color: var(--color-danger-text);
}
.qq-skkn-pill-info {
    background-color: var(--color-info-bg);
    color: var(--color-info-text);
}
.qq-skkn-pill-neutral {
    background-color: var(--color-bg-tertiary);
    color: var(--color-text-secondary);
}

/* Badge/pill tự đặt style riêng (không dùng class .qq-skkn-pill dùng chung) rải rác ở nhiều View — cùng
   thiếu white-space: nowrap nên chữ bị xuống dòng giữa chừng khi cột bảng hẹp. Sửa chung 1 lần bằng attribute
   selector thay vì sửa lẻ từng file. */
[style*="border-radius: var(--radius-pill)"] {
    white-space: nowrap;
}

/* Cards */
.qq-skkn-card {
    background-color: var(--color-bg-primary);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card-outer);
    box-shadow: var(--shadow-card);
    padding: 24px;
    transition: all 0.25s ease;
}
.qq-skkn-card-hover:hover {
    transform: translateY(-3px);
    border-color: rgba(var(--color-primary-rgb), 0.2);
    box-shadow: 0 20px 40px -10px rgba(var(--color-primary-rgb), 0.1);
}

/* Navbar */
.qq-skkn-nav {
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    background-color: var(--color-glass-bg);
    border-bottom: 1px solid var(--color-glass-border);
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 24px;
}
.qq-skkn-nav-logo {
    display: flex; align-items: center; gap: 12px;
    text-decoration: none;
    color: var(--color-heading);
    font-weight: 800;
    font-size: 1.1rem;
}
.qq-skkn-nav-logo img {
    height: 36px;
    width: auto;
}
.qq-skkn-nav-actions {
    display: flex; align-items: center; gap: 16px;
}
.qq-skkn-mobile-menu-btn {
    display: none;
    background: none; border: none; cursor: pointer; color: var(--color-text-primary);
}

/* Sidebar Layout */
.qq-skkn-layout {
    display: flex;
    min-height: 100vh;
}
.qq-skkn-sidebar {
    width: 260px;
    background-color: var(--color-sidebar-bg);
    border-right: 1px solid var(--color-border);
    transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
    z-index: 90;
    overflow-x: hidden;
}
.qq-skkn-sidebar.collapsed {
    width: 72px;
}
.qq-skkn-sidebar-header {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 16px;
    border-bottom: 1px solid var(--color-border);
}
.qq-skkn-sidebar.collapsed .qq-skkn-sidebar-header {
    justify-content: center;
}
.qq-skkn-sidebar-content {
    padding: 16px 0;
}
.qq-skkn-sidebar-group {
    margin-bottom: 16px;
}
.qq-skkn-sidebar-title {
    font-size: 11px;
    text-transform: uppercase;
    font-weight: 700;
    color: var(--color-text-muted);
    letter-spacing: 0.5px;
    padding: 0 20px;
    margin-bottom: 8px;
    white-space: nowrap;
}
.qq-skkn-sidebar.collapsed .qq-skkn-sidebar-title {
    display: none;
}
.qq-skkn-sidebar-title-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    user-select: none;
    /* To hơn + màu nổi bật hơn (--color-primary-fg — token đổi đúng theo theme sáng/tối, không
       dùng --color-primary thẳng vì biến đó giữ cố định mọi theme, dễ mất tương phản ở dark) so
       với 3 mục còn lại của .qq-skkn-sidebar-title dùng chung — chỉ áp cho 3 nhóm bấm được. */
    font-size: 13px;
    color: var(--color-primary-fg);
}
.qq-skkn-sidebar-title-toggle:hover {
    color: var(--color-primary-hover);
}
.qq-skkn-sidebar-chevron {
    font-size: 12px;
    line-height: 1;
    color: var(--color-primary-fg);
}
.qq-skkn-sidebar-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 20px;
    color: var(--color-text-secondary);
    text-decoration: none;
    font-weight: 500;
    transition: background 0.2s, color 0.2s;
    white-space: nowrap;
}
.qq-skkn-sidebar-link:hover {
    background-color: rgba(var(--color-primary-rgb), 0.05);
    color: var(--color-primary-fg);
}
.qq-skkn-sidebar-link.active {
    background-color: rgba(var(--color-primary-rgb), 0.1);
    color: var(--color-primary-fg);
    font-weight: 700;
    border-right: 3px solid var(--color-primary-fg);
}
.qq-skkn-sidebar-link .qq-skkn-icon {
    flex-shrink: 0;
}
.qq-skkn-sidebar-text {
    opacity: 1;
    transition: opacity 0.2s;
}
.qq-skkn-sidebar.collapsed .qq-skkn-sidebar-text {
    opacity: 0;
    display: none;
}
.qq-skkn-workspace {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.qq-skkn-topbar {
    background-color: var(--color-bg-primary);
    border-bottom: 1px solid var(--color-border);
    position: sticky;
    top: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 24px;
    height: 57px;
}

/* Tables */
.qq-skkn-table-wrapper {
    overflow-x: auto;
    border-radius: var(--radius-data);
    border: 1px solid var(--color-border);
    background: var(--color-bg-primary);
}
.qq-skkn-table {
    width: 100%; border-collapse: collapse; text-align: left;
}
.qq-skkn-table th {
    background-color: var(--color-bg-secondary);
    color: var(--color-text-secondary);
    font-weight: 600;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
}
.qq-skkn-table td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-primary);
    transition: background 0.2s ease;
}
.qq-skkn-table tr:last-child td { border-bottom: none; }
.qq-skkn-table tbody tr:hover td {
    background-color: var(--color-bg-secondary);
}

/* Forms */
.qq-skkn-field {
    width: 100%;
    padding: 10px 14px;
    border-radius: var(--radius-data);
    border: 1px solid var(--color-border);
    background-color: var(--color-bg-primary);
    color: var(--color-text-primary);
    font-family: inherit;
    font-size: 15px;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.qq-skkn-field:focus {
    outline: none;
    border-color: var(--color-primary-fg);
    box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.15);
}

/* Toast */
.qq-skkn-toast-stack {
    position: fixed;
    top: 24px; right: 24px;
    z-index: 9999;
    display: flex; flex-direction: column; gap: 12px;
}
.qq-skkn-toast {
    background-color: var(--color-bg-primary);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card-inner);
    box-shadow: var(--shadow-card);
    padding: 16px;
    width: 320px;
    transform: translateX(120%);
    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    display: flex; gap: 12px; align-items: flex-start;
}
.qq-skkn-toast.show {
    transform: translateX(0);
}
.qq-skkn-toast-icon { color: var(--color-primary-fg); }
.qq-skkn-toast-content h4 { margin: 0 0 4px; font-size: 15px; }
.qq-skkn-toast-content p { margin: 0; font-size: 13px; color: var(--color-text-muted); }
.qq-skkn-toast-close {
    margin-left: auto; background: none; border: none; cursor: pointer; color: var(--color-text-muted);
}

/* Bells (Not used in Layout right now per rules, but prepared) */
@keyframes bell-ring {
    0% { transform: rotate(0); }
    10% { transform: rotate(15deg); }
    20% { transform: rotate(-15deg); }
    30% { transform: rotate(10deg); }
    40% { transform: rotate(-10deg); }
    50% { transform: rotate(5deg); }
    60% { transform: rotate(-5deg); }
    70% { transform: rotate(0); }
    100% { transform: rotate(0); }
}
.qq-skkn-bell-btn:hover .qq-skkn-icon { animation: bell-ring 1s ease; }

/* Grid icon cell from demo */
.qq-skkn-icon-cell {
    transition: all 0.2s ease;
    border-radius: var(--radius-card-inner);
    padding: 20px;
    text-align: center;
}
.qq-skkn-icon-cell:hover {
    transform: translateY(-2px);
    background-color: var(--color-bg-tertiary);
}

/* Animations */
@keyframes text-shimmer {
    0% { background-position: -200% center; }
    100% { background-position: 200% center; }
}
.text-shimmer {
    background: linear-gradient(90deg, var(--color-text-primary) 30%, var(--color-primary-fg) 50%, var(--color-text-primary) 70%);
    background-size: 200% auto;
    color: transparent;
    -webkit-background-clip: text;
    background-clip: text;
    animation: text-shimmer 3s infinite linear;
}

.reveal-on-scroll {
    opacity: 0; transform: translateY(20px);
    transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.reveal-on-scroll.revealed {
    opacity: 1; transform: translateY(0);
}

@keyframes skeleton-loading {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}
.skeleton {
    background: linear-gradient(90deg, var(--color-bg-secondary) 25%, var(--color-border) 50%, var(--color-bg-secondary) 75%);
    background-size: 400% 100%;
    animation: skeleton-loading 1.5s ease-in-out infinite;
    border-radius: 4px;
}

/* Mobile Nav */
@media (max-width: 900px) {
    .qq-skkn-mobile-menu-btn { display: block; }
    .qq-skkn-layout {
        flex-direction: column;
    }
    .qq-skkn-sidebar {
        position: fixed;
        left: -260px;
        transition: left 0.3s ease;
        z-index: 1000;
        width: 260px; /* Force full width on mobile even if collapsed on desktop */
    }
    .qq-skkn-sidebar.collapsed {
        width: 260px;
    }
    .qq-skkn-sidebar.collapsed .qq-skkn-sidebar-text,
    .qq-skkn-sidebar.collapsed .qq-skkn-sidebar-title {
        display: block;
        opacity: 1;
    }
    .qq-skkn-sidebar.show {
        left: 0;
        box-shadow: 0 0 50px rgba(0,0,0,0.5);
    }
    .qq-skkn-workspace {
        width: 100%;
    }

    /* Chỉ giữ 1 nút mở menu duy nhất trên mobile (.qq-skkn-mobile-menu-btn ở topbar) —
       ẩn nút thu gọn sidebar (.qq-skkn-sidebar-toggle) vì nó chỉ có ý nghĩa trên desktop;
       để cả 2 cùng hiện gây rối mắt khi sidebar mở dạng overlay trên mobile. */
    .qq-skkn-sidebar-toggle {
        display: none;
    }

    /* Topbar/nav mobile: bớt phần tử phụ + siết khoảng cách/lề để hamburger và các nút
       thao tác chính (chuông, hồ sơ, đăng xuất) luôn có đủ chỗ hiển thị rõ ràng, không bị
       tràn/che khuất khi nhiều phần tử chen trong 1 hàng hẹp. Bắt buộc !important — TASK-38
       thêm style="display: inline-block" nội tuyến cho khối chữ 2 dòng ở Cổng công bố, mà
       style nội tuyến luôn thắng rule CSS ngoài nếu rule đó không có !important. */
    .qq-skkn-brand-text,
    .qq-skkn-user-name {
        display: none !important;
    }
    .qq-skkn-topbar,
    .qq-skkn-nav {
        padding-left: 12px;
        padding-right: 12px;
    }
    .qq-skkn-nav-actions {
        gap: 8px;
    }

    /* Dropdown chuông thông báo định vị theo nút chuông (position: relative của thẻ cha) —
       trên màn hình hẹp, nút chuông không nằm sát mép phải nên hộp rộng 320px cố định dễ
       tràn ra ngoài viewport. Chuyển sang định vị cố định theo viewport để không bao giờ
       tràn, bất kể nút chuông nằm ở đâu trong topbar. */
    .qq-skkn-notif-dropdown {
        position: fixed;
        top: 60px;
        left: 12px;
        right: 12px;
        width: auto;
    }

    /* Các hàng flex nội tuyến (style="display: flex; gap: ...") dùng lặp lại ở ~42 view để xếp
       nhiều trường form/bộ lọc nằm ngang — không có flex-wrap, bị bóp méo trên mobile. Ép wrap
       bằng attribute selector khớp chuỗi trong thuộc tính style (bắt buộc dùng !important vì
       style nội tuyến luôn có độ ưu tiên cao hơn CSS ngoài, không có cách nào khác để ghi đè). */
    [style*="display: flex"] {
        flex-wrap: wrap !important;
    }
    [style*="display: flex"] > * {
        flex: 1 1 200px;
    }

    /* Riêng <form> bộ lọc/tìm kiếm (Đợt, Trạng thái, Lĩnh vực, ô tìm kiếm...) — 1 số form có
       RIÊNG từng field 1 giá trị flex/max-width khác nhau (VD input flex:2, select flex:1,
       div max-width:400px) khiến rule chung ngay trên (không !important) không thắng được
       style nội tuyến của chính field đó — trên mobile các field vẫn chen chung 1 hàng
       không đều nhau thay vì xuống hẳn 1 cột như mong muốn, thay vì bị đẩy wrap. Ép RIÊNG
       cho thẻ <form> (bộ lọc luôn dùng <form>, khác với `<div>` của thanh phân trang/nhóm
       pill không nên bị ép xuống 1 cột) để mỗi field xuống đúng 1 hàng, rộng bằng nhau. */
    form[style*="display: flex"] > * {
        flex: 1 1 100% !important;
        max-width: 100% !important;
    }

    /* Lớp loại trừ cho các khối flex nội tuyến không nên bóp méo/giãn chiều rộng con.
       Định nghĩa thêm biến thể `form.qq-skkn-no-wrap-mobile` cùng độ ưu tiên CSS (loại +
       lớp) với rule `form[style*="display: flex"] > *` ở trên để thắng được đúng theo thứ
       tự khai báo sau — dùng cho form hành động gọn trong 1 hàng bảng (VD nhan-rong.php)
       không nên bị ép xuống 1 cột như form bộ lọc đầu trang. */
    .qq-skkn-no-wrap-mobile {
        flex-wrap: nowrap !important;
    }
    .qq-skkn-no-wrap-mobile > *,
    form.qq-skkn-no-wrap-mobile > * {
        flex: initial !important;
        max-width: none !important;
    }
}

/* Phân trang (Layout::renderPagination()) — căn giữa, 2 phiên bản chuyển đổi theo
   độ rộng màn hình: đầy đủ số trang trên desktop/tablet, rút gọn còn Trước/Sau +
   "Trang X / Y" trên mobile (liệt kê nhiều số trang trên màn hình hẹp dễ vỡ layout
   và khó bấm trúng bằng ngón tay — khuyến nghị UX phổ biến cho pagination mobile). */
.qq-skkn-pagination {
    display: flex;
    justify-content: center;
    margin-top: 20px;
}
.qq-skkn-pagination-numbers {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.qq-skkn-pagination-compact {
    display: none;
    justify-content: center;
    align-items: center;
    gap: 12px;
}
@media (max-width: 768px) {
    .qq-skkn-pagination-numbers { display: none; }
    .qq-skkn-pagination-compact { display: flex; }
}

/* Reduced Motion Override */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* Utilities for TASK-24 */
.qq-skkn-form-group { margin-bottom: 16px; }
.qq-skkn-form-label { display: block; font-weight: 600; margin-bottom: 6px; color: var(--color-heading); }

.qq-skkn-alert { padding: 12px 16px; margin-bottom: 20px; border-radius: var(--radius-card-inner); border: 1px solid; }
.qq-skkn-alert-success { background: var(--color-success-bg); color: var(--color-success-text); border-color: var(--color-success-text); }
.qq-skkn-alert-danger { background: var(--color-danger-bg); color: var(--color-danger-text); border-color: var(--color-danger-text); }
.qq-skkn-alert-warning { background: var(--color-warning-bg); color: var(--color-warning-text); border-color: var(--color-warning-text); }
.qq-skkn-alert-info { background: var(--color-info-bg); color: var(--color-info-text); border-color: var(--color-info-text); }

/* Popup cảnh báo giữa màn hình cho thông báo nhất thời sau submit (xem script cuối renderFooter()
   trong Layout.php — bắt các khối [qq-skkn-alert-flash], ẩn khung cũ, hiện popup này thay thế). */
.qq-skkn-modal-overlay {
    position: fixed; inset: 0; background: rgba(0, 0, 0, 0.55);
    display: flex; align-items: center; justify-content: center;
    z-index: 9999; padding: 20px;
}
.qq-skkn-modal-box {
    background: var(--color-bg-primary); border-radius: var(--radius-card);
    max-width: 440px; width: 100%; padding: 28px 24px 24px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
    text-align: center; animation: qqSkknModalIn 0.18s ease-out;
}
@keyframes qqSkknModalIn {
    from { transform: scale(0.92); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}
.qq-skkn-modal-icon {
    width: 56px; height: 56px; border-radius: 50%; margin: 0 auto 16px;
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; font-weight: 700;
}
.qq-skkn-modal-icon.success { background: var(--color-success-bg); color: var(--color-success-text); }
.qq-skkn-modal-icon.danger { background: var(--color-danger-bg); color: var(--color-danger-text); }
.qq-skkn-modal-icon.warning { background: var(--color-warning-bg); color: var(--color-warning-text); }
.qq-skkn-modal-icon.info { background: var(--color-info-bg); color: var(--color-info-text); }
.qq-skkn-modal-message {
    color: var(--color-text-primary); font-size: 15px; line-height: 1.6;
    white-space: pre-line; margin-bottom: 20px;
}
.qq-skkn-modal-btn { padding: 10px 32px; }

.qq-skkn-import-box { border: 2px dashed var(--color-border); border-radius: var(--radius-card-inner); padding: 30px; text-align: center; background: var(--color-bg-secondary); }

/* Utilities for Printing (van-ban.php) */
/* Lề theo đúng Nghị định 30/2020/NĐ-CP (thể thức văn bản hành chính): trên/dưới 20-25mm,
   trái 30-35mm (chừa chỗ đóng dấu/lưu hồ sơ), phải 15-20mm — trước đây 20mm đều 4 cạnh,
   lề trái thiếu so với mức tối thiểu quy định. */
.qq-skkn-print-container {
    width: 210mm;
    min-height: 297mm;
    margin: 0 auto;
    background: var(--color-bg-primary);
    padding: 20mm 20mm 20mm 30mm;
    box-shadow: var(--shadow-card);
    box-sizing: border-box;
    font-family: "Times New Roman", Times, serif;
    font-size: 14pt;
    line-height: 1.5;
    color: var(--color-text-primary);
}
@media print {
    /* Văn bản in ra giấy LUÔN nền trắng/chữ đen bất kể theme sáng/tối đang bật
       trên màn hình — dùng màu cứng có chủ đích ở đây (không phải vi phạm quy
       tắc token), vì giấy in vật lý không có khái niệm "theme". */
    body { background: #fff; padding: 0; }
    .qq-skkn-print-container { background: #fff; color: #000; box-shadow: none; margin: 0; padding: 0; width: 100%; min-height: auto; }
    .qq-skkn-print-title-danger { color: #000 !important; }
    .qq-skkn-print-hide { display: none !important; }
}
/* XEM (chưa in) trên màn hình hẹp hơn khổ A4 (~794px) — width:210mm cố định phía trên tràn
   ngang toàn trang trên điện thoại/tablet nhỏ (rà soát responsive phát hiện: viền khung 375px
   bị đẩy tràn hẳn ra ngoài, chữ đầu dòng bị cắt cụt). Co container về 100% + giảm lề (20-30mm
   quá lớn so với màn 375px, ăn gần nửa chiều rộng) — CHỈ áp dụng khi XEM trên màn hình, giữ
   nguyên @media print phía trên cho đúng khổ giấy A4 thật khi in ra. Dùng chung breakpoint
   768px với rule [style*="display: flex"] bên dưới (khối Quốc hiệu 2 cột tự wrap dọc đúng lúc
   container co lại, không lệch nhau giữa 2 rule) — không cần sửa từng file văn bản in riêng lẻ. */
@media screen and (max-width: 768px) {
    .qq-skkn-print-container {
        width: 100%;
        min-height: auto;
        padding: 16px;
    }
}

/* Thông báo (Chuông) */
.qq-skkn-notif-badge {
    position: absolute; top: 2px; right: 2px;
    min-width: 16px; height: 16px; padding: 0 4px;
    background: var(--color-danger-text); color: #fff;
    border-radius: 999px; font-size: 10px; font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    line-height: 1;
}
.qq-skkn-notif-dropdown {
    display: none;
    position: absolute; top: calc(100% + 8px); right: 0;
    width: 420px; max-height: 400px; overflow-y: auto;
    background: var(--color-bg-primary); border: 1px solid var(--color-border);
    border-radius: var(--radius-card-inner); box-shadow: var(--shadow-card);
    z-index: 1100;
}
.qq-skkn-notif-dropdown.show { display: block; }
/* Thanh công cụ trên cùng — "Xem tất cả" trái, "Đánh dấu tất cả đã đọc" phải, cùng 1 hàng. */
.qq-skkn-notif-toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 10px 16px; border-bottom: 1px solid var(--color-border);
    background: var(--color-bg-secondary);
}
.qq-skkn-notif-view-all {
    background: none; border: none; padding: 0;
    color: var(--color-primary-fg); font-size: 12.5px; font-weight: 600; text-decoration: none;
}
.qq-skkn-notif-view-all:hover { text-decoration: underline; }
.qq-skkn-notif-mark-all {
    background: none; border: none; padding: 0;
    color: var(--color-primary-fg); font-size: 12.5px; font-weight: 600;
    cursor: pointer; font-family: inherit;
}
.qq-skkn-notif-mark-all:hover { text-decoration: underline; }
/* "Xoá tất cả" — hàng riêng NGAY DƯỚI thanh công cụ, tách biệt rõ bằng màu danger vì đây là thao
   tác phá huỷ dữ liệu (khác 2 nút phía trên không mất dữ liệu gì). */
.qq-skkn-notif-clear-all {
    display: block; width: 100%; text-align: center;
    padding: 8px 16px; border: none; border-bottom: 1px solid var(--color-border);
    background: var(--color-danger-bg); color: var(--color-danger-text);
    font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.qq-skkn-notif-clear-all:hover { text-decoration: underline; }
/* "Hiển thị thêm" — cuối danh sách item, chỉ xuất hiện khi còn thông báo cũ hơn chưa tải (xem
   qqSkknRenderShowMoreBtn() trong Layout.php). Nhạt hơn 2 nút hành động phía trên (không viền
   dưới đậm, nền trong suốt) vì đây chỉ là điều hướng tải thêm, không phải hành động dữ liệu. */
.qq-skkn-notif-show-more {
    display: block; width: 100%; text-align: center;
    padding: 10px 16px; border: none;
    background: none; color: var(--color-primary-fg);
    font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit;
}
.qq-skkn-notif-show-more:hover { background: var(--color-bg-secondary); text-decoration: underline; }
.qq-skkn-notif-show-more:disabled { cursor: default; opacity: 0.6; text-decoration: none; }
.qq-skkn-notif-item {
    display: block; padding: 12px 16px; text-decoration: none;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-primary); font-size: 14px;
}
.qq-skkn-notif-item:last-child { border-bottom: none; }
.qq-skkn-notif-item:hover { background: var(--color-bg-secondary); }
.qq-skkn-notif-item.unread { font-weight: 700; background: var(--color-info-bg); }
.qq-skkn-notif-empty { padding: 24px 16px; text-align: center; color: var(--color-text-muted); font-size: 14px; }
/* Nội dung đầy đủ email thay thế khi SMTP tự động đang TẮT (xem QqSkkn\Core\Notifier) —
   luôn thường (không đậm) kể cả khi thông báo chưa đọc, để phân biệt rõ với tiêu đề. */
.qq-skkn-notif-body {
    font-weight: 400; font-size: 12.5px; color: var(--color-text-muted);
    margin-top: 6px; white-space: pre-line; line-height: 1.5;
}
/* Trang /thong-bao "Xem tất cả" — mỗi dòng dùng lại đúng tông màu unread của dropdown chuông. */
.qq-skkn-notif-page-item:hover { background: var(--color-bg-secondary) !important; }

/* "Xem thêm/Thu gọn" cho khối text dài (4 trường Mô tả bản chất Sáng kiến ở các trang chi tiết
   read-only) — cắt theo max-height ~6 dòng (line-height ép 1.5, max-height = 9em nên tự thích ứng
   theo font-size từng nơi, không phải số px cố định), JS ở Layout.php (qqSkknInitCollapsible) chỉ
   hiện nút khi nội dung thực sự tràn quá khung, nội dung ngắn không có nút thừa.
   KHÔNG dùng -webkit-line-clamp: nội dung ở đây đi qua HtmlSanitizer::clean() có thể chứa thẻ <br>
   (xuống dòng), mà line-clamp tính sai layout dòng cuối khi có phần tử khối/br bên trong, gây lỗi
   CHỮ CHỒNG LÊN NHAU ở dòng bị cắt (đã xác nhận qua ảnh chụp thật của người dùng) — max-height +
   overflow:hidden đơn thuần không có lỗi này vì chỉ cắt thô theo chiều cao, không tính lại dòng. */
.qq-skkn-collapsible-content {
    max-height: 9em; line-height: 1.5; overflow: hidden; position: relative;
}
.qq-skkn-collapsible-content:not(.qq-skkn-collapsible-expanded)::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1.6em;
    background: linear-gradient(to bottom, transparent, var(--color-bg-secondary));
    pointer-events: none;
}
.qq-skkn-collapsible-content.qq-skkn-collapsible-expanded {
    max-height: none; overflow: visible;
}
.qq-skkn-collapsible-toggle {
    display: none; margin-top: 8px; padding: 0; border: none; background: none;
    color: var(--color-primary-fg); font-size: 13px; font-weight: 600;
    cursor: pointer; font-family: inherit;
}
.qq-skkn-collapsible-toggle:hover { text-decoration: underline; }

/* Thanh tiến trình dạng stepper (Quy trình xét duyệt hồ sơ ở HoSo/Views/detail.php, Tiến trình hoạt
   động của Đợt ở Dot/Views/detail.php) — gộp về 1 định nghĩa DUY NHẤT ở đây, trước đây mỗi View tự
   viết 1 bản CSS gần giống nhau trong thẻ <style> riêng.
   Đường nối ngang là 1 THANH DUY NHẤT chạy full-width phía SAU cả hàng (::before của .stepper-wrapper,
   z-index thấp nhất trong toàn khối) — KHÔNG ghép từng đoạn ::before/::after theo từng ô như cách cũ
   (mỗi ô tự vẽ nửa đoạn nối sang ô bên cạnh, phụ thuộc z-index so sánh CHÉO giữa các stacking context
   của những phần tử anh em khác nhau — dễ bị trình duyệt tính sai khiến đường nối đè lên vòng tròn số,
   người dùng phản ánh trực tiếp qua ảnh chụp thật). Vòng tròn số luôn có position:relative + z-index
   cao hơn hẳn + nền đặc (không trong suốt) nên LUÔN nổi trên đường nối trong mọi trường hợp. */
/* padding-top: 8px — KHÔNG phải trang trí, đây là chỗ dành cho vòng halo (box-shadow spread 5px)
   của bước "active". overflow-x: auto khiến trình duyệt tự ép overflow-y cũng thành auto (quy tắc
   tương thích CSS2.1 cũ: 1 trục auto/scroll thì trục "visible" còn lại KHÔNG THỂ giữ visible, dù có
   khai overflow-y: visible tường minh cũng bị ép lại), tức là mọi thứ tràn ra ngoài khung đều bị cắt
   — halo của bước active tràn lên trên đúng 5px bị cắt cụt mất phần trên, người dùng phản ánh kèm
   ảnh chụp thật (vòng tròn "1" bị vạt phẳng phía trên). Cách sửa: không gỡ overflow-x (vẫn cần cho
   Đợt nhiều mốc cuộn ngang trên màn hẹp), mà CHỪA ĐỦ CHỖ trong chính khung bằng padding-top — halo
   nằm gọn trong padding, không còn chạm mép clip nữa. */
.stepper-wrapper {
    display: flex; justify-content: space-between; position: relative;
    margin-top: 30px; margin-bottom: 24px; overflow-x: auto; padding: 8px 0 4px;
}
.stepper-wrapper::before {
    content: ""; position: absolute; left: 0; right: 0; top: 23px; height: 3px;
    background-color: var(--color-border); z-index: 0;
}
.stepper-item {
    position: relative; z-index: 1;
    display: flex; flex-direction: column; align-items: center;
    flex: 1; min-width: 110px; text-align: center;
}
.step-counter {
    position: relative; z-index: 2;
    display: flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 50%;
    background-color: var(--color-border); color: var(--color-bg-primary);
    font-weight: bold; font-size: 14px; margin-bottom: 5px;
}
.step-name { font-size: 13px; font-weight: bold; color: var(--color-text-muted); text-align: center; }
.step-date { margin-top: 4px; font-size: 12px; color: var(--color-text-muted); text-align: center; }

.stepper-item.completed .step-counter { background-color: var(--color-success-text); }
.stepper-item.completed .step-name { color: var(--color-success-text); }
.stepper-item.active .step-counter { background-color: var(--color-primary); box-shadow: 0 0 0 5px rgba(var(--color-primary-rgb), 0.3); }
.stepper-item.active .step-name { color: var(--color-primary); }
.stepper-item.error .step-counter { background-color: var(--color-danger-text); }
.stepper-item.error .step-name { color: var(--color-danger-text); }
.stepper-item.warning .step-counter { background-color: var(--color-warning-text); }
.stepper-item.warning .step-name { color: var(--color-warning-text); }

/* Thanh bộ lọc dạng "nhãn + ô chọn" lặp lại nhiều lần trên 1 hàng (VD HoSo/Views/index.php: Trạng
   thái đợt / Lọc theo đợt / Số dòng-trang) — trước đây mỗi label và mỗi select là 1 flex-item RIÊNG
   LẺ nằm thẳng trong <form>, nên khi flex-wrap phải xuống dòng (màn hẹp/nhiều đợt tên dài), nó ngắt
   NGAY GIỮA 1 cặp label-select thay vì ngắt giữa 2 cặp — label "Lọc theo đợt:" bị trôi lên 1 mình,
   ô chọn của nó rớt xuống dòng khác, nhìn rất rối (người dùng phản ánh kèm ảnh chụp thật). Gộp mỗi
   cặp label+select vào 1 .qq-skkn-filter-group riêng để chúng LUÔN xuống dòng CÙNG NHAU. Trên mobile
   (≤768px), xếp thành đúng 1 cột dọc theo yêu cầu người dùng thay vì tự do wrap nhiều dòng lởm chởm. */
.qq-skkn-filter-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin: 0; }
.qq-skkn-filter-group { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.qq-skkn-filter-group > .qq-skkn-form-label { margin-bottom: 0; white-space: nowrap; }
@media (max-width: 768px) {
    .qq-skkn-filter-bar { flex-direction: column; align-items: stretch; }
    .qq-skkn-filter-group { width: 100%; }
    .qq-skkn-filter-group > select { flex: 1; width: 100%; min-width: 0; }
}
