/* ==========================================================================
   Maly Blimat — Design System (app.css)
   Native CSS, custom properties, logical properties for full RTL support.
   Every color is a variable. Never hardcode a color outside this token block.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens — light theme (default)
   -------------------------------------------------------------------------- */
:root {
    /* Brand / primary — confident indigo-blue */
    --color-primary-h: 231;
    --color-primary: #3454d1;
    --color-primary-rgb: 52, 84, 209;
    --color-primary-hover: #2a44ac;
    --color-primary-active: #223689;
    --color-primary-subtle: #e8ecfb;
    --color-primary-subtle-text: #2a44ac;

    /* Feedback colors */
    --color-success: #1f9d55;
    --color-success-rgb: 31, 157, 85;
    --color-success-subtle: #e3f6ea;
    --color-success-subtle-text: #146b3a;

    --color-warning: #b7791f;
    --color-warning-rgb: 183, 121, 31;
    --color-warning-subtle: #fbf0dc;
    --color-warning-subtle-text: #7a4f0f;

    --color-danger: #d1373f;
    --color-danger-rgb: 209, 55, 63;
    --color-danger-subtle: #fbe6e7;
    --color-danger-subtle-text: #96262c;

    --color-info: #1e88b0;
    --color-info-rgb: 30, 136, 176;
    --color-info-subtle: #e2f3f8;
    --color-info-subtle-text: #146078;

    /* Neutral surfaces & text */
    --color-bg: #f4f6fb;
    --color-surface: #ffffff;
    --color-surface-rgb: 255, 255, 255;
    --color-surface-2: #f8f9fc;
    --color-surface-2-rgb: 248, 249, 252;
    --color-surface-hover: #eef1f8;
    --color-surface-hover-rgb: 238, 241, 248;
    --color-border: #e1e5ee;
    --color-border-strong: #c7cdd9;

    --color-text: #1c2530;
    --color-text-rgb: 28, 37, 48;
    --color-text-secondary: #4b5563;
    /* Measured 4.26:1 on --color-bg at #6b7686 -- under the AA floor on every
       page subtitle in the app. #646e7e measures 4.77:1 on --color-bg and
       5.16:1 on --color-surface, and is still visibly "muted". */
    --color-text-muted: #646e7e;
    --color-text-inverse: #ffffff;

    /*
     * Ink for a SOLID button/badge of each accent. Not always white: at these
     * light-theme values white measures 3.54:1 on success, 3.65:1 on warning
     * and 4.03:1 on info, so those three take dark ink (5.19 / 5.04 / 4.56).
     * Primary and danger keep white (6.30 / 4.88). Every figure re-measured in
     * the browser by tests/teacher_ui_test.php.
     */
    --color-on-primary: #ffffff;
    --color-on-success: #10141f;
    --color-on-warning: #10141f;
    --color-on-danger: #ffffff;
    --color-on-info: #10141f;

    --color-sidebar-bg: #151b2e;
    --color-sidebar-text: #c7cde0;
    --color-sidebar-text-active: #ffffff;
    --color-sidebar-border: rgba(255, 255, 255, 0.08);
    --color-sidebar-active-bg: rgba(255, 255, 255, 0.08);

    --color-overlay: rgba(15, 20, 33, 0.5);

    /* Radius scale */
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 16px;
    --radius-xl: 24px;
    --radius-pill: 999px;

    /* Shadow scale */
    --shadow-sm: 0 1px 2px rgba(20, 25, 40, 0.06);
    --shadow-md: 0 4px 14px rgba(20, 25, 40, 0.08);
    --shadow-lg: 0 12px 32px rgba(20, 25, 40, 0.12);

    /* Spacing scale */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 64px;

    /* Font sizes */
    --font-size-xs: 0.75rem;
    --font-size-sm: 0.875rem;
    --font-size-base: 1rem;
    --font-size-md: 1.0625rem;
    --font-size-lg: 1.25rem;
    --font-size-xl: 1.5rem;
    --font-size-2xl: 2rem;
    --font-size-3xl: 2.75rem;

    /* Typography */
    --font-latin: 'Segoe UI', 'Inter', -apple-system, BlinkMacSystemFont, Tahoma, Arial, sans-serif;
    --font-arabic: 'Noto Kufi Arabic', 'Segoe UI', Tahoma, Arial, sans-serif;
    --font-body: var(--font-latin);
    --line-height-base: 1.5;
    --line-height-arabic: 1.9;
    --line-height-body: var(--line-height-base);

    /* Motion */
    --transition-fast: 120ms ease;
    --transition-base: 200ms ease;

    /* Layout */
    --sidebar-width: 264px;
    --sidebar-collapsed-width: 76px;
    --topbar-height: 64px;
    /* Diameter of the floating chat launcher (chat.css). Declared here because
       page layout has to reserve room for it -- see .form-actions below. */
    --chat-launcher-size: 56px;

    /* Z-index scale */
    --z-sidebar: 1030;
    --z-topbar: 1020;
    --z-backdrop: 1025;
    /* Above the topbar, so a Tom Select list rendered into <body> (see §23)
       is never painted underneath it; below modals, whose own dropdowns are
       descendants and therefore stack locally anyway. */
    --z-dropdown: 1040;
    --z-modal: 1050;
    --z-toast: 1080;
}

/* --------------------------------------------------------------------------
   2. Dark theme overrides — WCAG AA contrast maintained
   -------------------------------------------------------------------------- */
[data-theme="dark"] {
    --color-primary: #7690f0;
    --color-primary-rgb: 118, 144, 240;
    --color-primary-hover: #93a8f4;
    --color-primary-active: #aebdf7;
    --color-primary-subtle: #232d55;
    --color-primary-subtle-text: #c3cdfa;

    --color-success: #4ad080;
    --color-success-rgb: 74, 208, 128;
    --color-success-subtle: #143826;
    --color-success-subtle-text: #8fe6b2;

    --color-warning: #e0ac4c;
    --color-warning-rgb: 224, 172, 76;
    --color-warning-subtle: #3a2c10;
    --color-warning-subtle-text: #f0c987;

    --color-danger: #f0707a;
    --color-danger-rgb: 240, 112, 122;
    --color-danger-subtle: #3a1518;
    --color-danger-subtle-text: #f5a1a8;

    --color-info: #5cc2e8;
    --color-info-rgb: 92, 194, 232;
    --color-info-subtle: #123540;
    --color-info-subtle-text: #9adcf0;

    --color-bg: #10141f;
    --color-surface: #1a2032;
    --color-surface-rgb: 26, 32, 50;
    --color-surface-2: #212840;
    --color-surface-2-rgb: 33, 40, 64;
    --color-surface-hover: #262e48;
    --color-surface-hover-rgb: 38, 46, 72;
    --color-border: #2c3450;
    --color-border-strong: #3d4666;

    --color-text: #eef1f8;
    --color-text-rgb: 238, 241, 248;
    --color-text-secondary: #b7bfd4;
    --color-text-muted: #8891a8;
    --color-text-inverse: #10141f;

    /* Every dark accent is a LIGHT colour, so white ink fails on all five
       (primary measured 2.99:1, warning 2.06:1). Dark ink measures 6.14 /
       9.31 / 8.91 / 6.36 / 9.05. */
    --color-on-primary: #10141f;
    --color-on-success: #10141f;
    --color-on-warning: #10141f;
    --color-on-danger: #10141f;
    --color-on-info: #10141f;

    --color-sidebar-bg: #0b0e18;
    --color-sidebar-text: #a9b1cc;
    --color-sidebar-text-active: #ffffff;
    --color-sidebar-border: rgba(255, 255, 255, 0.06);
    --color-sidebar-active-bg: rgba(255, 255, 255, 0.07);

    --color-overlay: rgba(0, 0, 0, 0.6);

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 14px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 12px 32px rgba(0, 0, 0, 0.45);
}

/* --------------------------------------------------------------------------
   2b. Bootstrap neutral-palette bridge — the root cause of the dark-mode
       "invisible text" family
   --------------------------------------------------------------------------
   Bootstrap paints its neutral surfaces and text from --bs-body-color,
   --bs-body-bg, --bs-secondary-color, --bs-tertiary-bg and --bs-border-color.
   This design system switches themes with its OWN tokens and never redeclared
   Bootstrap's, so every component whose colour is not explicitly overridden
   below stayed frozen at the LIGHT values on a dark page. Measured examples,
   all Kurdish/RTL at 1440x900, dark theme:

     .card { color: var(--bs-body-color) }   -> rgb(33,37,41) on rgb(26,32,50)
       = 1.05:1. This is the teacher dashboard's .action-tile__label, the admin
       dashboard's .stat-tile__value, and the posts upload progress label.
     table.table.dataTable > :not(caption) > * > * { background-color:
       var(--bs-table-bg) } -> white cells, so every row of every DataTable
       measured 1.13:1 and its column titles 1.84:1.
     .wizard-progress .step { background: var(--bs-tertiary-bg) } -> 1.07:1.

   §7, §11b and §12 already fixed .card-header, .text-muted, the alerts and the
   dropdowns ONE COMPONENT AT A TIME from this same cause. Bridging the five
   variables at the root fixes the rest of the family at once -- and anything
   added later inherits the fix instead of repeating the bug.

   Nothing is hardcoded: each is an alias of a token that already changes with
   the theme. In LIGHT mode the values are within a shade of Bootstrap's own
   (#1c2530 vs #212529, #ffffff vs #ffffff, #f8f9fc vs #f8f9fa), so the light
   theme is unchanged by design.
   -------------------------------------------------------------------------- */
:root {
    --bs-body-color: var(--color-text);
    --bs-body-color-rgb: var(--color-text-rgb);
    --bs-body-bg: var(--color-surface);
    --bs-body-bg-rgb: var(--color-surface-rgb);
    --bs-emphasis-color: var(--color-text);
    --bs-emphasis-color-rgb: var(--color-text-rgb);
    --bs-secondary-color: var(--color-text-secondary);
    --bs-secondary-bg: var(--color-surface-hover);
    --bs-tertiary-color: var(--color-text-muted);
    --bs-tertiary-bg: var(--color-surface-2);
    /*
     * The -rgb COMPANIONS. Two members of the family above were missed, and
     * the reason is easy to walk past: the bridge sets --bs-tertiary-bg and
     * --bs-secondary-bg, but Bootstrap's .bg-body-tertiary and
     * .bg-body-secondary utilities do not read those. They read
     *
     *     background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity))
     *
     * so with only the non-rgb form bridged they stayed frozen at Bootstrap's
     * LIGHT defaults on a dark page: a white panel carrying the page's
     * light-on-dark text, which is the same ~1:1 failure this section was
     * written to end.
     *
     * Found on the teacher classes panel; fixed here rather than there,
     * because every future use of those two utilities has the same bug.
     *
     * .bg-light and .bg-dark are deliberately NOT bridged: those name a fixed
     * palette colour, not a themed surface, and anything that should follow
     * the theme wants the body-* utilities instead.
     */
    --bs-secondary-bg-rgb: var(--color-surface-hover-rgb);
    --bs-tertiary-bg-rgb: var(--color-surface-2-rgb);
    --bs-border-color: var(--color-border);
    --bs-border-color-translucent: var(--color-border);
    --bs-heading-color: var(--color-text);
    --bs-link-color: var(--color-primary);
    --bs-link-color-rgb: var(--color-primary-rgb);
    --bs-link-hover-color: var(--color-primary-hover);
    --bs-link-hover-color-rgb: var(--color-primary-rgb);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]):not([data-theme="dark"]) {
        --color-primary: #7690f0;
        --color-bg: #10141f;
        --color-surface: #1a2032;
    }
}

/* --------------------------------------------------------------------------
   3. Base / reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

html[dir="rtl"] { --font-body: var(--font-arabic); --line-height-body: var(--line-height-arabic); }
html[dir="ltr"] { --font-body: var(--font-latin); --line-height-body: var(--line-height-base); }

body {
    margin: 0;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-body);
    font-size: var(--font-size-base);
    line-height: var(--line-height-body);
    -webkit-font-smoothing: antialiased;
}

a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-hover); text-decoration: underline; }

img, svg { max-width: 100%; }

::selection { background: var(--color-primary-subtle); color: var(--color-primary-subtle-text); }

/* Scrollbars (WebKit) styled to match theme */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--color-border-strong); border-radius: var(--radius-pill); }
::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }

/* --------------------------------------------------------------------------
   4. App shell — sidebar + topbar + content
   -------------------------------------------------------------------------- */
.app-shell {
    display: flex;
    min-height: 100vh;
}

.sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--sidebar-width);
    background: var(--color-sidebar-bg);
    color: var(--color-sidebar-text);
    display: flex;
    flex-direction: column;
    z-index: var(--z-sidebar);
    transition: inset-inline-start var(--transition-base), width var(--transition-base);
    overflow: hidden;
    border-inline-end: 1px solid var(--color-sidebar-border);
}

.sidebar-brand {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: 0 var(--space-4);
    height: var(--topbar-height);
    min-height: var(--topbar-height);
    flex-shrink: 0;
    border-bottom: 1px solid var(--color-sidebar-border);
}

.sidebar-brand-link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-sidebar-text-active);
    font-weight: 700;
    font-size: var(--font-size-md);
    min-width: 0;
}
.sidebar-brand-link:hover { text-decoration: none; color: var(--color-sidebar-text-active); }

.sidebar-brand-logo,
.sidebar-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    background: transparent;
    flex-shrink: 0;
    object-fit: contain;
}

.sidebar-brand-text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-close-btn {
    background: transparent;
    border: 0;
    color: var(--color-sidebar-text);
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}
.sidebar-close-btn svg { width: 20px; height: 20px; }
.sidebar-close-btn:hover { background: var(--color-sidebar-active-bg); }

.sidebar-nav {
    flex: 1 1 auto;
    padding: var(--space-2);
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
}

.nav-list, .nav-children { list-style: none; margin: 0; padding: 0; }
.nav-children { padding-inline-start: var(--space-4); overflow: hidden; max-height: 0; transition: max-height var(--transition-base); }
.nav-children.show { max-height: 800px; }

.nav-item { margin-block-end: 2px; }

.nav-link, .nav-group-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    color: var(--color-sidebar-text);
    background: transparent;
    border: 0;
    font-size: var(--font-size-sm);
    text-align: start;
    cursor: pointer;
}
.nav-link:hover, .nav-group-toggle:hover {
    background: var(--color-sidebar-active-bg);
    color: var(--color-sidebar-text-active);
    text-decoration: none;
}
.nav-item.active > .nav-link,
.nav-item.active > .nav-group-toggle {
    background: var(--color-primary);
    color: #fff;
}
.nav-item-group.active > .nav-group-toggle { background: var(--color-sidebar-active-bg); color: var(--color-sidebar-text-active); }

.nav-icon { width: 20px; height: 20px; flex-shrink: 0; }
.nav-label { flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-caret { width: 16px; height: 16px; flex-shrink: 0; transition: transform var(--transition-fast); }
html[dir="rtl"] .nav-caret { transform: scaleX(-1); }
.nav-group-toggle[aria-expanded="true"] .nav-caret { transform: rotate(90deg); }
html[dir="rtl"] .nav-group-toggle[aria-expanded="true"] .nav-caret { transform: rotate(-90deg) scaleX(-1); }

.sidebar-collapse-footer {
    padding: var(--space-2);
    border-top: 1px solid var(--color-sidebar-border);
    flex-shrink: 0;
}

/*
 * The compact metrics above are for the desktop sidebar, where 56 nav entries
 * otherwise overflow the viewport. Below the lg breakpoint the sidebar is a
 * drawer people tap, so give the rows back their comfortable touch height --
 * there is no scrolling pressure there, since the drawer scrolls on its own.
 */
@media (max-width: 991.98px) {
    .nav-link, .nav-group-toggle { padding: var(--space-3); }
    .nav-item { margin-block-end: var(--space-1); }
}
.sidebar-collapse-btn {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-2);
    border-radius: var(--radius-md);
    background: transparent;
    border: 0;
    color: var(--color-sidebar-text);
}
.sidebar-collapse-btn svg { width: 20px; height: 20px; }
.sidebar-collapse-btn:hover { background: var(--color-sidebar-active-bg); color: var(--color-sidebar-text-active); }

.sidebar-backdrop {
    position: fixed;
    inset: 0;
    background: var(--color-overlay);
    z-index: var(--z-backdrop);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition-base);
}

.app-main {
    flex: 1 1 auto;
    min-width: 0;
    margin-inline-start: var(--sidebar-width);
    display: flex;
    flex-direction: column;
    transition: margin-inline-start var(--transition-base);
}

/* Desktop collapsed state */
#appShell.sidebar-collapsed .sidebar { width: var(--sidebar-collapsed-width); }
#appShell.sidebar-collapsed .sidebar .sidebar-brand-text,
#appShell.sidebar-collapsed .sidebar .nav-label,
#appShell.sidebar-collapsed .sidebar .nav-caret { display: none; }
#appShell.sidebar-collapsed .sidebar .nav-children { display: none; }
#appShell.sidebar-collapsed .app-main { margin-inline-start: var(--sidebar-collapsed-width); }
#appShell.sidebar-collapsed .nav-link, #appShell.sidebar-collapsed .nav-group-toggle { justify-content: center; }

/* --------------------------------------------------------------------------
   5. Topbar
   -------------------------------------------------------------------------- */
.topbar {
    position: sticky;
    top: 0;
    z-index: var(--z-topbar);
    min-height: var(--topbar-height);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-inline: var(--space-4);
}

.topbar-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: transparent;
    border: 1px solid transparent;
    color: var(--color-text-secondary);
    position: relative;
}
.topbar-btn:hover { background: var(--color-surface-hover); color: var(--color-text); }
.topbar-btn svg { width: 20px; height: 20px; }
.user-btn { width: auto; padding-inline: var(--space-2); gap: var(--space-2); }

.topbar-title { font-size: var(--font-size-md); font-weight: 600; margin: 0; }
.topbar-spacer { flex: 1 1 auto; }
.topbar-actions { display: flex; align-items: center; gap: var(--space-2); }

.icon-moon { display: none; }
[data-theme="dark"] .icon-sun { display: none; }
[data-theme="dark"] .icon-moon { display: block; }

.notif-badge {
    position: absolute;
    inset-block-start: 4px;
    inset-inline-end: 4px;
    min-width: 16px;
    height: 16px;
    padding-inline: 3px;
    border-radius: var(--radius-pill);
    background: var(--color-danger);
    color: #fff;
    font-size: 10px;
    line-height: 16px;
    text-align: center;
}

.user-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--color-primary-subtle);
    color: var(--color-primary-subtle-text);
    font-weight: 700;
    font-size: var(--font-size-sm);
    flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   6. Content area / page header
   -------------------------------------------------------------------------- */
.app-content { padding: var(--space-5); flex: 1 1 auto; min-width: 0; }

.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-5);
}

.breadcrumb-nav { margin-block-end: var(--space-1); }
.breadcrumb { display: flex; flex-wrap: wrap; gap: var(--space-1); list-style: none; padding: 0; margin: 0; font-size: var(--font-size-xs); color: var(--color-text-muted); }
.breadcrumb-item + .breadcrumb-item::before { content: "/"; margin-inline-end: var(--space-1); color: var(--color-text-muted); }
html[dir="rtl"] .breadcrumb-item + .breadcrumb-item::before { content: "\\"; }
.breadcrumb-item.active { color: var(--color-text-secondary); }

.page-title { font-size: var(--font-size-xl); font-weight: 700; margin: 0; }
.page-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   7. Cards
   -------------------------------------------------------------------------- */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}
.card-header {
    padding: var(--space-4);
    border-bottom: 1px solid var(--color-border);
    font-weight: 600;
    background: transparent;
    /*
     * Bootstrap resolves .card-header's colour from --bs-card-cap-color, which
     * this design system never re-declares, so every card title stayed at the
     * light-theme near-black (#212529) and became unreadable on a dark card.
     */
    color: var(--color-text);
}

/*
 * Same root cause for .text-muted (--bs-secondary-color): it is the class every
 * "(optional)" hint and page subtitle in the admin views uses, and it rendered
 * as near-black text on a dark surface. !important because Bootstrap's own
 * declaration carries it.
 */
.text-muted { color: var(--color-text-muted) !important; }

/* Same story again for a checkbox/radio's own label, which Bootstrap paints
   from --bs-body-color: measured rgb(33,37,41) on a rgb(26,32,50) card, i.e.
   the "issue a replacement" wording on the damage form was invisible in dark
   mode. .form-label was already tokenised; this one was missed. */
.form-check-label { color: var(--color-text); }
.card-body { padding: var(--space-4); }
.card-footer { padding: var(--space-4); border-top: 1px solid var(--color-border); background: transparent; }

/* --------------------------------------------------------------------------
   8. Buttons (Bootstrap overrides mapped to tokens)
   -------------------------------------------------------------------------- */
.btn { border-radius: var(--radius-md); font-weight: 600; }

/*
 * Every solid button took its label colour from Bootstrap (#fff) or, for
 * warning/info, from a `color: #fff` written here -- while the BACKGROUND came
 * from a token that flips with the theme. In dark mode all five accents are
 * light colours, so white-on-accent measured 2.99:1 (primary) and 2.06:1
 * (warning); in light mode success/warning/info measured 3.54 / 3.65 / 4.03.
 * The ink is now a token too (--color-on-*, declared per theme in §1/§2), so
 * the pair is chosen together and both themes clear AA.
 */
.btn-primary { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
.btn-primary:hover, .btn-primary:focus { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: var(--color-on-primary); }
.btn-primary:active { background: var(--color-primary-active); border-color: var(--color-primary-active); color: var(--color-on-primary); }
.btn-success { background: var(--color-success); border-color: var(--color-success); color: var(--color-on-success); }
.btn-danger { background: var(--color-danger); border-color: var(--color-danger); color: var(--color-on-danger); }
.btn-warning { background: var(--color-warning); border-color: var(--color-warning); color: var(--color-on-warning); }
.btn-info { background: var(--color-info); border-color: var(--color-info); color: var(--color-on-info); }

/*
 * The outline variants were left entirely to Bootstrap, whose per-variant
 * literals (#6c757d secondary, #dc3545 danger) are not in this palette at all:
 * measured 3.45:1 for "Add photo/video" on the teacher posts page and 3.58:1
 * for the wizard's remove-row button, both in dark mode.
 */
/* Bootstrap keeps a SECOND copy of each variant's literal in
   --bs-btn-disabled-color, which `.btn.disabled` (0,2,0) applies over the
   plain `color` below. Without this the wizard's remove-row button -- disabled
   while only one row exists -- stayed Bootstrap's #dc3545 rather than this
   palette's red. Contrast is not the point (a disabled control is exempt from
   1.4.3); palette consistency is. */
.btn-outline-primary { --bs-btn-disabled-color: var(--color-primary); }
.btn-outline-secondary { --bs-btn-disabled-color: var(--color-text-secondary); }
.btn-outline-danger { --bs-btn-disabled-color: var(--color-danger); }
.btn-outline-success { --bs-btn-disabled-color: var(--color-success-subtle-text); }
.btn-outline-warning { --bs-btn-disabled-color: var(--color-warning-subtle-text); }
.btn-outline-info { --bs-btn-disabled-color: var(--color-info-subtle-text); }

.btn-outline-primary { color: var(--color-primary); border-color: var(--color-primary); }
.btn-outline-primary:hover, .btn-outline-primary:focus { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-on-primary); }
.btn-outline-secondary { color: var(--color-text-secondary); border-color: var(--color-border-strong); }
.btn-outline-secondary:hover, .btn-outline-secondary:focus { background: var(--color-surface-hover); border-color: var(--color-border-strong); color: var(--color-text); }
.btn-outline-danger { color: var(--color-danger); border-color: var(--color-danger); }
.btn-outline-danger:hover, .btn-outline-danger:focus { background: var(--color-danger); border-color: var(--color-danger); color: var(--color-on-danger); }
/* success/warning/info are mid-tone accents: as OUTLINE text on a light
   surface they measure 3.54 / 3.65 / 4.03. The `-subtle-text` token of the
   same family is the darker/lighter member already declared for both themes
   (6.58 / 7.11 / 7.23 light, 10:1+ dark); the border stays the accent. */
.btn-outline-success { color: var(--color-success-subtle-text); border-color: var(--color-success); }
.btn-outline-success:hover, .btn-outline-success:focus { background: var(--color-success); border-color: var(--color-success); color: var(--color-on-success); }
.btn-outline-warning { color: var(--color-warning-subtle-text); border-color: var(--color-warning); }
.btn-outline-warning:hover, .btn-outline-warning:focus { background: var(--color-warning); border-color: var(--color-warning); color: var(--color-on-warning); }
.btn-outline-info { color: var(--color-info-subtle-text); border-color: var(--color-info); }
.btn-outline-info:hover, .btn-outline-info:focus { background: var(--color-info); border-color: var(--color-info); color: var(--color-on-info); }
.btn-link { color: var(--color-primary); }

.btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn.is-loading::after {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.5);
    border-top-color: #fff;
    animation: spin 0.7s linear infinite;
}
.btn-outline-primary.is-loading::after, .btn-link.is-loading::after {
    border-color: rgba(var(--color-primary-rgb), 0.35);
    border-top-color: var(--color-primary);
}

/* --------------------------------------------------------------------------
   9. Badges
   -------------------------------------------------------------------------- */
.badge-soft-primary { background: var(--color-primary-subtle); color: var(--color-primary-subtle-text); }
.badge-soft-success { background: var(--color-success-subtle); color: var(--color-success-subtle-text); }
.badge-soft-warning { background: var(--color-warning-subtle); color: var(--color-warning-subtle-text); }
.badge-soft-danger { background: var(--color-danger-subtle); color: var(--color-danger-subtle-text); }
.badge-soft-info { background: var(--color-info-subtle); color: var(--color-info-subtle-text); }
.badge-soft-primary, .badge-soft-success, .badge-soft-warning, .badge-soft-danger, .badge-soft-info {
    display: inline-flex;
    align-items: center;
    padding: 0.3em 0.65em;
    border-radius: var(--radius-pill);
    font-size: var(--font-size-xs);
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   10. Forms
   -------------------------------------------------------------------------- */
.form-label { font-weight: 600; color: var(--color-text-secondary); margin-block-end: var(--space-1); font-size: var(--font-size-sm); }
.form-control, .form-select {
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    color: var(--color-text);
    border-radius: var(--radius-md);
}
.form-control:focus, .form-select:focus {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-primary);
    box-shadow: 0 0 0 0.2rem rgba(var(--color-primary-rgb), 0.2);
}
.form-control::placeholder { color: var(--color-text-muted); }
.form-control:disabled, .form-select:disabled { background: var(--color-surface-2); color: var(--color-text-muted); }

.is-invalid, .form-control.is-invalid, .form-select.is-invalid { border-color: var(--color-danger) !important; }
.invalid-feedback { color: var(--color-danger); font-size: var(--font-size-xs); }
.form-text { color: var(--color-text-muted); font-size: var(--font-size-xs); }

.input-group-text { background: var(--color-surface-2); border-color: var(--color-border-strong); color: var(--color-text-secondary); }

.password-toggle-btn {
    background: transparent;
    border: 1px solid var(--color-border-strong);
    border-inline-start: 0;
    color: var(--color-text-muted);
}
.password-toggle-btn:hover { color: var(--color-text); }

/* --------------------------------------------------------------------------
   11. Tables + DataTables theme fixes (light & dark)
   -------------------------------------------------------------------------- */
.table { color: var(--color-text); }
.table > :not(caption) > * > * {
    background-color: var(--color-surface);
    color: var(--color-text);
    border-bottom-color: var(--color-border);
}
.table-hover > tbody > tr:hover > * { background-color: var(--color-surface-hover); }
.table thead th { background: var(--color-surface-2); color: var(--color-text-secondary); font-weight: 600; border-bottom: 2px solid var(--color-border); }

.dataTables_wrapper { color: var(--color-text); }
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate { color: var(--color-text-secondary); }
.dataTables_wrapper .dataTables_filter input,
.dataTables_wrapper .dataTables_length select {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    padding: 4px 8px;
}
table.dataTable {
    background: var(--color-surface);
    border-color: var(--color-border) !important;
}
table.dataTable thead th, table.dataTable thead td {
    background: var(--color-surface-2);
    color: var(--color-text-secondary);
    border-bottom: 2px solid var(--color-border);
}
table.dataTable tbody th, table.dataTable tbody td {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-border);
}
table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd { background-color: var(--color-surface-2); }
table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover { background-color: var(--color-surface-hover); }
.dataTables_wrapper .dataTables_paginate .paginate_button {
    color: var(--color-text-secondary) !important;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
    background: var(--color-primary) !important;
    border-color: var(--color-primary) !important;
    color: #fff !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
    background: var(--color-surface-hover) !important;
    color: var(--color-text) !important;
    border-color: var(--color-border) !important;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled { color: var(--color-text-muted) !important; }

/*
 * DataTables 2.x (vendored 2.3.8) writes its own
 * `table.table.dataTable > :not(caption) > * > * { background-color:
 * var(--bs-table-bg) }` and its stylesheet is push()ed per view, so it loads
 * AFTER this file and outranks the `.table thead th` tint above. The bridge in
 * §2b already makes --bs-table-bg follow the theme, so the cells are correct;
 * this only restores the header's slightly-raised surface. !important for the
 * load-order reason only -- the value is still a token.
 */
table.table.dataTable > thead > tr > th,
table.table.dataTable > thead > tr > td { background-color: var(--color-surface-2) !important; }

/* --------------------------------------------------------------------------
   11b. Alerts
   --------------------------------------------------------------------------
   Bootstrap 5.3 builds every alert from --bs-alert-color / -bg / -border-color,
   which it resolves from its own --bs-{colour}-bg-subtle and
   --bs-{colour}-text-emphasis. This design system never re-declares those, so
   every alert stayed a LIGHT panel with near-black text on a dark page: the
   stock preview on the adjustment/gift/damage forms, the gift's zero-value
   notice, and -- the one that actually costs money -- the negative-stock
   warning, which an admin could not read at all before saving.

   This system already owns the exact pair Bootstrap is missing
   (--color-{x}-subtle / --color-{x}-subtle-text, declared for BOTH themes), so
   the whole fix is re-pointing the four custom properties per variant. Nothing
   is hardcoded and dark mode is inherited, as everywhere else here.
   -------------------------------------------------------------------------- */
.alert-primary {
    --bs-alert-color: var(--color-primary-subtle-text);
    --bs-alert-bg: var(--color-primary-subtle);
    --bs-alert-border-color: rgba(var(--color-primary-rgb), 0.35);
    --bs-alert-link-color: var(--color-primary-subtle-text);
}
.alert-success {
    --bs-alert-color: var(--color-success-subtle-text);
    --bs-alert-bg: var(--color-success-subtle);
    --bs-alert-border-color: rgba(var(--color-success-rgb), 0.35);
    --bs-alert-link-color: var(--color-success-subtle-text);
}
.alert-warning {
    --bs-alert-color: var(--color-warning-subtle-text);
    --bs-alert-bg: var(--color-warning-subtle);
    --bs-alert-border-color: rgba(var(--color-warning-rgb), 0.35);
    --bs-alert-link-color: var(--color-warning-subtle-text);
}
.alert-danger {
    --bs-alert-color: var(--color-danger-subtle-text);
    --bs-alert-bg: var(--color-danger-subtle);
    --bs-alert-border-color: rgba(var(--color-danger-rgb), 0.35);
    --bs-alert-link-color: var(--color-danger-subtle-text);
}
.alert-info {
    --bs-alert-color: var(--color-info-subtle-text);
    --bs-alert-bg: var(--color-info-subtle);
    --bs-alert-border-color: rgba(var(--color-info-rgb), 0.35);
    --bs-alert-link-color: var(--color-info-subtle-text);
}
/* No -subtle pair exists for these three, which are neutral by definition, so
   they take the neutral surfaces instead of inventing a colour. */
.alert-secondary,
.alert-dark {
    --bs-alert-color: var(--color-text);
    --bs-alert-bg: var(--color-surface-2);
    --bs-alert-border-color: var(--color-border);
    --bs-alert-link-color: var(--color-text);
}
.alert-light {
    --bs-alert-color: var(--color-text);
    --bs-alert-bg: var(--color-surface);
    --bs-alert-border-color: var(--color-border);
    --bs-alert-link-color: var(--color-text);
}

/* An alert is a tinted panel, so muted/secondary text inside it must key off
   the alert's own colour rather than the page's -- otherwise §10's .text-muted
   token lands on a tint it was never measured against. No opacity here on
   purpose: dimming to 0.85 measured 4.37:1 on the lightest panel, i.e. under
   AA, and a smaller font already carries the "secondary" reading. */
.alert .text-muted,
.alert small {
    color: inherit !important;
}

/* --------------------------------------------------------------------------
   12. Modals & tabs
   -------------------------------------------------------------------------- */
.modal-content { background: var(--color-surface); color: var(--color-text); border-radius: var(--radius-lg); border: 1px solid var(--color-border); }
.modal-header, .modal-footer { border-color: var(--color-border); }
.modal-backdrop.show { opacity: 0.6; background: var(--color-overlay); }
.btn-close { filter: none; }
[data-theme="dark"] .btn-close { filter: invert(1) grayscale(100%) brightness(200%); }

/*
 * Bootstrap dropdowns were the one surface the theme never reached: the topbar
 * language menu, user menu and notification panel stayed a white sheet on a
 * dark page. Left alone it also traps the .text-muted fix above -- the "no
 * notifications" line sits inside this panel, and a muted token on white
 * measures 3.15:1. Tokenising the panel fixes both at once.
 */
.dropdown-menu {
    background: var(--color-surface);
    border-color: var(--color-border);
    color: var(--color-text);
    box-shadow: var(--shadow-md);
}
.dropdown-item { color: var(--color-text); }
.dropdown-item:hover,
.dropdown-item:focus { background: var(--color-surface-hover); color: var(--color-text); }
.dropdown-item.active,
.dropdown-item:active { background: var(--color-primary); color: var(--color-text-inverse); }
.dropdown-item.disabled, .dropdown-item:disabled { color: var(--color-text-muted); }
.dropdown-header { color: var(--color-text-secondary); }
.dropdown-divider { border-top-color: var(--color-border); }

.nav-tabs { border-bottom-color: var(--color-border); }
.nav-tabs .nav-link { color: var(--color-text-secondary); border: 1px solid transparent; }
.nav-tabs .nav-link.active { color: var(--color-primary); background: var(--color-surface); border-color: var(--color-border) var(--color-border) var(--color-surface); }
.nav-tabs .nav-link:hover { border-color: transparent; color: var(--color-text); }

/* --------------------------------------------------------------------------
   13. Empty states, spinners, skeletons
   -------------------------------------------------------------------------- */
.empty-state {
    text-align: center;
    padding: var(--space-7) var(--space-4);
    color: var(--color-text-muted);
}
.empty-state-icon { width: 56px; height: 56px; margin-inline: auto; margin-block-end: var(--space-3); color: var(--color-text-muted); }
.empty-state-title { font-size: var(--font-size-md); font-weight: 600; color: var(--color-text-secondary); margin-block-end: var(--space-1); }

@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 3px solid var(--color-border);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

@keyframes skeleton-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.skeleton {
    background: var(--color-surface-2);
    border-radius: var(--radius-sm);
    animation: skeleton-pulse 1.4s ease-in-out infinite;
}
.skeleton-text { height: 0.9em; margin-block-end: var(--space-2); }
.skeleton-line { height: 14px; border-radius: var(--radius-sm); }

/* --------------------------------------------------------------------------
   14. Toasts (SweetAlert2) — flip side in RTL, sit below the topbar
   -------------------------------------------------------------------------- */
.swal2-container.swal2-top-end,
.swal2-container.swal2-top-start { padding-block-start: calc(var(--topbar-height) + var(--space-3)); }
.swal2-popup { font-family: var(--font-body); border-radius: var(--radius-lg) !important; }
.swal2-toast { background: var(--color-surface) !important; color: var(--color-text) !important; box-shadow: var(--shadow-lg) !important; }
.swal2-toast .swal2-title { color: var(--color-text) !important; }
.swal2-container { z-index: var(--z-toast); }

/* --------------------------------------------------------------------------
   15. Public layout (quiz/verify/landing)
   -------------------------------------------------------------------------- */
.layout-public { background: var(--color-bg); min-height: 100vh; }
.public-shell { min-height: 100vh; display: flex; flex-direction: column; }
.public-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
}
.public-brand { font-weight: 700; font-size: var(--font-size-md); color: var(--color-text); flex: 1 1 auto; }
.public-brand:hover { color: var(--color-text); text-decoration: none; }
.public-lang-switch { display: flex; gap: var(--space-2); }
.public-lang-link { padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); color: var(--color-text-muted); font-size: var(--font-size-sm); }
.public-lang-link.active { background: var(--color-primary-subtle); color: var(--color-primary-subtle-text); font-weight: 600; }
.public-lang-link:hover { text-decoration: none; color: var(--color-text); }
.public-content { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; padding: var(--space-5); }
.public-card {
    width: 100%;
    max-width: 560px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md);
    padding: var(--space-6);
}

/* --------------------------------------------------------------------------
   16. Error pages
   -------------------------------------------------------------------------- */
.error-page { text-align: center; padding: var(--space-4); }
.error-icon { width: 88px; height: 88px; margin-inline: auto; margin-block-end: var(--space-4); color: var(--color-primary); }
.error-title { font-size: var(--font-size-lg); font-weight: 700; margin-block-end: var(--space-4); }
.error-action { display: inline-block; }

/* --------------------------------------------------------------------------
   17. Exam layout — big, bright, cheerful, playful
   -------------------------------------------------------------------------- */
.exam-theme {
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh;
    background: linear-gradient(160deg, #6f7ff5 0%, #8f6ff0 45%, #f0709e 100%);
    color: #241c47;
    overflow-x: hidden;
}
[data-theme="dark"] .exam-theme {
    background: linear-gradient(160deg, #241c47 0%, #382a63 45%, #5a2f52 100%);
    color: #f4f0ff;
}
.exam-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    padding: var(--space-4);
}
.exam-progress-wrap { display: flex; align-items: center; gap: var(--space-3); margin-block-end: var(--space-4); }
.exam-progress-track {
    flex: 1 1 auto;
    height: 20px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.35);
    overflow: hidden;
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15);
}
.exam-progress-fill {
    height: 100%;
    border-radius: var(--radius-pill);
    background: linear-gradient(90deg, #ffd166, #06d6a0);
    transition: width 0.4s ease;
}
.exam-progress-label { font-weight: 800; font-size: var(--font-size-lg); color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); }
.exam-content {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
}
.exam-card {
    width: 100%;
    max-width: 640px;
    background: #fffdf9;
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
    padding: var(--space-7) var(--space-6);
    text-align: center;
}
[data-theme="dark"] .exam-card { background: #241c47; color: #f4f0ff; }
.exam-number { font-size: var(--font-size-3xl); font-weight: 800; letter-spacing: 0.02em; }
.exam-mascot { font-size: 4rem; line-height: 1; margin-block-end: var(--space-3); }
.exam-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 96px;
    min-height: 72px;
    font-size: var(--font-size-xl);
    font-weight: 800;
    border-radius: var(--radius-lg);
    border: 0;
    background: #ffffff;
    color: #241c47;
    box-shadow: var(--shadow-md);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.exam-btn:active { transform: scale(0.95); }
.exam-btn-primary { background: linear-gradient(135deg, #06d6a0, #1e88b0); color: #fff; }
.exam-keypad { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3); max-width: 320px; margin-inline: auto; }
.exam-celebrate { font-size: var(--font-size-2xl); font-weight: 800; }

/* --------------------------------------------------------------------------
   18. Teacher layout — friendlier, larger touch targets, more whitespace
   -------------------------------------------------------------------------- */
.layout-teacher .app-content { padding: var(--space-6); }
.layout-teacher .btn { min-height: 44px; padding-inline: var(--space-4); }
.layout-teacher .page-title { font-size: var(--font-size-2xl); }
.layout-teacher .card-body { padding: var(--space-5); }

/*
 * THE TEACHER SIDEBAR IS NOT A DIFFERENT SIDEBAR.
 *
 * partials/sidebar.php and layouts/teacher.php are byte-for-byte the same
 * shell as the admin pair -- same .app-shell, same #appSidebar, same
 * .nav-link markup, same collapse button. The reason the teacher's looked
 * untouched by the compact pass is one declaration that used to live here:
 *
 *   .layout-teacher .nav-link { padding: var(--space-4) var(--space-3);
 *                               font-size: var(--font-size-base); }
 *
 * It is (0,2,0), so it outranked §4's compact `.nav-link` (0,1,0) at EVERY
 * width -- including desktop, where §4's whole point is that the rows must be
 * tight. Measured at 1440x900, Kurdish: teacher rows 62px tall vs the admin's
 * 39px, i.e. the compact pass reached the teacher sidebar and was then
 * overridden two hundred lines later.
 *
 * The rule itself is not wrong, only unscoped. §4 already states the contract:
 * compact from lg UP, comfortable below, because below lg the sidebar is a
 * drawer people tap. Applying the same media scope here makes the teacher's
 * desktop sidebar identical to the admin's and keeps the large touch targets
 * exactly where they were meant to be.
 *
 * NOTHING in partials/sidebar.php or layouts/teacher.php needs to change.
 */
@media (max-width: 991.98px) {
    .layout-teacher .nav-link,
    .layout-teacher .nav-group-toggle { padding: var(--space-4) var(--space-3); font-size: var(--font-size-base); }
}

/*
 * Dashboard action tiles. These carried NO stylesheet rule at all -- the icon
 * rendered at body size and the label was whatever the card happened to
 * inherit, which in dark mode was Bootstrap's #212529 (see §2b) at 1.05:1.
 * Now that each tile is an <a>, it also needs its own text colour: a bare
 * anchor would take --color-primary and read as a link, not a tile.
 */
.action-tile {
    color: var(--color-text);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
}
.action-tile:hover,
.action-tile:focus-visible {
    color: var(--color-text);
    text-decoration: none;
    border-color: var(--color-primary);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}
.action-tile:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.action-tile__icon { font-size: var(--font-size-2xl); line-height: 1.2; margin-block-end: var(--space-2); }
.action-tile__label { color: var(--color-text); font-weight: 600; font-size: var(--font-size-sm); }

/* --------------------------------------------------------------------------
   19. Responsive — mobile-first adjustments
   -------------------------------------------------------------------------- */
@media (max-width: 991.98px) {
    .sidebar {
        inset-inline-start: calc(-1 * var(--sidebar-width));
        box-shadow: var(--shadow-lg);
    }
    body.sidebar-open .sidebar { inset-inline-start: 0; }
    body.sidebar-open .sidebar-backdrop { opacity: 1; visibility: visible; }
    .app-main { margin-inline-start: 0 !important; }
    #appShell.sidebar-collapsed .app-main { margin-inline-start: 0 !important; }
    .sidebar-collapse-footer { display: none !important; }
    .app-content { padding: var(--space-4); }
    .public-card { padding: var(--space-5); }
    .exam-card { padding: var(--space-5) var(--space-4); }
}

@media (min-width: 992px) {
    .sidebar-close-btn { display: none !important; }
}

/* --------------------------------------------------------------------------
   20. Print styles
   -------------------------------------------------------------------------- */
@media print {
    .sidebar, .sidebar-backdrop, .topbar, .page-actions, .no-print { display: none !important; }
    .app-main { margin-inline-start: 0 !important; }
    body { background: #fff; color: #000; }
    .card { box-shadow: none; border-color: #ccc; }
}

/* --------------------------------------------------------------------------
   21. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* --------------------------------------------------------------------------
   22. Compact single-screen forms
   --------------------------------------------------------------------------
   Opt in with class="form-compact" on the <form> (or any wrapper). It tightens
   SPACING only -- never type size, never control height -- so a short admin
   form (purchase entry, stock adjustment, gift, damage) fits a ~900px laptop
   viewport without scrolling.

   Exactly the same rule as the sidebar metrics in §4: the tightening applies
   from the lg breakpoint UP, because below it these are surfaces people tap and
   the comfortable defaults have to come back. Nothing here changes any
   font-size or any input/button height, at any width.
   -------------------------------------------------------------------------- */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    /*
     * The chat launcher is position:fixed in the inline-end bottom corner.
     * Reserving its lane here means a Save/Cancel row can never end up
     * underneath it -- in either direction, since the buttons sit at the
     * inline-START of this row and the padding is taken off the inline-END.
     * It costs no vertical space, so it does not fight the compaction above.
     */
    padding-inline-end: calc(var(--chat-launcher-size) + var(--space-5));
}

@media (min-width: 992px) {
    .form-compact .card-header { padding-block: var(--space-2); }
    .form-compact .card-body   { padding-block: var(--space-3); }
    .form-compact .form-label  { margin-block-end: 2px; }
    /*
     * A form whose body IS a table -- the student-entry wizard -- has no
     * .form-label rows to tighten; its vertical budget is spent by the cells.
     * Same contract as everything else in this section: spacing only, and only
     * from lg up. Control heights and type sizes are untouched, so the row is
     * still as tappable as it ever was.
     */
    .form-compact .table > :not(caption) > * > * { padding-block: var(--space-1); }
    .form-compact .table > thead > tr > th { padding-block: var(--space-1); }
}

/* --------------------------------------------------------------------------
   23. Tom Select — themed, and free to escape a scrolling ancestor
   --------------------------------------------------------------------------
   vendor/tomselect/tom-select.bootstrap5.min.css is pushed per view, so it
   loads AFTER this file, and it hardcodes #343a40 text on var(--bs-body-bg) --
   i.e. a white sheet with near-black text whatever the theme, which made every
   searchable picker unreadable in dark mode. These declarations carry
   !important for that load-order reason only; the same reason the DataTables
   block in §11 needs them. Every value is still a token, so dark mode is
   inherited rather than re-specified.

   The z-index and the position:absolute below are what let a picker inside an
   .table-responsive (the purchase line-item table) render its list into <body>
   with `dropdownParent: 'body'` instead of being cut off by that container's
   overflow. Tom Select sets top/left/width inline from the control's rect, so
   the list stays aligned with its input in RTL as well as LTR.
   -------------------------------------------------------------------------- */
.ts-control,
.ts-control input,
.ts-wrapper .ts-control,
.ts-wrapper .ts-control input {
    color: var(--color-text) !important;
}

.ts-wrapper.form-control .ts-control,
.ts-wrapper.form-select .ts-control {
    border-color: var(--color-border-strong) !important;
}

.ts-wrapper.focus .ts-control {
    border-color: var(--color-primary) !important;
    box-shadow: 0 0 0 0.2rem rgba(var(--color-primary-rgb), 0.2) !important;
}

.ts-control input::placeholder {
    color: var(--color-text-muted) !important;
    opacity: 1;
}

.ts-dropdown,
.ts-dropdown.form-control,
.ts-dropdown.form-select {
    background: var(--color-surface) !important;
    border: 1px solid var(--color-border) !important;
    border-radius: var(--radius-md) !important;
    box-shadow: var(--shadow-lg) !important;
    color: var(--color-text) !important;
    text-align: start;
    z-index: var(--z-dropdown) !important;
}

.ts-dropdown .option,
.ts-dropdown .optgroup-header,
.ts-dropdown .no-results,
.ts-dropdown .create {
    padding-block: var(--space-2);
    padding-inline: var(--space-3);
    color: var(--color-text) !important;
}

.ts-dropdown .optgroup-header {
    background: var(--color-surface-2) !important;
    color: var(--color-text-secondary) !important;
}

.ts-dropdown .no-results,
.ts-dropdown .create {
    color: var(--color-text-muted) !important;
}

.ts-dropdown .active,
.ts-dropdown .active.create {
    background-color: var(--color-primary-subtle) !important;
    color: var(--color-primary-subtle-text) !important;
}

.ts-dropdown [data-selectable] .highlight {
    background: rgba(var(--color-primary-rgb), 0.28);
    border-radius: 2px;
    color: inherit;
}

.ts-dropdown-content { max-height: 260px; }

/*
 * Inside a table cell the vendor's `min-width: 7rem` on the inner text input
 * only takes effect once the control goes active, so clicking the picker made
 * the whole column jump wider (measured 240px -> 395px) a frame or two later.
 * The input still flexes to fill whatever the column gives it; it just stops
 * demanding more the moment it is focused.
 */
td .ts-control > input,
th .ts-control > input { min-width: 0 !important; }

/* --------------------------------------------------------------------------
   24. Dashboard Stat Tiles — compact, responsive metrics
   -------------------------------------------------------------------------- */
.stat-tile {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
    position: relative;
    overflow: hidden;
}

.stat-tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm);
    border-color: var(--color-border-strong);
}

.stat-tile .card-body {
    padding: var(--space-2) var(--space-3);
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 64px;
}

.stat-tile__value {
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1.2;
    color: var(--color-text);
    margin-bottom: 2px;
}

.stat-tile__label {
    font-size: 0.76rem;
    color: var(--color-text-secondary);
    line-height: 1.25;
    white-space: normal;
}

/* Accent indicator line along inline-start border */
.stat-tile::before {
    content: '';
    position: absolute;
    inset-inline-start: 0;
    inset-block: 0;
    width: 3px;
    background: var(--color-border);
    transition: background-color var(--transition-fast);
}

.stat-tile--primary::before { background: var(--color-primary); }
.stat-tile--info::before    { background: var(--color-info); }
.stat-tile--success::before { background: var(--color-success); }
.stat-tile--warning::before { background: var(--color-warning); }
.stat-tile--danger::before  { background: var(--color-danger); }


/* Delete control on a chat message. Deliberately quiet until hovered or
   focused: it destroys the message permanently, and a delete that catches the
   eye is a delete that gets pressed by accident. Always reachable by keyboard
   and always at full opacity on touch, where there is no hover to reveal it. */
.chat-delete {
    border: 0;
    background: none;
    color: var(--color-text-muted);
    font-size: 1rem;
    line-height: 1;
    padding: 0 .25rem;
    cursor: pointer;
    opacity: .35;
    transition: opacity .12s ease, color .12s ease;
}

.chat-msg:hover .chat-delete,
.chat-delete:focus-visible {
    opacity: 1;
    color: var(--color-danger, #ef476f);
}

@media (hover: none) {
    .chat-delete { opacity: .7; }
}
