/* ============================================================
   SchoolOS SCHOOL MANAGEMENT SYSTEM - FULLY RESPONSIVE STYLES v3
   Auto-adjusts across ALL user roles: admin, staff, teacher,
   student, parent, super_admin.
   ============================================================ */

/* CSS Variables */
:root {
    --primary: #0d9488;
    --primary-dark: #0f766e;
    --primary-light: #ccfbf1;
    --secondary: #f97316;
    --success: #10b981;
    --danger: #ef4444;
    --warning: #f59e0b;
    --info: #3b82f6;
    --dark: #1e293b;
    --dark-light: #334155;
    --gray: #64748b;
    --gray-light: #94a3b8;
    --light: #f1f5f9;
    --lighter: #f8fafc;
    --white: #ffffff;
    /* Foreground colours for text sitting ON a saturated accent fill.
       --white is a FLIPPING token: [data-theme="dark"] remaps it to #1e293b so
       that panels painted var(--dark) stay readable. The accent colours
       (--primary/--success/--danger/--info) do NOT flip, so any rule that
       paired one of them with color: var(--white) turned into dark-on-saturated
       in dark mode - "Sign In" measured 2.5:1. These three do not flip, and
       --on-primary/--on-secondary are overridden per school in
       includes/header.php (and per platform in index.php) via contrastTextOn(),
       so a pale brand colour gets dark text instead of unreadable white. */
    --on-accent: #ffffff;
    --on-primary: #ffffff;
    --on-secondary: #ffffff;
    /* Per-colour label choices, each the higher-contrast of #ffffff / #1e293b:
       emerald and amber are too light to carry white text (2.54:1 and 2.16:1),
       so their buttons take a dark label instead. */
    --on-success: #1e293b;
    --on-warning: #422006;
    /* Red #ef4444 and blue #3b82f6 are a dead heat - white is 3.76:1, dark is
       3.89:1, and neither clears AA's 4.5 for a 14px/600 label. Rather than pick
       the ugly winner, the BUTTON fill steps one shade darker (these are the
       app's own existing hover shades), which lets white pass at 4.83 and 5.17.
       The --danger / --info tokens themselves are untouched, so badges, alert
       borders and icon colours keep the lighter hue they were designed with. */
    --danger-btn: #dc2626;
    --info-btn: #2563eb;
    /* .btn-primary's hover fill. Separate from --primary-dark (which is also
       used for headings, links and borders) because the safe hover DIRECTION
       depends on the label: darken under a white label, lighten under a dark
       one. Overridden per school by brandHoverOn() at each emission site; this
       default is the lightened teal, because white on #0d9488 is only 3.69:1 so
       the default label is dark. */
    --primary-hover: #42aca2;
    --border: #e2e8f0;
    --shadow: 0 1px 3px rgba(0,0,0,0.1);
    --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1);
    --radius: 8px;
    --radius-lg: 12px;
    --sidebar-width: 260px;
    --topbar-height: 60px;
    --transition: all 0.3s ease;
}

/* Reset & Base */
*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    font-size: 16px;
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: transparent;
}

body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: var(--lighter);
    color: var(--dark);
    line-height: 1.6;
    min-height: 100vh;
    min-height: 100dvh;
    /* Was overflow-x: hidden - that blocks a horizontal scrollbar from
       ever appearing at ANY screen size, not just on phones, so any page
       with content genuinely too wide to reflow (a broadsheet with many
       subject columns, a wide report, etc.) just got silently clipped
       with no way to reach the rest of it. `auto` only shows a scrollbar
       when something actually overflows, so normal pages that already
       fit are unaffected. */
    overflow-x: auto;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a {
    text-decoration: none;
    color: var(--primary);
    transition: var(--transition);
}

a:hover {
    color: var(--primary-dark);
}

img {
    max-width: 100%;
    height: auto;
}

/* ============================================================
   LOGIN PAGE
   ============================================================ */
.login-page {
    background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.login-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    max-width: 1000px;
    width: 100%;
    background: var(--white);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-lg);
}

.login-brand {
    background: linear-gradient(135deg, var(--dark) 0%, var(--dark-light) 100%);
    color: var(--white);
    padding: 60px 40px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.logo-icon {
    width: 60px;
    height: 60px;
    background: var(--primary);
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    font-weight: 700;
    margin-bottom: 20px;
}

.login-brand h2 {
    font-size: 28px;
    margin-bottom: 15px;
}

.login-brand p {
    color: var(--gray-light);
    margin-bottom: 30px;
    line-height: 1.7;
}

.features {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.feature {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 14px;
}

.feature i {
    color: var(--primary);
    width: 20px;
}

.login-form-wrap {
    padding: 60px 40px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.login-form-wrap h3 {
    font-size: 24px;
    margin-bottom: 8px;
}

.subtitle {
    color: var(--gray);
    margin-bottom: 30px;
    font-size: 14px;
}

.login-form .form-group {
    margin-bottom: 20px;
}

.login-form .form-label {
    display: block;
    margin-bottom: 6px;
    font-weight: 500;
    font-size: 14px;
}

.login-form .form-control {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid var(--border);
    border-radius: var(--radius);
    font-size: 15px;
    transition: var(--transition);
    font-family: inherit;
    min-height: 44px;
}

.login-form .form-control:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}

.login-footer {
    margin-top: 25px;
    padding-top: 20px;
    border-top: 1px solid var(--border);
    font-size: 13px;
    color: var(--gray);
    text-align: center;
}

/* ============================================================
   SIDEBAR & LAYOUT
   ============================================================ */
.sidebar {
    position: fixed;
    left: 0;
    top: 0;
    width: var(--sidebar-width);
    height: 100vh;
    height: 100dvh;
    background: var(--dark);
    color: var(--white);
    z-index: 1000;
    overflow-y: auto;
    overflow-x: hidden;
    transition: var(--transition);
    -webkit-overflow-scrolling: touch;
    display: flex;
    flex-direction: column;
    scrollbar-width: thin;
    scrollbar-color: var(--dark-light) transparent;
}

.sidebar::-webkit-scrollbar { width: 4px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb { background: var(--dark-light); border-radius: 4px; }

body.sidebar-hidden .sidebar {
    transform: translateX(-100%);
}

.sidebar-brand {
    padding: 15px 10px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border-bottom: 1px solid var(--dark-light);
    min-height: auto;
    text-align: center;
    flex-shrink: 0;
}

.sidebar-brand .logo-icon {
    width: 50px;
    height: 50px;
    font-size: 22px;
    margin-bottom: 0;
    flex-shrink: 0;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
}

.sidebar-brand img {
    height: 50px;
    width: 50px;
    object-fit: contain;
    border-radius: 12px;
    background: white;
    padding: 3px;
    flex-shrink: 0;
}

.logo-text {
    font-size: 13px;
    font-weight: 700;
    white-space: normal;
    text-align: center;
    line-height: 1.3;
    word-break: break-word;
    max-width: 100%;
    color: var(--white);
}

.sidebar-user {
    padding: 20px;
    display: flex;
    align-items: center;
    gap: 12px;
    border-bottom: 1px solid var(--dark-light);
    flex-shrink: 0;
}

.user-avatar {
    width: 40px;
    height: 40px;
    background: var(--primary);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 16px;
    flex-shrink: 0;
}

.user-name {
    font-weight: 600;
    font-size: 14px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-role {
    font-size: 12px;
    color: var(--gray-light);
    text-transform: capitalize;
}

.sidebar-nav {
    padding: 15px 0;
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}

.nav-section {
    padding: 10px 20px 5px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--gray-light);
    font-weight: 600;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 20px;
    color: var(--gray-light);
    font-size: 14px;
    transition: var(--transition);
    border-left: 3px solid transparent;
    white-space: nowrap;
    min-height: 44px;
}

.nav-item:hover,
.nav-item.active {
    background: var(--dark-light);
    color: var(--white);
    border-left-color: var(--primary);
}

.nav-item i {
    width: 20px;
    text-align: center;
    flex-shrink: 0;
    font-size: 16px;
}

.nav-logout {
    color: var(--danger);
}

.nav-logout:hover {
    background: rgba(239, 68, 68, 0.1);
    color: var(--danger);
}

/* Topbar */
.topbar {
    position: fixed;
    top: 0;
    left: var(--sidebar-width);
    right: 0;
    height: var(--topbar-height);
    background: var(--white);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 25px;
    z-index: 900;
    transition: var(--transition);
    gap: 10px;
}

body.sidebar-hidden .topbar {
    left: 0;
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 15px;
}

.sidebar-toggle-btn {
    background: none;
    border: none;
    font-size: 20px;
    color: var(--dark);
    cursor: pointer;
    padding: 8px;
    border-radius: var(--radius);
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    transition: var(--transition);
    min-height: 44px;
    min-width: 44px;
}

.sidebar-toggle-btn:hover {
    background: var(--light);
    color: var(--primary);
}

.topbar-right {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    justify-content: flex-end;
    min-width: 0;
}

.school-name {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-weight: 700;
    color: var(--primary-dark);
    font-size: 16px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 440px;
    flex-shrink: 1;
    min-width: 0;
}

.school-name i {
    color: var(--primary);
    font-size: 14px;
    flex-shrink: 0;
}

.session-info {
    font-size: 13px;
    color: var(--gray);
    background: var(--light);
    padding: 4px 12px;
    border-radius: 20px;
    white-space: nowrap;
}

/* Colorful topbar info chips: clock, date, session, term */
/* No pill fill and no rounded shape - these are just coloured labels now. The
   tinted capsules they used to be read as four competing badges across the top of
   every page; without the fill the topbar is quieter and the only colour left is
   the school's own. Padding is kept horizontally so the four still separate. */
.tb-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    padding: 5px 8px;
    background: none;
    border-radius: 0;
    white-space: nowrap;
}

.tb-pill i { font-size: 11px; }

/* Colours come from the per-school tokens header.php emits (--tb-primary-fg /
   --tb-secondary-fg), which are contrast-corrected per theme by
   brandReadableOn(). The hardcoded blue/orange/teal/green that used to live here
   ignored the school's branding entirely. */
.tb-pill-time,
.tb-pill-session { color: var(--tb-primary-fg, var(--primary)); }
.tb-pill-date,
.tb-pill-term    { color: var(--tb-secondary-fg, var(--secondary)); }

/* Main Content */
.main-content {
    margin-left: var(--sidebar-width);
    padding: calc(var(--topbar-height) + 25px) 25px 25px;
    min-height: 100vh;
    min-height: 100dvh;
    transition: var(--transition);
    width: calc(100% - var(--sidebar-width));
    max-width: 100%;
}

body.sidebar-hidden .main-content {
    margin-left: 0;
    width: 100%;
}

/* ============================================================
   COMPONENTS
   ============================================================ */

/* Page Header */
.page-header {
    margin-bottom: 25px;
}

.page-header h1 {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 5px;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    word-break: break-word;
}

.page-header h1 i {
    color: var(--primary);
}

.page-header p {
    color: var(--gray);
    font-size: 14px;
}

/* Cards */
.card {
    background: var(--white);
    border-radius: var(--radius-lg);
    padding: 25px;
    margin-bottom: 20px;
    box-shadow: var(--shadow);
    border: 1px solid var(--border);
    width: 100%;
    overflow: hidden;
}

.card h3 {
    font-size: 18px;
    margin-bottom: 20px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.card h3 i {
    color: var(--primary);
}

/* Stats Grid - Responsive */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
    gap: 20px;
    margin-bottom: 25px;
}

.stat-card {
    background: var(--white);
    border-radius: var(--radius-lg);
    padding: 20px;
    box-shadow: var(--shadow);
    border: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    min-width: 0;
}

.stat-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 4px;
    height: 100%;
}

/* No coloured stripe on any tile.
   These six were stock blue/green/orange/purple/red/teal, so a school's dashboard
   looked like the same stock product whatever its branding - and `stat-cyan` had
   no rule at all, which is why Fees Collected was the ONE tile that looked right
   and every other one looked wrong beside it. The tiles now carry the school's
   colour in the icon alone, the same way the Quick Action cards do. */
.stat-card::before { background: none; }

/* The dashboard tiles (Fees Collected, Students, Classes ...) show a bare icon
   now - no filled square behind it. Six fixed pastel fills meant every school's
   dashboard looked the same regardless of its branding, and the tiles competed
   with the numbers they exist to introduce. The glyph keeps its size so the row
   still lines up; only the plate is gone. */
.stat-icon {
    width: 48px;
    height: 48px;
    border-radius: 0;
    background: none;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    margin-bottom: 15px;
}

/* Alternating the school's two brand colours, so a row of tiles still has some
   variation without reintroducing six unrelated hues. */
.stat-blue .stat-icon,
.stat-teal .stat-icon,
.stat-purple .stat-icon { background: none; color: var(--primary); }
.stat-green .stat-icon,
.stat-orange .stat-icon,
.stat-red .stat-icon { background: none; color: var(--secondary); }

.stat-info h3 {
    font-size: 28px;
    font-weight: 700;
    margin-bottom: 5px;
    word-break: break-word;
}

.stat-info p {
    color: var(--gray);
    font-size: 14px;
}

.stat-link {
    margin-top: 15px;
    font-size: 13px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 5px;
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: var(--radius);
    border: none;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: var(--transition);
    font-family: inherit;
    white-space: nowrap;
    min-height: 44px;
    touch-action: manipulation;
}

.btn-primary {
    background: var(--primary);
    color: var(--on-primary);
}

.btn-primary:hover {
    background: var(--primary-hover);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(13, 148, 136, 0.3);
}

.btn-success {
    background: var(--success);
    color: var(--on-success);
}

.btn-success:hover {
    /* Lightens rather than darkens, because this button carries a DARK label:
       the old #059669 hover dropped it to 3.88:1. #34d399 keeps it at 7.61:1. */
    background: #34d399;
}

.btn-danger {
    background: var(--danger-btn);
    color: var(--on-accent);
}

.btn-danger:hover {
    background: #b91c1c;
}

.btn-warning {
    background: var(--warning);
    color: var(--on-warning);
}

.btn-info {
    background: var(--info-btn);
    color: var(--on-accent);
}

.btn-secondary {
    background: var(--gray-light);
    color: var(--dark);
}

/* Quick Actions */
.quick-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

/* Forms */
.form-group {
    margin-bottom: 20px;
}

.form-label {
    display: block;
    margin-bottom: 6px;
    font-weight: 500;
    font-size: 14px;
    color: var(--dark);
}

.form-control {
    width: 100%;
    padding: 10px 14px;
    border: 2px solid var(--border);
    border-radius: var(--radius);
    font-size: 14px;
    transition: var(--transition);
    font-family: inherit;
    background: var(--white);
    color: var(--dark);
    min-height: 44px;
}

.form-control:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px var(--primary-light);
}

select.form-control {
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    padding-right: 36px;
}

textarea.form-control {
    min-height: 100px;
    resize: vertical;
}

/* Tables */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.data-table th {
    background: var(--light);
    padding: 12px 15px;
    text-align: left;
    font-weight: 600;
    color: var(--dark);
    border-bottom: 2px solid var(--border);
    white-space: nowrap;
}

.data-table td {
    padding: 12px 15px;
    border-bottom: 1px solid var(--border);
    color: var(--dark-light);
}

.data-table tr:hover td {
    background: var(--lighter);
}

/* Action buttons (view/edit/delete icons) in table cells - this is the
   same markup pattern repeated across every list page (students, staff,
   parents, fees, homework, lesson notes, etc.): 2-3 icon-only .btn.btn-sm
   links sitting directly next to each other with no gap of their own, so
   they either touched edge-to-edge or wrapped unevenly depending on how
   much width the column happened to get. Fixed once, globally, instead of
   editing each list page's markup individually. */
.data-table td a.btn,
.data-table td button.btn {
    margin: 2px 4px 2px 0;
}
.data-table td a.btn:last-child,
.data-table td button.btn:last-child {
    margin-right: 0;
}
/* NOTE: display is deliberately NOT set on the <td> itself here anymore -
   setting display:flex/grid directly on a table cell pulls it out of the
   browser's table column-layout algorithm and can make it render
   misaligned with (or overlapping) neighboring columns. footer.php's
   global script wraps a cell's button(s) in an inner <div class="td-btn-
   wrap"> at page load; that inner div is what gets flexed/gridded below,
   leaving the <td> itself as an ordinary table cell. */
.data-table td:has(> a.btn),
.data-table td:has(> button.btn) {
    white-space: normal;
}
.data-table .td-btn-wrap {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 6px;
}
.data-table td:has(> a.btn) a.btn,
.data-table td:has(> button.btn) button.btn,
.data-table .td-btn-wrap a.btn,
.data-table .td-btn-wrap button.btn {
    margin: 0;
}

/* Table wrapper for horizontal scroll */
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--radius);
}

.table-responsive .data-table {
    min-width: 600px;
}

/* Badges */
.badge {
    display: inline-block;
    padding: 4px 10px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.badge-success {
    background: #d1fae5;
    color: #065f46;
}

.badge-danger {
    background: #fee2e2;
    color: #991b1b;
}

.badge-warning {
    background: #fef3c7;
    color: #92400e;
}

.badge-info {
    background: #dbeafe;
    color: #1e40af;
}

/* Flash Messages */
.flash-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 9999;
    max-width: 90vw;
    width: 400px;
}

.flash {
    padding: 15px 20px;
    border-radius: var(--radius);
    margin-bottom: 10px;
    font-size: 14px;
    display: flex;
    /* flex-start, not center: a flash can be a whole sentence (the Add Staff /
       Add Student one hands over a login ID and a password), and centring puts
       the icon halfway down a four-line block. */
    align-items: flex-start;
    gap: 10px;
    line-height: 1.5;
    animation: slideIn 0.3s ease;
    box-shadow: var(--shadow-lg);
    word-break: break-word;
}

/* The icon is a flex item with no intrinsic minimum width, so a long message
   squeezes it down to a sliver unless it is told not to shrink. */
.flash > i {
    flex: 0 0 auto;
    margin-top: 2px;
}

.flash .flash-body {
    flex: 1 1 auto;
    min-width: 0;
    /* An issued password like e9cf-u6yd-3dyv has no spaces to break at, so
       without this it pushes the box wider than its container. */
    overflow-wrap: anywhere;
}

/* A long message must not run off the bottom of the screen - it scrolls inside
   the flash instead. */
.flash-container .flash {
    max-height: 60vh;
    overflow-y: auto;
}

@keyframes slideIn {
    from { transform: translateX(100%); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

.flash-success {
    background: #d1fae5;
    color: #065f46;
    border-left: 4px solid var(--success);
}

.flash-error {
    background: #fee2e2;
    color: #991b1b;
    border-left: 4px solid var(--danger);
}

.flash-info {
    background: #dbeafe;
    color: #1e40af;
    border-left: 4px solid var(--info);
}

.flash-warning {
    background: #fef3c7;
    color: #92400e;
    border-left: 4px solid var(--warning);
}

/* Text utilities */
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }
.text-warning { color: var(--warning); }
.text-info { color: var(--info); }
.text-gray { color: var(--gray); }

/* Grid - Responsive by default */
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }

/* Page actions */
.page-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    flex-wrap: wrap;
    gap: 10px;
}

.search-box {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.search-box input {
    padding: 8px 14px;
    border: 2px solid var(--border);
    border-radius: var(--radius);
    font-size: 14px;
    width: 250px;
    max-width: 100%;
    min-height: 44px;
}

/* Empty state */
.empty-state {
    text-align: center;
    padding: 60px 20px;
    color: var(--gray);
}

.empty-state i {
    font-size: 48px;
    margin-bottom: 15px;
    color: var(--gray-light);
}

/* Pagination */
.pagination {
    display: flex;
    gap: 5px;
    justify-content: center;
    margin-top: 20px;
    flex-wrap: wrap;
}

.pagination a,
.pagination span {
    padding: 8px 14px;
    border-radius: var(--radius);
    font-size: 14px;
    font-weight: 500;
    min-height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.pagination a {
    background: var(--white);
    border: 1px solid var(--border);
    color: var(--dark);
}

.pagination a:hover {
    background: var(--primary);
    color: var(--on-primary);
    border-color: var(--primary);
}

.pagination .current {
    background: var(--primary);
    color: var(--on-primary);
}

/* Small buttons for table actions */
.btn-sm {
    padding: 6px 12px;
    font-size: 12px;
    min-height: 36px;
}

/* Primary & Secondary badges */
.badge-primary {
    background: var(--primary-light);
    color: var(--primary-dark);
}

.badge-secondary {
    background: var(--light);
    color: var(--gray);
}

/* ============================================================
   UNIVERSAL RESPONSIVE BREAKPOINTS — ALL USER ROLES
   ============================================================ */

/* Large screens: wider sidebar */
@media (min-width: 1400px) {
    :root {
        --sidebar-width: 280px;
    }
    .sidebar { width: 280px; }
    .topbar { left: 280px; }
    .main-content { margin-left: 280px; width: calc(100% - 280px); }
    body.sidebar-hidden .topbar { left: 0; }
    body.sidebar-hidden .main-content { margin-left: 0; width: 100%; }
}

/* Large tablets and small desktops */
@media (max-width: 1200px) {
    .grid-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .stats-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Tablets */
@media (max-width: 992px) {
    .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .page-header h1 { font-size: 1.35rem; }
    .card { padding: 18px; }
}

/* Mobile landscape and small tablets */
@media (max-width: 768px) {
    /* Login */
    .login-container {
        grid-template-columns: 1fr;
    }
    .login-brand {
        padding: 40px 30px;
    }
    .login-form-wrap {
        padding: 40px 30px;
    }

    /* Sidebar - hidden by default, slide in when .open */
    .sidebar {
        transform: translateX(-100%);
        width: 280px !important;
    }
    .sidebar.open {
        transform: translateX(0);
    }
    body.sidebar-hidden .sidebar {
        transform: translateX(-100%);
    }

    /* Topbar - full width */
    .topbar {
        left: 0 !important;
        padding: 0 14px;
    }

    /* Main content - full width */
    .main-content {
        margin-left: 0 !important;
        width: 100% !important;
        padding: calc(var(--topbar-height) + 12px) 12px 12px;
    }

    /* Sidebar brand compact */
    .sidebar-brand {
        padding: 12px 10px;
    }
    .sidebar-brand .logo-icon,
    .sidebar-brand img {
        width: 44px;
        height: 44px;
    }
    .logo-text {
        font-size: 12px;
    }

    /* Sidebar user compact */
    .sidebar-user {
        padding: 12px 16px;
    }
    .sidebar-user .user-avatar,
    .sidebar-user img {
        width: 36px;
        height: 36px;
    }
    .sidebar-user .user-name {
        font-size: 13px;
    }

    /* Nav items compact */
    .nav-item {
        padding: 10px 16px;
        font-size: 13px;
    }
    .nav-section {
        padding: 8px 16px 4px;
        font-size: 10px;
    }

    /* Stats */
    .stats-grid {
        grid-template-columns: 1fr;
        gap: 12px;
    }

    /* Grids */
    .grid-2,
    .grid-3,
    .grid-4 {
        grid-template-columns: 1fr;
        gap: 12px;
    }

    /* Page header */
    .page-header h1 {
        font-size: 1.15rem;
    }
    .page-header {
        margin-bottom: 16px;
    }

    /* Cards */
    .card {
        padding: 14px;
        margin-bottom: 12px;
        border-radius: 10px;
    }

    /* Tables - horizontal scroll wrapper */
    .data-table {
        display: block;
        overflow-x: auto;
        white-space: nowrap;
        -webkit-overflow-scrolling: touch;
        font-size: 13px;
    }
    .data-table thead th,
    .data-table tbody td {
        padding: 10px 12px;
    }

    /* Action buttons (view/edit/link/delete icons) in table cells - on
       narrow screens tile two-per-row instead of a single-column stack
       (which wasted a lot of vertical space) or a single row that only
       fit via horizontal scroll. Reverts to a normal single row above
       this breakpoint since the base rule (outside this media query)
       stays flex/nowrap.
       Grid is applied to the .td-btn-wrap div footer.php's script inserts
       around a cell's button(s) - NOT to the <td> itself (see the note
       above the base-rule version of this comment for why).
       auto-fit (not a fixed repeat(2, ...)) matters here: some cells only
       ever hold ONE button (e.g. a lone "View" or "Review & Confirm"
       action in its own column, as opposed to a cell with several icon
       buttons together). With a fixed 2-column grid, that lone button got
       squeezed into just the first of two tracks - half the cell's width -
       cutting its label off ("Review & C..."). auto-fit collapses the
       second, empty track to 0 width when there's only one item, so a
       single button gets the full row; two or more buttons still tile
       two-per-row exactly as before. */
    .data-table td:has(> .td-btn-wrap) {
        min-width: 84px;
    }
    .data-table .td-btn-wrap {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
        gap: 6px;
    }
    .data-table td:has(> a.btn) a.btn,
    .data-table td:has(> button.btn) button.btn,
    .data-table .td-btn-wrap a.btn,
    .data-table .td-btn-wrap button.btn {
        width: 100%;
        justify-content: center;
    }

    /* Topbar right */
    .topbar-right {
        gap: 10px;
    }
    .school-name {
        font-size: 13px;
        max-width: 160px;
    }
    .tb-pill-session, .tb-pill-term {
        display: none;
    }

    /* Forms */
    .form-group {
        margin-bottom: 14px;
    }
    .form-label {
        font-size: 13px;
    }
    .form-control,
    select.form-control,
    textarea.form-control,
    .login-form .form-control {
        padding: 10px 12px;
        font-size: 16px; /* Prevents iOS zoom */
        min-height: 44px;
    }
    textarea.form-control {
        min-height: 80px;
    }

    /* Buttons */
    .btn {
        padding: 10px 16px;
        font-size: 13px;
        min-height: 44px;
    }
    .btn-sm {
        padding: 8px 12px;
        font-size: 12px;
        min-height: 36px;
    }

    /* Search box - filter controls (search input, dropdowns, search/clear
       buttons) tile two-per-row instead of stacking one-per-row. The
       !important overrides are needed because several pages set inline
       widths/display on these elements (e.g. style="width:200px"). Reverts
       to the normal single-row layout automatically above this breakpoint,
       since these rules only exist inside this media query. */
    .search-box,
    .search-box form {
        display: grid !important;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
        width: 100%;
    }
    .search-box input,
    .search-box select,
    .search-box .form-control,
    .search-box button,
    .search-box .btn,
    .search-box a {
        width: 100% !important;
        max-width: none !important;
        margin: 0;
        font-size: 16px;
    }

    /* Page actions - the action bar (search box + buttons like "Add
       Student"/"Promote Students") also tiles two-per-row on mobile. The
       search box (which already tiles its own filters in twos above) spans
       the full width so its internal grid has room to work with; the
       remaining action buttons pair up two-per-row beneath it. */
    .page-actions {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: stretch;
        gap: 10px;
    }
    .page-actions > .search-box {
        grid-column: 1 / -1;
    }
    .page-actions .btn {
        width: 100%;
        justify-content: center;
    }

    /* Flash */
    .flash-container {
        left: 12px;
        right: 12px;
        width: auto;
        top: 12px;
    }
    .flash {
        padding: 12px 14px;
        font-size: 13px;
    }

    /* Stats cards */
    .stat-card {
        padding: 16px;
    }
    .stat-icon {
        width: 40px;
        height: 40px;
        font-size: 18px;
    }
    .stat-info h3 {
        font-size: 22px;
    }

    /* Pagination */
    .pagination a,
    .pagination span {
        padding: 6px 10px;
        font-size: 13px;
    }
}

/* Mobile portrait */
@media (max-width: 480px) {
    html {
        font-size: 15px;
    }

    .sidebar {
        width: 260px !important;
    }
    .main-content {
        padding: calc(var(--topbar-height) + 10px) 10px 10px;
    }

    .topbar {
        padding: 0 12px;
    }
    .topbar .school-name {
        font-size: 12px;
        max-width: 90px;
    }
    .topbar-right {
        gap: 6px;
    }

    .sidebar-brand {
        padding: 10px 8px;
    }
    .sidebar-brand .logo-icon,
    .sidebar-brand img {
        width: 40px;
        height: 40px;
    }
    .logo-text {
        font-size: 11px;
    }

    .sidebar-user {
        padding: 10px 14px;
        gap: 10px;
    }
    .sidebar-user .user-avatar,
    .sidebar-user img {
        width: 34px;
        height: 34px;
    }
    .sidebar-user .user-name {
        font-size: 12px;
    }
    .sidebar-user .user-role {
        font-size: 10px;
    }

    .nav-item {
        padding: 9px 14px;
        font-size: 12px;
    }
    .nav-section {
        padding: 6px 14px 3px;
        font-size: 9px;
    }

    .page-header h1 {
        font-size: 1.05rem;
    }
    .page-header p {
        font-size: 12px;
    }

    .stat-info h3 {
        font-size: 1.5rem;
    }
    .stat-info p {
        font-size: 12px;
    }
    .stat-icon {
        width: 36px;
        height: 36px;
        font-size: 16px;
        margin-bottom: 10px;
    }

    .btn {
        padding: 9px 14px;
        font-size: 12px;
    }
    .btn-sm {
        padding: 6px 10px;
        font-size: 11px;
    }

    .card {
        padding: 12px;
        border-radius: 10px;
    }
    .card h3 {
        font-size: 15px;
        margin-bottom: 14px;
    }

    .data-table thead th,
    .data-table tbody td {
        padding: 8px 10px;
        font-size: 12px;
    }

    .form-control {
        padding: 8px 10px;
    }
    .form-label {
        font-size: 12px;
    }

    .badge {
        padding: 3px 8px;
        font-size: 11px;
    }

    .empty-state {
        padding: 40px 16px;
    }
    .empty-state i {
        font-size: 36px;
    }

    .search-box input {
        padding: 8px 10px;
    }

    .login-brand {
        padding: 30px 20px;
    }
    .login-brand h2 {
        font-size: 22px;
    }
    .login-form-wrap {
        padding: 30px 20px;
    }

    /* Module-local panel classes (grades, broadsheet, skills, analytics,
       publish, settings) carry their own <style> blocks and never inherited
       the .card/.data-table shrink above, so they stayed full-size and
       overflowed on phones. Same treatment, just the extra class names. */
    .filter-card, .bs-filter, .an-filter, .sk-filter, .pub-filter, .gs-card,
    .gs-preview, .sk-card, .ms-card, .pub-card, .pub-empty, .an-stat-card,
    .an-chart-card, .an-table-card, .an-empty, .grade-table-wrap, .bs-table-wrap {
        padding: 12px;
        border-radius: 10px;
    }
    .filter-card label, .bs-filter label, .an-filter label, .sk-filter label,
    .pub-filter label, .gs-card label {
        font-size: 11px;
    }
    .an-stat-card .value, .stat-mini .info h4 {
        font-size: 1.3rem;
    }
    .grade-table thead th, .grade-table tbody td,
    .bs-table thead th, .bs-table tbody td,
    .gs-table thead th, .gs-table tbody td,
    .an-table thead th, .an-table tbody td {
        padding: 6px 8px;
        font-size: 11px;
    }
    .ms-page .page-header h1, .sk-page .page-header h1, .gs-page .page-header h1,
    .pub-page .page-header h1, .analytics-page .page-header h1, .bs-page .page-header h1 {
        font-size: 1.05rem;
    }
    .ms-grid {
        grid-template-columns: 1fr;
    }
    .ms-add-form {
        flex-direction: column;
    }
}

/* Very small screens */
@media (max-width: 360px) {
    .sidebar {
        width: 100% !important;
    }
    .main-content {
        padding: calc(var(--topbar-height) + 8px) 8px 8px;
    }
    .topbar {
        padding: 0 10px;
    }
    .topbar .school-name {
        display: none;
    }
    .sidebar-brand .logo-icon,
    .sidebar-brand img {
        width: 36px;
        height: 36px;
    }
    .logo-text {
        font-size: 10px;
    }

    .card {
        padding: 10px;
        border-radius: 8px;
    }
    .page-header h1 {
        font-size: 1rem;
    }
    .stat-info h3 {
        font-size: 1.3rem;
    }
    .stat-icon {
        width: 32px;
        height: 32px;
        font-size: 14px;
    }

    .btn {
        padding: 8px 12px;
        font-size: 12px;
        min-height: 40px;
    }

    .data-table thead th {
        padding: 6px 8px;
        font-size: 11px;
    }
    .data-table tbody td {
        padding: 6px 8px;
        font-size: 11px;
    }

    .nav-item {
        padding: 8px 12px;
        font-size: 12px;
    }
}

/* Touch device optimizations */
@media (hover: none) and (pointer: coarse) {
    .nav-item, .submenu-toggle, .btn, .form-control, select, input, textarea, .sidebar-toggle-btn {
        min-height: 44px;
    }
    .nav-item {
        padding-top: 12px;
        padding-bottom: 12px;
    }
    .submenu-toggle {
        padding-top: 12px;
        padding-bottom: 12px;
    }
    .nav-item:hover:not(.active) {
        background: transparent;
        color: var(--gray-light);
        border-left-color: transparent;
    }
    .btn:active {
        transform: scale(0.98);
    }
    .data-table {
        font-size: 15px; /* Prevents zoom on iOS */
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
    .card, .stat-card, .btn, .form-control {
        border-width: 2px;
    }
    .nav-item.active {
        border-left-width: 4px;
    }
}

/* Print styles */
@media print {
    .sidebar,
    .topbar,
    .sidebar-overlay,
    .page-actions,
    .btn,
    .theme-toggle-btn,
    .sidebar-toggle-btn,
    .ai-widget {
        display: none !important;
    }
    .main-content {
        margin-left: 0;
        padding: 0;
        width: 100%;
        /* .main-content carries min-height: 100vh/100dvh for the normal
           dashboard layout (so short pages still fill the screen behind the
           sidebar). In print that forces every page to reserve a full
           screen-height of blank space no matter how short the actual
           content is - a one-card receipt would print as a mostly-empty
           page, sometimes spilling a blank second page. Printing should
           size to content instead. */
        min-height: 0 !important;
        height: auto !important;
    }
    body {
        background: #fff;
        color: #000;
        min-height: 0 !important;
    }
    .card {
        box-shadow: none;
        border: 1px solid #ddd;
        break-inside: avoid;
    }
    .data-table {
        font-size: 12px;
    }
    a {
        text-decoration: none;
        color: #000;
    }
}
/* ---------------------------------------------------------------------------
   Platform wordmark: DASTIC with a raised, smaller OS.
   Emitted by platformWordmark() (includes/functions.php) as
   <sup class="wm-os">OS</sup>. The default <sup> in most browsers is ~0.83em
   with a baseline shift tuned for footnote digits, which next to a 900-weight
   logotype reads as a footnote reference rather than part of the mark - hence
   the explicit size and a line-height of 0 so the raised pair can never push
   the line box taller than the word it sits on.
   The ANIMATED wordmark on the landing/entrance pages does not use this class:
   its letters are split in JS and carry .bl-sup / .sfl-sup instead, because
   those scripts rebuild the element from textContent and would drop a <sup>.
   --------------------------------------------------------------------------- */
.wm-os {
    font-size: 0.52em;
    line-height: 0;
    vertical-align: super;
    letter-spacing: 0.02em;
    font-weight: inherit;
}

/* ============================================================
   DARK MODE OVERRIDES FOR PAGE-LOCAL PANELS
   ============================================================

   Many module pages carry their own <style> block that hardcodes
   `background: #fff` on a panel and `color: #1e293b` on the text inside it.
   Those values never flip, so in dark mode a page ends up as bright white
   islands on a dark background - and anywhere the surrounding surface DOES
   come from a token, the dark text lands on a dark panel and disappears.

   These rules fix that additively: they apply ONLY under
   html[data-theme="dark"], so light mode cannot be affected by anything here.
   That matters - an earlier attempt rewrote the colours in the page files
   themselves and turned readable dark-on-white into invisible light-on-white
   wherever the background was a gradient or a fixed fill that was missed.
   Overriding one theme only means a mistake here can never break the other.

   Specificity, not order, is what makes these win: the page's own rules are
   inline in <head> and therefore come after this file, so `html[data-theme=...]`
   is prepended to out-weigh them.

   Panels and their text are always set TOGETHER. Setting only one side is the
   exact mistake that caused the invisible text in the first place.
   ============================================================ */

/* --- Panels, cards and filter bars: dark surface instead of white --- */
html[data-theme="dark"] .bs-filter,
html[data-theme="dark"] .bs-table-wrap,
html[data-theme="dark"] .bs-mini,
html[data-theme="dark"] .filter-card,
html[data-theme="dark"] .stat-mini,
html[data-theme="dark"] .grade-table-wrap,
html[data-theme="dark"] .an-filter,
html[data-theme="dark"] .an-stat-card,
html[data-theme="dark"] .an-chart-card,
html[data-theme="dark"] .gs-card,
html[data-theme="dark"] .gs-preview,
html[data-theme="dark"] .sk-filter,
html[data-theme="dark"] .sk-card,
html[data-theme="dark"] .pub-filter,
html[data-theme="dark"] .pub-card,
html[data-theme="dark"] .pub-empty,
html[data-theme="dark"] .stat-card-dash,
html[data-theme="dark"] .metric-card,
html[data-theme="dark"] .panel,
html[data-theme="dark"] .stat-pill,
html[data-theme="dark"] .results-table {
    background: var(--white) !important;
    border-color: var(--border) !important;
    color: var(--dark);
}

/* --- Headings and figures inside those panels --- */
html[data-theme="dark"] .stat-mini .info h4,
html[data-theme="dark"] .an-stat-card .value,
html[data-theme="dark"] .an-chart-card h3,
html[data-theme="dark"] .gs-card h3,
html[data-theme="dark"] .gs-preview h3,
html[data-theme="dark"] .sk-card h3,
html[data-theme="dark"] .sk-skill-row label,
html[data-theme="dark"] .pub-card h4,
html[data-theme="dark"] .pub-empty h3,
html[data-theme="dark"] .table-header h3,
html[data-theme="dark"] .stat-card-dash .value,
html[data-theme="dark"] .metric-card .value,
html[data-theme="dark"] .stat-pill strong {
    color: var(--dark) !important;
}

/* --- Muted secondary text stays muted, but readable --- */
html[data-theme="dark"] .stat-mini .info span,
html[data-theme="dark"] .an-stat-card .label,
html[data-theme="dark"] .stat-card-dash .label,
html[data-theme="dark"] .metric-card .label,
html[data-theme="dark"] .pub-card p,
html[data-theme="dark"] .pub-empty p {
    color: var(--gray) !important;
}

/* --- Table headers: the broadsheet header was the worst offender, pale text
       on a pale fill. Both sides set here so the pair is always legible. --- */
html[data-theme="dark"] .bs-table thead th,
html[data-theme="dark"] .gs-table thead th,
html[data-theme="dark"] .results-table thead th,
html[data-theme="dark"] .data-table thead th,
html[data-theme="dark"] .grade-table thead th {
    background: var(--light) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

html[data-theme="dark"] .bs-table tbody td,
html[data-theme="dark"] .gs-table tbody td,
html[data-theme="dark"] .results-table tbody td,
html[data-theme="dark"] .grade-table tbody td {
    color: var(--dark);
    border-color: var(--border) !important;
}

html[data-theme="dark"] .bs-table tbody td.student-cell {
    background: var(--light) !important;
    color: var(--dark) !important;
}

/* --- Form controls inside those panels: a white select on a dark card is as
       jarring as unreadable text, and the caret goes invisible too. --- */
html[data-theme="dark"] .bs-filter select,
html[data-theme="dark"] .filter-card select,
html[data-theme="dark"] .an-filter select,
html[data-theme="dark"] .sk-filter select,
html[data-theme="dark"] .pub-filter select,
html[data-theme="dark"] .bs-filter input,
html[data-theme="dark"] .filter-card input,
html[data-theme="dark"] .sk-filter input {
    background: var(--white) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

/* --- Second batch: the panel class names that repeat across many pages.
       Deliberately EXCLUDED: .school-crest img, .school-logo, .site-nav-brand img
       and .toggle-slider:before. Those are white on purpose - a logo backing and
       a toggle knob - and darkening them would break the artwork rather than fix
       contrast. Contrast is only a problem where TEXT sits on the fill. --- */
html[data-theme="dark"] .stat-card,
html[data-theme="dark"] .filter-bar,
html[data-theme="dark"] .card,
html[data-theme="dark"] .quick-action-card,
html[data-theme="dark"] .admin-card,
html[data-theme="dark"] .page-header,
html[data-theme="dark"] .inline-modal-content,
html[data-theme="dark"] .cp-card,
html[data-theme="dark"] .chart-card,
html[data-theme="dark"] .exam-card,
html[data-theme="dark"] .modal-box,
html[data-theme="dark"] .form-panel,
html[data-theme="dark"] .question-card,
html[data-theme="dark"] .hw-card,
html[data-theme="dark"] .answer-card,
html[data-theme="dark"] .preview-card,
html[data-theme="dark"] .review-section,
html[data-theme="dark"] .an-table-card,
html[data-theme="dark"] .an-empty,
html[data-theme="dark"] .grade-legend,
html[data-theme="dark"] .ai-chat-container,
html[data-theme="dark"] .chat-input-area,
html[data-theme="dark"] .ai-widget-panel,
html[data-theme="dark"] .ai-widget-input-area,
html[data-theme="dark"] .kb-admin .stat-card,
html[data-theme="dark"] .kb-admin .provider-card,
html[data-theme="dark"] .kb-admin table,
html[data-theme="dark"] .kb-admin .form-modal .modal-content {
    background: var(--white) !important;
    border-color: var(--border) !important;
    color: var(--dark);
}

/* Table headers inside those panels. */
html[data-theme="dark"] .an-table thead th,
html[data-theme="dark"] .kb-admin table th {
    background: var(--light) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

/* Inputs and selects sitting on the now-dark panels. */
html[data-theme="dark"] .filter-bar select,
html[data-theme="dark"] .filter-bar input,
html[data-theme="dark"] .filter-group select,
html[data-theme="dark"] .filter-group input,
html[data-theme="dark"] .form-control,
html[data-theme="dark"] .score-input,
html[data-theme="dark"] .fill-input {
    background: var(--white) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

/* --- .btn-secondary: "Check Again", "Back to Home", "Use Another Code" on the
       entrance exam pages, and every Cancel/Back button across the signed-in
       app (~80 files use this class). The base rule pairs
       background: var(--gray-light) with color: var(--dark) - both tokens flip
       in dark mode, but to the SAME light end of the palette
       (--gray-light: #cbd5e1, --dark: #e2e8f0), so the result is pale text on a
       pale background rather than the dark surface a "secondary" button needs.
       That is the washed-out, barely-readable look reported on the entrance
       exam page. The pairing that already works everywhere else in dark mode -
       background: var(--white), color: var(--dark) - is used here too, so the
       button reads as a proper dark control instead of nearly disappearing.
       This selector matches on documentElement, so it applies equally to
       entrance_exam.php's own [data-theme] toggle and to the main app's. --- */
html[data-theme="dark"] .btn-secondary {
    background: var(--white) !important;
    color: var(--dark) !important;
    border: 1px solid var(--border);
}
html[data-theme="dark"] .btn-secondary:hover {
    background: var(--light) !important;
}

/* --- manage_skills.php: same white-card-on-dark-page gap as the rest,
       just never covered because the page uses its own .ms- prefix. --- */
html[data-theme="dark"] .ms-card {
    background: var(--white) !important;
    border-color: var(--border) !important;
    color: var(--dark);
}
html[data-theme="dark"] .ms-card h3 {
    color: var(--dark) !important;
}
html[data-theme="dark"] .ms-card p.desc {
    color: var(--gray) !important;
}
html[data-theme="dark"] .ms-skill-row {
    border-color: var(--border) !important;
}
html[data-theme="dark"] .ms-skill-row .name {
    color: var(--dark) !important;
}
/* ============================================================
   DARK MODE, PART 2 - THE TEXT SIDE THAT PART 1 MISSED
   ============================================================

   Part 1 above flipped panel BACKGROUNDS to var(--white) (#1e293b in dark)
   but left the page-local <style> blocks' hardcoded TEXT colours alone. Those
   are dark by design (#1a1a2e, #1e293b, #334155, #475569, #64748b) because
   they were written for a white card. Once the card went dark underneath them,
   several landed at a measured contrast ratio of 1.00 - the same colour as
   their own background, i.e. completely invisible. Student names and total
   scores on the grade entry page were literally unreadable.

   That is precisely the failure the comment on Part 1 warns about ("Panels and
   their text are always set TOGETHER"). Part 1 set one side. This sets the
   other. Audited by walking every text node on the grades and broadsheet pages
   and computing the WCAG contrast ratio against its effective background,
   rather than by eye - eyeballing is what let a ratio of 1.00 through twice.
   ============================================================ */

/* --- Page titles. Every module page repeats `.page-header h1 { color:#1a1a2e }`
       in its own <style>, and .page-header itself is given a dark surface by
       Part 1, so the title disappeared into it. --- */
html[data-theme="dark"] .page-header h1,
html[data-theme="dark"] .bs-page .page-header h1,
html[data-theme="dark"] .analytics-page .page-header h1,
html[data-theme="dark"] .gs-page .page-header h1,
html[data-theme="dark"] .sk-page .page-header h1,
html[data-theme="dark"] .ms-page .page-header h1,
html[data-theme="dark"] .pub-page .page-header h1 {
    color: var(--dark) !important;
}

/* --- Secondary/muted copy. #64748b and #475569 sit at ~1.9-3.1 against the
       dark panel, under the 4.5 minimum. --gray (#94a3b8) clears it while
       still reading as muted rather than primary text. --- */
html[data-theme="dark"] .page-header p,
html[data-theme="dark"] .filter-card label,
html[data-theme="dark"] .bs-filter label,
html[data-theme="dark"] .an-filter label,
html[data-theme="dark"] .sk-filter label,
html[data-theme="dark"] .pub-filter label,
html[data-theme="dark"] .gs-card label,
html[data-theme="dark"] .remark-text,
html[data-theme="dark"] .legend-item,
html[data-theme="dark"] .student-cell .adm,
html[data-theme="dark"] .summary-card h4,
html[data-theme="dark"] .performer-card .info .avg {
    color: var(--gray) !important;
}

/* --- Grade entry table: the two that measured 1.00. --- */
html[data-theme="dark"] .student-cell .name,
html[data-theme="dark"] .total-score,
html[data-theme="dark"] .summary-card .value,
html[data-theme="dark"] .performer-card .info .name,
html[data-theme="dark"] .performer-card .score {
    color: var(--dark) !important;
}

/* --- Broadsheet summary and top-performer cards: still white islands, since
       Part 1's list never included them. The performer card paints a white
       linear-gradient, so background-color alone will not cover it. --- */
html[data-theme="dark"] .summary-card,
html[data-theme="dark"] .performer-card {
    background: var(--white) !important;
    background-image: none !important;
    border-color: var(--border) !important;
}

/* --- Broadsheet tinted cells. These keep a pale fill (#fffbeb, #f0fdf4,
       #faf5ff) from the page's own CSS while Part 1 turned the table text
       light - light on pale measured 1.15-1.19. The tint is meaningful here
       (total / average / position columns and the A-F grade colouring), so it
       is preserved as a DARK tint of the same hue rather than dropped, and the
       text is set alongside it. --- */
html[data-theme="dark"] .bs-table tbody td.total-cell {
    background: #3a3320 !important;
    color: #fde68a !important;
}
html[data-theme="dark"] .bs-table tbody td.avg-cell {
    background: #1e3a2b !important;
    color: #86efac !important;
}
html[data-theme="dark"] .bs-table tbody td.pos-cell {
    background: #2e2545 !important;
    color: #c4b5fd !important;
}

/* Grade colour coding. The page sets these with !important, so these must
   carry it too, and .bs-display is the inner div that holds the number. */
html[data-theme="dark"] .score-A { background: #14351f !important; color: #86efac !important; }
html[data-theme="dark"] .score-B { background: #16294a !important; color: #93c5fd !important; }
html[data-theme="dark"] .score-C { background: #3a3320 !important; color: #fde68a !important; }
html[data-theme="dark"] .score-D { background: #3d2a17 !important; color: #fdba74 !important; }
html[data-theme="dark"] .score-E { background: #40201f !important; color: #fca5a5 !important; }
html[data-theme="dark"] .score-F { background: #451a1a !important; color: #fca5a5 !important; }
html[data-theme="dark"] .score-A .bs-display,
html[data-theme="dark"] .score-B .bs-display,
html[data-theme="dark"] .score-C .bs-display,
html[data-theme="dark"] .score-D .bs-display,
html[data-theme="dark"] .score-E .bs-display,
html[data-theme="dark"] .score-F .bs-display {
    color: inherit !important;
}

/* Zebra striping and hover both painted a pale fill under light text. */
html[data-theme="dark"] .bs-table tbody tr:nth-child(even),
html[data-theme="dark"] .bs-table tbody tr:nth-child(even) td {
    background: var(--light) !important;
}
html[data-theme="dark"] .bs-table tbody tr:hover,
html[data-theme="dark"] .bs-table tbody tr:hover td,
html[data-theme="dark"] .grade-table tbody tr:hover {
    background: #2f3f57 !important;
}
html[data-theme="dark"] .bs-display {
    color: var(--dark);
}

/* --- Stat-mini icon chips (grade entry) and the export button: pale fills
       that survived as bright blobs on the dark card. Tinted dark, keeping
       each one's hue so the colour still distinguishes them. --- */
html[data-theme="dark"] .stat-mini .icon.blue   { background: #16294a !important; color: #93c5fd !important; }
html[data-theme="dark"] .stat-mini .icon.green  { background: #14351f !important; color: #86efac !important; }
html[data-theme="dark"] .stat-mini .icon.purple { background: #2e2545 !important; color: #c4b5fd !important; }
html[data-theme="dark"] .stat-mini .icon.orange { background: #3d2a17 !important; color: #fdba74 !important; }
html[data-theme="dark"] .btn-export {
    background: #16294a !important;
    color: #93c5fd !important;
}

/* --- The ACTIVE sidebar item, on every page of the app. The base rule pairs
       background: var(--dark-light) with color: var(--white). Both tokens flip
       in dark mode, but to the SAME light end (--dark-light: #cbd5e1), so the
       highlighted item became pale-on-pale at a measured 1.36 - the currently
       open section was the least readable thing in the sidebar. This is the
       identical failure already documented for .btn-secondary above; the fix is
       the same, a genuinely dark surface with light text, keeping the primary
       accent on the left border so "active" still reads as active. --- */
html[data-theme="dark"] .nav-item.active,
html[data-theme="dark"] .submenu-toggle.active {
    background: var(--light) !important;
    color: var(--dark) !important;
}
html[data-theme="dark"] .nav-item.active span,
html[data-theme="dark"] .submenu-toggle.active span,
html[data-theme="dark"] .nav-item.active i,
html[data-theme="dark"] .submenu-toggle.active i {
    color: var(--dark) !important;
}

/* --- Text coloured by an INLINE style attribute, on the grades pages that use
       them (skills.php's collapsible "Customize skill & remark labels" panel and
       its section headings). An inline style outranks a normal stylesheet rule,
       so these kept their #1e293b / #64748b even after the card went dark - the
       summary heading measured 1.00, identical to its own background. Only
       `!important` in a stylesheet beats a non-important inline declaration,
       which is why these carry it. Scoped to the specific containers rather than
       applied blanket-wide, so no unrelated inline colour is disturbed. --- */
html[data-theme="dark"] .sk-card > summary,
html[data-theme="dark"] .sk-card h4,
html[data-theme="dark"] .gs-card h4,
html[data-theme="dark"] .an-table-card h3,
html[data-theme="dark"] .an-empty h3 {
    color: var(--dark) !important;
}
html[data-theme="dark"] .sk-card > p,
html[data-theme="dark"] .sk-card label,
html[data-theme="dark"] .gs-card > p {
    color: var(--gray) !important;
}
/* Grade entry's "Entered By" column carries its muted #64748b inline
   (grades.php:284), so the table-wide rule cannot reach it. */
html[data-theme="dark"] .grade-table tbody td[style*="color"] {
    color: var(--gray) !important;
}
html[data-theme="dark"] .sk-card a,
html[data-theme="dark"] .sk-student-row a {
    color: #a5b4fc !important;
}

html[data-theme="dark"] .ms-add-form input {
    background: var(--white) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

/* ============================================================
   DARK MODE, PART 3: THE EDIT PANEL AND THE REMAINING TINTED CHIPS
   ============================================================
   Part 2 covered the broadsheet's own tinted cells. These are the
   surfaces it did not reach: the report card's inline EDIT panel, the
   grade-entry badges, and the alert/badge chips that repeat across the
   skills, settings and publish screens - each a pale fill from a page's
   own <style> block, each still bright-on-dark or invisible.
   ============================================================ */

/* --- Report card INLINE EDIT panel (skills + remarks editor).
       This is the surface behind "editing skills doesn't work": the panel
       is a hardcoded white card, so in dark mode anything in it without an
       explicit colour inherited the light body text and disappeared, which
       reads as a form that does nothing. An earlier fix pinned the panel to
       `color-scheme: light` so the native selects would stop rendering dark
       -on-white; now that the panel itself goes dark, color-scheme has to
       flip WITH it or those selects invert straight back. The printed
       report card is deliberately untouched - it stays light, and is
       display:none in print regardless. --- */
html[data-theme="dark"] .rc-inline-edit {
    background: var(--white) !important;
    color: var(--dark) !important;
    color-scheme: dark;
    border: 1px solid var(--border);
}
html[data-theme="dark"] .rc-inline-edit h4 {
    color: var(--dark) !important;
}
html[data-theme="dark"] .rc-edit-row label {
    color: var(--gray) !important;
}
html[data-theme="dark"] .rc-inline-edit select,
html[data-theme="dark"] .rc-inline-edit input,
html[data-theme="dark"] .rc-inline-edit textarea {
    background: var(--light) !important;
    color: var(--dark) !important;
    border-color: var(--border) !important;
}

/* --- Grade entry badges. Same A-F colour language as the broadsheet's
       .score-* cells, so the values are kept identical to those above. --- */
html[data-theme="dark"] .grade-badge.A { background: #14351f !important; color: #86efac !important; }
html[data-theme="dark"] .grade-badge.B { background: #16294a !important; color: #93c5fd !important; }
html[data-theme="dark"] .grade-badge.C { background: #3a3320 !important; color: #fde68a !important; }
html[data-theme="dark"] .grade-badge.D { background: #3d2a17 !important; color: #fdba74 !important; }
html[data-theme="dark"] .grade-badge.E { background: #401d1d !important; color: #fca5a5 !important; }
html[data-theme="dark"] .grade-badge.F { background: #451a1a !important; color: #fca5a5 !important; }

/* --- Row states on the grade entry table. --- */
html[data-theme="dark"] .grade-table tbody tr.unsaved,
html[data-theme="dark"] .grade-table tbody tr.unsaved td {
    background: #3a3320 !important;
}
html[data-theme="dark"] .grade-table tbody tr:hover,
html[data-theme="dark"] .an-table tbody tr:hover {
    background: #22304a !important;
}

/* --- Alert strips: one shape, four different page prefixes. --- */
html[data-theme="dark"] .sk-alert.success,
html[data-theme="dark"] .ms-alert.success,
html[data-theme="dark"] .gs-alert.success,
html[data-theme="dark"] .pub-alert.success {
    background: #14351f !important;
    color: #86efac !important;
    border-color: #206136 !important;
}
html[data-theme="dark"] .sk-alert.error,
html[data-theme="dark"] .ms-alert.error,
html[data-theme="dark"] .gs-alert.error,
html[data-theme="dark"] .pub-alert.error {
    background: #401d1d !important;
    color: #fca5a5 !important;
    border-color: #7f1d1d !important;
}
html[data-theme="dark"] .pub-alert.warning {
    background: #3a3320 !important;
    color: #fde68a !important;
    border-color: #78500f !important;
}
html[data-theme="dark"] .gs-info {
    background: #16294a !important;
    color: #bfdbfe !important;
    border-color: #1e40af !important;
}

/* --- Small chips: skill badges, the remove/restore buttons beside them,
       the selected student row, and the publish card's published state. --- */
html[data-theme="dark"] .ms-skill-row .badge.builtin {
    background: #262a5a !important;
    color: #c7d2fe !important;
}
html[data-theme="dark"] .ms-skill-row .badge.custom {
    background: #3a3320 !important;
    color: #fde68a !important;
}
html[data-theme="dark"] .ms-skill-row .badge.disabled,
html[data-theme="dark"] .btn-remove {
    background: #401d1d !important;
    color: #fca5a5 !important;
}
html[data-theme="dark"] .btn-restore {
    background: #14351f !important;
    color: #86efac !important;
}
html[data-theme="dark"] .gs-actions .btn-add {
    background: #16294a !important;
    color: #93c5fd !important;
}
html[data-theme="dark"] .sk-student-row.active {
    background: #262a5a !important;
    border-color: #4f46e5 !important;
}
html[data-theme="dark"] .status-badge.draft {
    background: #2b3648 !important;
    color: var(--dark) !important;
}
html[data-theme="dark"] .pub-card.is-published {
    background: #14351f !important;
    border-color: #206136 !important;
}
html[data-theme="dark"] .pub-card.is-published .status-line { color: #86efac !important; }
html[data-theme="dark"] .pub-card:not(.is-published) .status-line { color: #fdba74 !important; }
html[data-theme="dark"] .pub-card .btn-unpublish {
    background: var(--light) !important;
    color: #86efac !important;
    border-color: #206136 !important;
}
