/* ============================================
   Ayulquran LMS - ArchitectUI Premium Design
   Modern, Clean & Professional Dashboard
   ============================================ */

/* This is the SOURCE file - includes/header.php actually serves
   public/css/style.min.css. After editing this file, run
   `npm run build:assets` and commit the regenerated style.min.css
   before deploying, or your change won't reach the live site. */

/* Import Google Fonts - matches the Ayulquran marketing site's brand font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700;800&display=swap');

/* CSS Variables - ArchitectUI Color Palette */
:root {
    /* Brand Colors - AyulQuran Unified */
    --primary-color: #00a199;
    /* Teal/Turquoise */
    --primary-dark: #008078;
    --primary-light: #00c4b8;
    --primary-lighter: #e7f6f5;
    --primary-soft: rgba(0, 161, 153, 0.1);

    --secondary-color: #9a8731;
    /* Gold/Olive Accent */
    --secondary-dark: #7a6a25;
    --secondary-light: #b09d4a;
    --secondary-soft: rgba(154, 135, 49, 0.1);

    /* Rich Aesthetic Gradients */
    --primary-gradient: linear-gradient(135deg, #00a199 0%, #00c4b8 100%);
    --secondary-gradient: linear-gradient(135deg, #9a8731 0%, #b09d4a 100%);
    --glass-gradient: linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%);
    --dark-glass-gradient: linear-gradient(135deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.1) 100%);

    /* Semantic Colors (distinct from brand colors so status always reads unambiguously) */
    --success-color: #16a34a;
    --danger-color: #d92550;
    --warning-color: #f59e0b;
    --info-color: #0ea5e9;

    /* Surface Colors - Support for Depth */
    --bg-page: #f3f4f8;
    --bg-surface-1: #ffffff;
    --bg-surface-2: #f8f9fa;
    --bg-surface-dark: #1a1a1a;
    --bg-glass: rgba(255, 255, 255, 0.7);

    /* Text Colors */
    --text-primary: #1a1a1a;
    --text-secondary: #636e72;
    --text-muted: #95a5a6;
    --text-white: #ffffff;

    /* Premium Shadows */
    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.08);
    --shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.1);
    --shadow-xl: 0 15px 50px rgba(0, 0, 0, 0.12);
    --shadow-premium: 0 20px 60px rgba(0, 0, 0, 0.1);
    --shadow-primary: 0 8px 30px rgba(0, 161, 153, 0.2);
    --shadow-hover: 0 8px 20px rgba(0, 161, 153, 0.25);
    --shadow-gold: 0 8px 30px rgba(154, 135, 49, 0.2);

    /* Status Gradients */
    --success-gradient: linear-gradient(135deg, #16a34a 0%, #22c55e 100%);
    --danger-gradient: linear-gradient(135deg, #d92550 0%, #f05369 100%);
    --warning-gradient: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
    --info-gradient: linear-gradient(135deg, #0ea5e9 0%, #38bdf8 100%);

    /* Grayscale (Bootstrap-compatible) */
    --white: #ffffff;
    --gray-100: #f8f9fa;
    --gray-200: #e9ecef;
    --gray-300: #dee2e6;
    --gray-400: #ced4da;
    --gray-500: #adb5bd;
    --gray-600: #6c757d;
    --gray-700: #495057;
    --gray-800: #343a40;
    --gray-900: #212529;

    /* Glassmorphism Tokens */
    --glass-blur: blur(12px);
    --glass-border: 1px solid rgba(255, 255, 255, 0.2);
    --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.07);

    /* Tab & Pill Tokens */
    --nav-tab-active-color: var(--primary-color);
    --nav-pills-active-bg: var(--primary-gradient);

    /* Border Radius */
    --radius-sm: 0.25rem;
    --radius: 0.5rem;
    --radius-lg: 0.875rem;
    --radius-xl: 1.25rem;

    /* Transitions */
    --transition-base: all 0.2s ease-in-out;
    --transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    --transition-bounce: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

/* Global Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Safety net: several fixed-width/no-wrap flex rows across the app (badge
   groups, card headers) can end up a few pixels wider than a narrow phone
   viewport. Without this, that pushes the whole page sideways instead of
   just clipping that one row - content past the right edge becomes
   invisible with no visible scrollbar, since mobile browsers often hide it.
   This doesn't fix the underlying rows (see the flex-wrap fixes on the
   specific ones found so far), but stops any OTHER such row from taking
   the whole page down with it. */
html {
    overflow-x: hidden;
}

/* Global safety net: Bootstrap's .d-flex.justify-content-between (used
   throughout for card headers, page headers, and list rows - title/label
   on one side, badges or buttons on the other) defaults to
   flex-wrap: nowrap. On narrow screens, whenever the right-side content
   doesn't fit, that squeezes or pushes it off-screen to be clipped by the
   overflow-x: hidden above instead of reflowing. Forcing wrap here is a
   no-op wherever content already fits on one line (i.e. everywhere on
   desktop), and lets it drop to a second line instead of clipping
   wherever it doesn't. */
.d-flex.justify-content-between {
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
}

/* Companion to the above: Bootstrap's .flex-grow-1 sets flex-grow: 1 but
   leaves the flexbox default min-width: auto in place, which sizes the
   item to fit its content (thumbnail + title + description, etc.)
   unshrunk - so instead of wrapping/truncating within the available
   width, it forces the row wider than the screen and gets clipped. This
   lets it actually shrink to fit, the way "grow" already implies it
   should grow to fit. */
.flex-grow-1 {
    min-width: 0;
}

/* Same overflow pattern, but for clustered action buttons: Bootstrap's
   .btn-group is display: inline-flex with no wrap, so a row/list item with
   several icon buttons (edit/delete/view, etc.) next to title text pushes
   past the screen edge instead of dropping to its own line on mobile.
   Wrapping costs the seamless connected-border look only in the rare case
   it actually wraps - the far smaller price versus buttons being clipped
   off-screen and unreachable. */
.btn-group,
.btn-toolbar {
    flex-wrap: wrap;
}

body {
    font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.5;
    color: var(--text-primary);
    background-color: var(--bg-page);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    letter-spacing: 0.01em;
    overflow-x: hidden;
    width: 100%;
    /* Real sticky-footer mechanism: body is the flex column, and the
       sidebar+main wrapper below (body's direct-child .container-fluid -
       not any of the many nested .container-fluid uses inside page
       content) grows to fill any leftover space, pushing <footer>'s own
       existing margin-top: auto down to the bottom of the viewport
       instead of leaving it stranded after a short page's actual content. */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

body > .container-fluid {
    flex: 1 0 auto;
}

h1,
h2,
h3,
h4,
h5,
h6 {
    font-weight: 600;
    line-height: 1.2;
    color: var(--gray-900);
    letter-spacing: -0.02em;
}

h1 {
    font-size: 2.25rem;
}

h2 {
    font-size: 1.875rem;
}

h3 {
    font-size: 1.5rem;
}

h4 {
    font-size: 1.25rem;
}

h5 {
    font-size: 1.125rem;
}

h6 {
    font-size: 1rem;
}

a {
    text-decoration: none;
    transition: var(--transition-base);
}

/* Page Wrapper */
.page-wrapper {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* Gradient Backgrounds */
.bg-gradient-primary {
    background: var(--primary-gradient) !important;
}

.bg-gradient-success {
    background: var(--success-gradient) !important;
}

.bg-gradient-danger {
    background: var(--danger-gradient) !important;
}

.bg-gradient-warning {
    background: var(--warning-gradient) !important;
}

.bg-gradient-info {
    background: var(--info-gradient) !important;
}

/* ============================================
   NAVIGATION & SIDEBAR
   ============================================ */

/* Top Navbar - matches the Ayulquran marketing site's solid white,
   brand-shadowed header rather than a generic translucent admin-panel bar. */
.navbar {
    padding: 0.65rem 1.5rem;
    min-height: 72px;
    background: var(--bg-surface-1) !important;
    border-bottom: none;
    box-shadow: 0 2px 10px rgba(154, 135, 49, 0.12);
    z-index: 1030;
    position: sticky;
    top: 0;
}

.navbar-brand {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.5px;
    color: var(--primary-color);
    transition: var(--transition-base);
}

.navbar-brand img {
    height: 56px;
    max-width: 220px;
    object-fit: contain;
}

.navbar-brand:hover {
    color: var(--primary-dark);
    transform: translateY(-1px);
}

/* Scoped Navbar Links */
.navbar .nav-link {
    font-weight: 500;
    transition: var(--transition-base);
    position: relative;
    color: var(--text-secondary);
}

.navbar .nav-link:hover {
    color: var(--secondary-color) !important;
}

/* Header icon buttons (sidebar toggle, search, notifications) - circular
   hover treatment echoing the marketing site's icon-button language instead
   of plain Bootstrap nav-links. */
.navbar-icon-btn {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    color: var(--text-secondary) !important;
    transition: var(--transition-base);
}

.navbar-icon-btn:hover {
    background: var(--primary-soft);
    color: var(--primary-color) !important;
}

/* Global Nav Links (Tabs/Pills) */
.nav-link {
    transition: var(--transition-smooth);
    font-weight: 500;
}

/* Sidebar */
.sidebar {
    position: fixed;
    /* Mobile default: sits below the separate mobile top bar (~72px tall).
       Desktop overrides this to 0 below - the sidebar now owns the "header"
       area itself (see .sidebar-brand) since there's no separate full-width
       navbar above it anymore on desktop. */
    top: 72px;
    bottom: 0;
    left: 0;
    /* Must stay above Bootstrap's own offcanvas backdrop (z-index: 1040) below
       the md breakpoint, where this element acts as an offcanvas drawer -
       otherwise the backdrop sits on top and blocks all interaction with it */
    z-index: 1045;
    padding: 0 0 1.5rem;
    overflow-x: hidden;
    overflow-y: auto;
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(10px);
    border-right: 1px solid rgba(0, 0, 0, 0.05);
    box-shadow: var(--shadow-sm);
}

@media (min-width: 768px) {
    .sidebar {
        top: 0;
    }
}

/* Sidebar Brand (logo) box - desktop only, sits at the top of the sidebar
   in place of the old full-width top navbar's logo. .sidebar itself is the
   scrolling element (overflow-y: auto), so without position: sticky here
   the logo scrolls away with the nav links once they overflow the viewport. */
.sidebar-brand {
    height: 96px;
    padding: 0 1rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
    flex-shrink: 0;
    position: sticky;
    top: 0;
    z-index: 2;
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(10px);
}

#sidebar.sidebar-collapsed .sidebar-brand strong {
    display: none;
}

#sidebar.sidebar-collapsed .sidebar-brand img {
    max-height: 36px;
}

/* Desktop Top Bar (collapse toggle, search, notifications, profile) - stays
   pinned to the top of the viewport while the page content scrolls beneath it.
   Full-bleeds into <main>'s side padding and carries a real shadow so it
   still reads as a distinct header bar once the page title has scrolled
   past, instead of blending into the white page background. */
/* Mobile Top Bar (hamburger, logo, account menu) - same reasoning as
   .desktop-topbar below, but that rule is scoped to itself (d-md-none
   already hides this on desktop) so it needed its own class rather than
   just reusing .desktop-topbar's. Without this it scrolled away with the
   page like ordinary content instead of staying reachable. */
.mobile-topbar {
    position: sticky;
    top: 0;
    z-index: 100;
}

.desktop-topbar {
    position: sticky;
    top: 0;
    /* Must outrank .card:hover's z-index: 20 (its hover-lift effect) - any
       card scrolled under this bar would otherwise cover it on hover, since
       20 > this bar's old z-index of 10. */
    z-index: 100;
    background: var(--bg-surface-1, #fff);
    padding: 1rem 1.5rem;
    margin: 0 -1.5rem 1rem;
    box-shadow: 0 2px 10px rgba(154, 135, 49, 0.12);
}

/* intl-tel-input wraps the phone <input> in a ".iti" box that's
   inline-block/shrink-to-fit by default, so it renders narrower than a
   plain .form-control sibling (e.g. the email field) in the same row. */
.iti {
    display: block;
    width: 100%;
}

.sidebar::-webkit-scrollbar {
    width: 6px;
}

.sidebar::-webkit-scrollbar-track {
    background: transparent;
}

.sidebar::-webkit-scrollbar-thumb {
    background: var(--gray-300);
    border-radius: 10px;
}

.sidebar .nav-link {
    color: var(--text-secondary);
    padding: 0.875rem 1.5rem;
    font-weight: 500;
    font-size: 0.9375rem;
    border-radius: 0;
    margin: 0.125rem 0.75rem;
    border-radius: var(--radius);
    transition: var(--transition-smooth);
    position: relative;
    display: flex;
    align-items: center;
}

.sidebar .nav-link:hover {
    background-color: rgba(154, 135, 49, 0.1);
    color: var(--secondary-color);
    transform: translateX(2px);
}

.sidebar .nav-link.active {
    background: linear-gradient(90deg, rgba(0, 161, 153, 0.1) 0%, rgba(0, 161, 153, 0.05) 100%);
    color: var(--primary-color);
    font-weight: 600;
}

.sidebar .nav-link i {
    margin-right: 0.875rem;
    font-size: 1.125rem;
    width: 20px;
    text-align: center;
    transition: var(--transition-base);
}

.sidebar .nav-link:hover i {
    transform: scale(1.1);
    color: var(--secondary-color);
}

.sidebar .nav-link.active i {
    transform: scale(1.1);
    color: var(--primary-color);
}

/* ============================================
   STATS CARDS - ArchitectUI Style
   ============================================ */
.stats-card {
    transition: var(--transition-smooth);
    border-radius: var(--radius-lg);
    overflow: hidden;
    position: relative;
}

.stats-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--primary-gradient);
    opacity: 0;
    transition: var(--transition-smooth);
}

.stats-card:hover {
    position: relative;
    z-index: 20;
    transform: translateY(-8px) scale(1.02);
    box-shadow: var(--shadow-xl) !important;
}

.stats-card:hover::before {
    opacity: 1;
}

.stats-icon {
    width: 64px;
    height: 64px;
    border-radius: var(--radius-lg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    background: var(--primary-gradient);
    box-shadow: var(--shadow-md);
    transition: var(--transition-bounce);
}

/* "Needs Your Attention" cards: quiet by default, only draw the eye when
   there's actually something to act on (count > 0). */
.action-card {
    border-radius: var(--radius-lg);
    transition: var(--transition-base);
}

.action-card:hover {
    position: relative;
    z-index: 20;
    transform: translateY(-3px);
    box-shadow: var(--shadow-md) !important;
}

.action-card-active {
    background: var(--warning-soft, rgba(245, 158, 11, 0.06));
}

.stats-card:hover .stats-icon {
    transform: rotateY(180deg) scale(1.1);
}

.stats-card h3 {
    font-size: 2rem;
    font-weight: 700;
    margin: 0.5rem 0;
    background: var(--primary-gradient);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* Avatar Placeholder */
.avatar-placeholder {
    width: 32px;
    height: 32px;
    background: var(--primary-gradient);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    font-size: 14px;
}

/* Initial-letter avatar used across student/tutor/family lists.
   Default size is overridden inline where a page needs a different size. */
.avatar-circle {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
}

/* ============================================
   CARDS - ArchitectUI Style
   ============================================ */
.card {
    border-radius: var(--radius-xl);
    margin-bottom: 2rem;
    border: none;
    box-shadow: var(--shadow-md);
    transition: var(--transition-smooth);
    background: var(--bg-surface-1);
    border: 1px solid rgba(0, 0, 0, 0.03);
}

.card:hover {
    position: relative;
    z-index: 20;
    box-shadow: var(--shadow-premium);
    transform: translateY(-5px);
}

/* Keeps a row/card above its neighbors for as long as its actions dropdown
   is open (see includes/footer.php show.bs.dropdown/hidden.bs.dropdown
   handler) - covers touch and keyboard-triggered dropdowns, which never
   match :hover.
   transform is also forced off: several :hover rules across the app
   (.card, table rows, .family-list-row) apply a `transform` for a lift
   effect, and a `transform` on ANY ancestor creates a new containing block
   for position:fixed descendants per the CSS spec - which breaks
   initFixedPositionDropdowns() (footer.php), since the cursor is
   necessarily hovering this exact row/card while clicking its own dropdown
   toggle, and Popper's viewport-relative math then gets computed against
   the wrong (transformed) containing block instead.
   Deliberately unconditional, not scoped to :hover (tried that first) -
   transform: translateX/Y shifts the element's own hit-box out from under
   a stationary cursor, so the moment a :hover-scoped override applied,
   :hover could drop out, un-applying the override, snapping the transform
   back, re-entering :hover, and repeating - a rapid flicker loop.
   Unconditional + !important applies regardless of hover state, so there's
   nothing left to oscillate. */
.dropdown-open {
    position: relative;
    z-index: 20;
    transform: none !important;
}

/* Lets a row's actions dropdown escape .table-responsive's horizontal-scroll
   clipping for as long as it's open (see includes/footer.php
   show.bs.dropdown/hidden.bs.dropdown handler) - overflow-x: auto also
   clips overflow-y, cutting the dropdown off at the table's bottom edge,
   most noticeably when the table only has one or two rows. */
.table-responsive-dropdown-open {
    overflow: visible;
}

.card-header {
    border-radius: var(--radius-lg) var(--radius-lg) 0 0 !important;
    padding: 1.25rem 1.5rem;
    background-color: var(--white);
    border-bottom: 1px solid var(--gray-200);
    font-weight: 600;
}

.card-body {
    padding: 1.5rem;
}

.card-footer {
    padding: 1.25rem 1.5rem;
    background-color: var(--gray-100);
    border-top: 1px solid var(--gray-200);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

/* ============================================
   BUTTONS - ArchitectUI Style
   ============================================ */
.btn {
    border-radius: var(--radius);
    padding: 0.75rem 1.5rem;
    font-weight: 600;
    font-size: 0.875rem;
    transition: var(--transition-smooth);
    border: none;
    letter-spacing: 0.03em;
    box-shadow: var(--shadow-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

.btn:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-md);
}

.btn:active {
    transform: translateY(-1px);
}

.btn-sm {
    padding: 0.45rem 1rem;
    font-size: 0.8125rem;
}

.btn-lg {
    padding: 0.875rem 1.75rem;
    font-size: 1rem;
}

.btn-primary {
    background: var(--primary-gradient);
    border: none;
    color: white;
}

.btn-primary:hover {
    box-shadow: var(--shadow-hover);
    opacity: 0.95;
}

.btn-success {
    background: var(--success-gradient);
    color: white;
    box-shadow: 0 2px 8px rgba(0, 161, 153, 0.3);
}

.btn-danger {
    background: var(--danger-gradient);
    color: white;
    box-shadow: 0 2px 8px rgba(240, 83, 105, 0.3);
}

.btn-warning {
    background: var(--warning-gradient);
    color: white;
    box-shadow: 0 2px 8px rgba(154, 135, 49, 0.3);
}

.btn-info {
    background: var(--info-gradient);
    color: white;
    box-shadow: 0 2px 8px rgba(0, 196, 184, 0.3);
}

.btn-outline-primary {
    border: 2px solid var(--primary-color);
    color: var(--primary-color);
    background: transparent;
}

/* Outline / non-colored buttons: no fill on hover, just the secondary
   (gold) accent - kept distinct from filled buttons, which stay white. */
.btn-outline-primary:hover,
.btn-outline-secondary:hover,
.btn-outline-success:hover,
.btn-outline-danger:hover,
.btn-outline-warning:hover,
.btn-outline-info:hover {
    background: transparent !important;
    color: var(--secondary-color) !important;
    border-color: var(--secondary-color) !important;
}

/* ============================================
   BADGES - ArchitectUI Style
   ============================================ */
.badge {
    border-radius: var(--radius);
    padding: 0.375rem 0.75rem;
    font-weight: 600;
    font-size: 0.75rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    /* Bootstrap's own .badge forces white-space: nowrap, meant for short
       fixed labels - but several pages put dynamic, sometimes-long text in
       one (curriculum level names, etc.), which then can't shrink below its
       unwrapped width and pushes past the screen edge on mobile instead of
       wrapping. Short badges are unaffected since there's nothing to wrap. */
    white-space: normal;
    max-width: 100%;
}

.badge-success {
    background: var(--success-gradient);
    box-shadow: 0 2px 8px rgba(22, 163, 74, 0.3);
}

.badge-danger {
    background: linear-gradient(135deg, #f05369 0%, #e73827 100%);
    box-shadow: 0 2px 8px rgba(240, 83, 105, 0.3);
}

.badge-warning {
    background: var(--warning-gradient);
    box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}

.badge-info {
    background: var(--info-gradient);
    box-shadow: 0 2px 8px rgba(14, 165, 233, 0.3);
}

.badge-primary {
    background: var(--primary-gradient);
    box-shadow: 0 2px 8px rgba(0, 161, 153, 0.3);
}

/* ============================================
   TABLES - ArchitectUI Style
   ============================================ */
.table {
    border-radius: var(--radius-lg);
    overflow: hidden;
    font-size: 0.875rem;
}

.table thead th {
    background: linear-gradient(135deg, rgba(0, 161, 153, 0.05) 0%, rgba(0, 196, 184, 0.05) 100%);
    border-bottom: 2px solid var(--gray-300);
    font-weight: 700;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.08em;
    padding: 1rem;
    color: var(--gray-700);
}

.table tbody tr {
    transition: var(--transition-smooth);
    border-bottom: 1px solid var(--gray-200);
}

.table tbody tr:hover {
    position: relative;
    z-index: 20;
    background: linear-gradient(135deg, rgba(0, 161, 153, 0.03) 0%, rgba(0, 196, 184, 0.03) 100%);
    /* No transform here (previously translateX(4px)) - shifting a row's
       geometry on :hover can move it out from under a stationary cursor,
       which drops :hover, snaps the shift back, re-enters :hover, and
       repeats - a flicker most noticeable right at the edge of a small
       target like a row's own Actions button. It also broke
       position:fixed dropdown positioning while hovered (see
       .dropdown-open above) since transform creates a new containing
       block for fixed descendants. */
    box-shadow: var(--shadow-sm);
}

.table tbody td {
    padding: 1rem;
    vertical-align: middle;
    /* Emails, long names, and IDs shown in a plain (non table-responsive)
       detail table have no natural break point for the browser to wrap
       on, so on a narrow phone screen the cell - and the whole table -
       gets forced wider than its card, spilling text out past the card's
       edge instead of wrapping within it. */
    overflow-wrap: anywhere;
}

/* The label column of a key/value info table (Email:, Phone:, Rate:, etc.
   in the tutor/family/staff card layouts) is always a short, static word,
   and the value next to it is whatever's actually long (an email, a name).
   A max-width:0 + width:100% pairing on the value column (tried first)
   turned out not to be a hard enough constraint: since that cell also
   needs white-space: nowrap for the ellipsis to work, the browser's
   default (auto) table layout can still let its full unwrapped text
   width push the whole table wider than the card - overflow:hidden then
   only clips the rendering inside that already-oversized box, so the
   text visibly spills out past the card's rounded edge instead of
   showing "...".
   table-layout: fixed is what actually enforces a hard width: the label
   column gets a fixed px width (generous enough for the longest label
   across every table this rule touches, "Monthly Salary:" on the staff
   cards) via a CSS class rule that applies identically to every row, so
   it isn't subject to the usual "only the first row's cells decide
   fixed-layout's column widths" gotcha - every row already agrees on
   that same width. The value column then gets whatever's left, and
   overflow:hidden + ellipsis can only ever clip within that real,
   enforced box, not visually leak past it. */
.table-borderless {
    table-layout: fixed;
    width: 100%;
}

.table-borderless td.text-muted {
    white-space: nowrap;
    width: 145px;
}

.table-borderless td:not(.text-muted) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A badge is just as capable of overflowing its now-narrow value column as
   plain text is - without this it can wrap into a vertical one-letter-per-
   line stack instead of staying on one line and getting clipped like
   everything else in the column. */
.table-borderless td:not(.text-muted) .badge {
    white-space: nowrap;
}

/* The 145px reserved for the label column above is generous enough for
   "Monthly Salary:" on a spacious desktop card, but on an actual phone-
   width card it eats so much of the total width that values are left
   with almost nothing - "arabicrwaq@..." truncating down to just "ara...".
   Every one of these labels pairs with an icon that's already meaningful
   on its own (envelope, phone, flag, calendar) - past a certain page
   width, the icon carries the label and the text next to it becomes a
   luxury the value column needs more than the label does. label-text
   spans (added alongside the icon in every .text-muted cell) let this
   collapse to icon-only without touching each page's markup further. */
@media (max-width: 480px) {
    .student-card .table-borderless td.text-muted {
        width: 28px;
        text-align: center;
    }

    .student-card .table-borderless td.text-muted i {
        margin-right: 0 !important;
    }

    .student-card .table-borderless td.text-muted .label-text {
        display: none;
    }
}

/* Visual polish for the shared "profile summary card" pattern (avatar +
   name + key/value table-borderless list + centered actions) used across
   the tutor, family, student, and staff mobile card layouts - all tagged
   .student-card. Purely cosmetic on top of the overflow/truncation fixes
   above: a soft ring around the avatar, a hairline divider between rows
   instead of them running together, a touch of brand color on the label
   icons, and slightly heavier weight on the value so it reads as "the
   answer" against the muted label. */
.student-card .avatar-circle {
    box-shadow: 0 0 0 4px var(--primary-soft);
}

.student-card .table-borderless tr:not(:last-child) td {
    border-bottom: 1px solid var(--gray-200);
}

.student-card .table-borderless td {
    padding-top: 0.65rem;
    padding-bottom: 0.65rem;
}

.student-card .table-borderless td.text-muted i {
    color: var(--primary-color);
    opacity: 0.75;
    width: 1.1em;
    display: inline-block;
}

.student-card .table-borderless td:not(.text-muted) {
    font-weight: 500;
}

/* Consolidated here from three separate page-local <style> blocks
   (admin/students.php, tutor/students.php, admin/families.php) that had
   each copy-pasted the identical rule - admin/tutors.php and the staff
   cards used the same .student-card class without it, so they never got
   the lift-on-hover treatment their siblings did. */
.student-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .15) !important;
    transition: all 0.2s;
}

/* .table-responsive tables are meant to overflow and scroll horizontally
   on a narrow screen, not squeeze their columns to fit - but
   overflow-wrap: anywhere above explicitly permits the browser's table
   auto-layout to shrink a column below its natural min-content width
   instead of triggering that scrollbar, which is what turned short
   labels and descriptions (e.g. "When you receive a new message" in the
   Notification Types matrix, or the Staff/Roles tables) into an
   unreadable one-character-per-line stack instead of just scrolling.
   Restoring normal wrapping here lets the table push wider than its
   container again so .table-responsive's own overflow-x: auto takes
   over, matching how it already behaves everywhere overflow-wrap:
   anywhere isn't fighting it. */
.table-responsive .table tbody td {
    overflow-wrap: normal;
}

.table-hover tbody tr:hover {
    background: linear-gradient(90deg, rgba(0, 161, 153, 0.05) 0%, transparent 100%);
}

.table-striped tbody tr:nth-of-type(odd) {
    background-color: rgba(0, 0, 0, 0.02);
}

/* ============================================
   FORMS - ArchitectUI Style
   ============================================ */
.form-control,
.form-select {
    border-radius: var(--radius);
    border: 2px solid var(--gray-300);
    padding: 0.625rem 1rem;
    transition: var(--transition-smooth);
    font-size: 0.875rem;
    background-color: var(--white);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 4px rgba(0, 161, 153, 0.1);
    background-color: var(--white);
    outline: none;
}

.form-control:hover,
.form-select:hover {
    border-color: var(--primary-light);
}

.form-label {
    font-weight: 600;
    color: var(--gray-700);
    margin-bottom: 0.5rem;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
}

.form-text {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 0.375rem;
}

/* Input Groups */
.input-group {
    box-shadow: var(--shadow-sm);
    border-radius: var(--radius);
}

.input-group-text {
    background: var(--gray-100);
    border: 2px solid var(--gray-300);
    border-right: none;
    color: var(--gray-600);
    font-weight: 500;
}

/* Notifications Dropdown */
.notification-dropdown {
    max-height: 400px;
    overflow-y: auto;
    border-radius: 12px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15);
}

/* Keep a per-row "Actions" dropdown from being clipped by the horizontal
   scroll of its table-responsive wrapper, without disabling that scroll
   the rest of the time (needed for wide tables on small screens). */
.table-responsive:has(.dropdown-menu.show) {
    overflow: visible;
}

.dropdown-menu {
    z-index: 1050;
}

.notification-dropdown .dropdown-item {
    padding: 12px 20px;
    border-bottom: 1px solid #f3f4f6;
}

.notification-dropdown .dropdown-item:hover {
    background-color: #f9fafb;
}

/* List Groups */
.list-group-item {
    border: none;
    border-bottom: 1px solid #f3f4f6;
    padding: 15px 20px;
}

.list-group-item:last-child {
    border-bottom: none;
}

/* Messages inbox pane - scrolls internally instead of growing the whole
   page, so the search bar/tabs/pagination stay in view while browsing a
   long list. Height adapts to viewport rather than a fixed pixel value. */
.messages-scroll-pane {
    max-height: calc(100vh - 320px);
    min-height: 300px;
    overflow-y: auto;
}

/* Messages filter row - forces the search box and date pickers to the same
   height/padding so they read as one group instead of mismatched controls
   (native date inputs render slightly differently than plain text inputs). */
.messages-filter-row .input-group-sm > .form-control {
    height: calc(1.5em + 0.5rem + 2px);
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}

/* ============================================
   ALERTS - ArchitectUI Style
   ============================================ */
.alert {
    border-radius: var(--radius-lg);
    border: none;
    padding: 1rem 1.25rem;
    box-shadow: var(--shadow-sm);
    font-weight: 500;
}

.alert-success {
    background: linear-gradient(135deg, rgba(22, 163, 74, 0.1) 0%, rgba(34, 197, 94, 0.05) 100%);
    color: #15803d;
}

.alert-danger {
    background: linear-gradient(135deg, rgba(217, 37, 80, 0.1) 0%, rgba(240, 83, 105, 0.05) 100%);
    color: #a41e34;
}

.alert-warning {
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(251, 191, 36, 0.05) 100%);
    color: #92400e;
}

.alert-info {
    background: linear-gradient(135deg, rgba(14, 165, 233, 0.1) 0%, rgba(56, 189, 248, 0.05) 100%);
    color: #0369a1;
}

.alert i {
    margin-right: 0.75rem;
    font-size: 1.125rem;
}

/* ============================================
   MODALS - ArchitectUI Style
   ============================================ */
.modal-content {
    border-radius: var(--radius-xl);
    border: none;
    box-shadow: var(--shadow-xl);
}

.modal-header {
    padding: 1.5rem;
    border-bottom: 2px solid var(--gray-200);
    background: linear-gradient(135deg, rgba(0, 161, 153, 0.05) 0%, rgba(0, 196, 184, 0.05) 100%);
}

.modal-title {
    font-weight: 700;
    color: var(--gray-900);
}

.modal-body {
    padding: 1.5rem;
}

.modal-footer {
    padding: 1.25rem 1.5rem;
    border-top: 2px solid var(--gray-200);
    background-color: var(--gray-100);
}

/* Footer */
.footer {
    margin-top: 2rem;
    width: 100%;
    clear: both;
    position: relative;
    z-index: 1;
    margin-left: 0;
    background: var(--gray-100);
    border-top: 1px solid var(--gray-300);
}

.footer-links a {
    color: var(--text-secondary);
    text-decoration: none;
    margin: 0 0.75rem;
    font-size: 0.875rem;
    transition: var(--transition-base);
}

.footer-links a:hover {
    color: var(--secondary-color);
}

/* Bottom breathing room before the footer (the actual sticky-footer
   mechanism now lives on body/body>.container-fluid below - this used to
   also carry a min-height: calc(100vh - 200px) guess to "push" the footer
   down, which instead stranded it mid-page with a large blank gap on any
   short-content view, since it forced main tall regardless of its
   sidebar's real height or the viewport's actual size). */
main.col-md-9 {
    margin-bottom: 2rem;
}

/* Fix container to account for fixed sidebar */
@media (min-width: 768px) {
    .container-fluid>.row {
        margin-left: 0;
    }
}

/* Responsive */
@media (max-width: 768px) {
    /* .sidebar keeps its fixed positioning below md - it's now a Bootstrap
       offcanvas drawer (see includes/header.php) and needs position:fixed
       for the slide-in/out transform to work. Its top offset (72px) is set
       once in the base .sidebar rule above, matching both bars' heights. */
    main {
        padding-top: 20px;
    }
}

/* Loading Spinner */
.spinner-border-sm {
    width: 1rem;
    height: 1rem;
    border-width: 0.15em;
}

/* ============================================
   ANIMATIONS - ArchitectUI Style
   ============================================ */
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(20px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideInRight {
    from {
        opacity: 0;
        transform: translateX(30px);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes scaleIn {
    from {
        opacity: 0;
        transform: scale(0.9);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes pulse {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.7;
    }
}

.fade-in {
    animation: fadeIn 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.slide-in-right {
    animation: slideInRight 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}

.scale-in {
    animation: scaleIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ============================================
   BRAND UNIFICATION & BOOTSTRAP OVERRIDES
   ============================================ */

/* Primary Overrides */
.bg-primary,
.badge.bg-primary,
.btn-primary {
    background: var(--primary-gradient) !important;
    border: none !important;
    color: var(--text-white) !important;
}

.btn-primary:hover {
    background: var(--primary-gradient) !important;
    box-shadow: var(--shadow-primary), 0 0 15px var(--secondary-soft) !important;
    transform: translateY(-3px);
}

.text-primary {
    color: var(--primary-color) !important;
}

.border-primary {
    border-color: var(--primary-color) !important;
}

/* Unread message row highlight - deliberately uses the gold secondary
   accent (not primary teal, which already carries nav/active/brand meaning
   everywhere else) so "unread" reads as its own distinct signal. Also uses
   the pre-defined soft tint since .bg-primary above is overridden with a
   solid gradient for buttons/badges, which silently defeats Bootstrap's
   bg-opacity-* utilities. */
.message-item.is-unread {
    background: var(--secondary-soft);
}

.badge-unread {
    background: var(--secondary-color) !important;
    color: #fff !important;
}

.is-unread-notification {
    background: var(--secondary-soft);
}

/* Prevents the flex item's intrinsic content width from blocking
   text-truncate - without this, a long subject/preview line refuses to
   shrink and pushes the date/badges/action icons past the card's edge
   instead of ellipsizing. */
.message-item .flex-grow-1 {
    min-width: 0;
}

/* Dropdown Menu Refinements */
.dropdown-menu {
    border: none;
    box-shadow: var(--shadow-lg);
    border-radius: var(--radius-lg);
    padding: 0.5rem;
}

.dropdown-item {
    border-radius: var(--radius);
    padding: 0.625rem 1rem;
    font-weight: 500;
    transition: var(--transition-smooth);
}

.dropdown-item:hover {
    background-color: var(--primary-soft) !important;
    color: var(--secondary-color) !important;
}

.dropdown-item i {
    transition: var(--transition-smooth);
}

.dropdown-item:hover i {
    color: var(--secondary-color);
    transform: translateX(3px);
}

/* Links */
a {
    color: var(--primary-color);
}

a:hover {
    color: var(--secondary-color);
}

/* Success, Info, Warning Overrides */
.btn-success,
.badge.bg-success {
    background: var(--success-color) !important;
    border: none !important;
}

.btn-success {
    color: white !important;
}

.btn-info,
.badge.bg-info {
    background: var(--info-color) !important;
    border: none !important;
}

.btn-info {
    color: white !important;
}

.btn-warning,
.badge.bg-warning {
    background: var(--warning-color) !important;
    border: none !important;
    color: white !important;
}

/* Tables & Progress */
.table-primary {
    background-color: var(--primary-soft);
}

.progress-bar {
    background: var(--primary-gradient) !important;
}

/* ============================================
   TABS & PILLS - Luxury Branding
   ============================================ */
.nav-tabs {
    border-bottom: 2px solid var(--gray-200);
    gap: 0.5rem;
}

.nav-tabs .nav-link {
    border: none;
    color: var(--text-secondary);
    padding: 0.75rem 1.25rem;
    border-radius: var(--radius) var(--radius) 0 0;
    position: relative;
    background: transparent;
}

.nav-tabs .nav-link::after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    width: 0;
    height: 2px;
    background: var(--primary-gradient);
    transition: var(--transition-smooth);
}

.nav-tabs .nav-link:hover {
    color: var(--primary-color);
    background: var(--primary-soft);
}

.nav-tabs .nav-link.active {
    color: var(--primary-color) !important;
    background: transparent !important;
    font-weight: 600;
}

.nav-tabs .nav-link.active::after {
    width: 100%;
}

.nav-pills .nav-link {
    color: var(--text-secondary);
    border-radius: var(--radius);
    padding: 0.6rem 1.25rem;
    margin-right: 0.5rem;
}

.nav-pills .nav-link.active {
    background: var(--primary-gradient) !important;
    color: var(--text-white) !important;
    box-shadow: var(--shadow-primary);
}

.nav-pills .nav-link:hover:not(.active) {
    background: var(--primary-soft);
    color: var(--primary-color);
}

/* ============================================
   ADMIN SETTINGS PAGE
   ============================================ */
.settings-shell {
    display: flex;
    align-items: flex-start;
    gap: 1.5rem;
}

.settings-nav {
    flex: 0 0 260px;
    flex-direction: column;
    position: sticky;
    top: 84px;
    background: var(--bg-surface-1);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 0.75rem 0;
}

.settings-nav .nav-link {
    color: var(--text-secondary);
    padding: 0.75rem 1.25rem;
    margin: 0.125rem 0.75rem;
    font-weight: 500;
    font-size: 0.9375rem;
    border-radius: var(--radius);
    transition: var(--transition-smooth);
    position: relative;
    display: flex;
    align-items: center;
}

.settings-nav .nav-link:hover {
    background-color: rgba(154, 135, 49, 0.1);
    color: var(--secondary-color);
    transform: translateX(2px);
}

.settings-nav .nav-link.active {
    background: linear-gradient(90deg, rgba(0, 161, 153, 0.1) 0%, rgba(0, 161, 153, 0.05) 100%);
    color: var(--primary-color);
    font-weight: 600;
}

.settings-nav .nav-link i {
    margin-right: 0.75rem;
    font-size: 1.05rem;
    width: 20px;
    text-align: center;
    transition: var(--transition-base);
}

.settings-nav .nav-link:hover i,
.settings-nav .nav-link.active i {
    transform: scale(1.1);
}

.settings-nav-divider {
    margin: 0.25rem 1.25rem;
    border-color: rgba(0, 0, 0, 0.06);
}

.settings-content {
    flex: 1;
    min-width: 0;
}

.settings-content .tab-pane.fade.show {
    animation: fadeIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}

/* The settings-nav sidebar already narrows .settings-content, so the
   Bootstrap col-lg-8/col-lg-10 mx-auto wrappers used for form-width tabs
   would otherwise apply a second percentage-based cap on top of that and
   over-shrink on laptop-width screens. Cap with a fixed max-width instead
   so it stops narrowing once the column is already reasonably sized. */
@media (min-width: 992px) {
    .settings-content .col-lg-8,
    .settings-content .col-lg-10 {
        width: 100%;
        max-width: 760px;
    }
}

.settings-icon-badge {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--primary-soft);
    color: var(--primary-color);
    font-size: 1.25rem;
}

.settings-icon-badge.lg {
    width: 52px;
    height: 52px;
    font-size: 1.5rem;
}

@media (max-width: 991.98px) {
    .settings-shell {
        flex-direction: column;
        align-items: stretch;
    }

    /* Was a horizontal swipeable scroll row on mobile - switched to a plain
       vertical stacked list (full-width rows, like the Messages page's own
       nav-pills sidebar) since its hidden scrollbar made the swipe gesture
       easy to miss, and it hid tabs off-screen rather than showing them
       all at a glance. */
    .settings-nav {
        position: static;
        width: 100%;
        flex: 0 0 auto;
        top: auto;
        padding: 0.5rem;
    }
}

/* Admin-authored lesson content (Quill-produced HTML, rendered raw) - keep
   inline images/videos from overflowing their card on narrow screens. */
.lesson-rich-text img {
    max-width: 100%;
    height: auto;
    border-radius: var(--radius);
}

.lesson-rich-text iframe {
    max-width: 100%;
}

/* Quill applies per-paragraph alignment as classes (ql-align-*), not inline
   styles - those only render correctly where Quill's own stylesheet is
   loaded (the admin editor). Viewer pages (tutor/family/admin
   student-details.php) never load Quill's CSS, so without these rules every
   paragraph silently falls back to the block's dir-based default alignment,
   ignoring whatever the admin actually chose per paragraph. */
.lesson-rich-text .ql-align-left {
    text-align: left;
}

.lesson-rich-text .ql-align-center {
    text-align: center;
}

.lesson-rich-text .ql-align-right {
    text-align: right;
}

.lesson-rich-text .ql-align-justify {
    text-align: justify;
}

/* Fill-in-the-blank gap spans, authored via the lesson text editor's "Gap"
   toolbar button. Pure CSS + an inline onclick baked into the stored HTML
   (see admin/lesson-details.php's GapBlot) - no JS needed on viewer pages. */
.lesson-gap {
    display: inline-block;
    min-width: 3.5em;
    padding: 1px 10px;
    margin: 0 2px;
    border-radius: 4px;
    background: #fff3cd;
    border: 1px dashed #cc9a06;
    color: #fff3cd;
    cursor: pointer;
    user-select: none;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.lesson-gap:hover {
    background: #ffe69c;
}

.lesson-gap.revealed {
    color: #14532d;
    background: #d1e7dd;
    border: 1px solid #198754;
    cursor: default;
}

/* "Table" lesson content block - genuine row/column reference data (see
   CurriculumProgress::renderTableBlock()), styled with the app's brand teal
   to match the rest of the lesson content blocks. */
.lesson-content-table {
    width: 100%;
    border-collapse: collapse;
}

.lesson-content-table th {
    background: var(--primary-gradient);
    color: #fff;
    text-align: left;
    padding: 10px 14px;
    font-weight: 600;
    white-space: nowrap;
}

.lesson-content-table td {
    padding: 10px 14px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    vertical-align: top;
}

.lesson-content-table tbody tr:nth-child(even) {
    background: rgba(0, 161, 153, 0.04);
}

.lesson-content-table[dir="rtl"] th,
.lesson-content-table[dir="rtl"] td {
    text-align: right;
}

/* ============================================
   RICH AESTHETICS & MICRO-ANIMATIONS
   ============================================ */

/* Glass Surfaces */
.glass-panel {
    background: var(--bg-glass);
    backdrop-filter: var(--glass-blur);
    -webkit-backdrop-filter: var(--glass-blur);
    border: var(--glass-border);
    box-shadow: var(--glass-shadow);
}

/* Vibrant Gradients */
.text-gradient-primary {
    background: var(--primary-gradient);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* Enhanced Shadows */
.shadow-premium {
    box-shadow: var(--shadow-premium) !important;
}

.shadow-soft {
    box-shadow: var(--shadow-md) !important;
}

/* Animations */
@keyframes pulse-primary {
    0% {
        box-shadow: 0 0 0 0 rgba(0, 161, 153, 0.4);
    }

    70% {
        box-shadow: 0 0 0 10px rgba(0, 161, 153, 0);
    }

    100% {
        box-shadow: 0 0 0 0 rgba(0, 161, 153, 0);
    }
}

.pulse-primary {
    animation: pulse-primary 2s infinite;
}

.hover-lift {
    transition: var(--transition-smooth);
}

.hover-lift:hover {
    transform: translateY(-5px);
    box-shadow: var(--shadow-lg);
}

/* Custom Scrollbar Premium */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
}

::-webkit-scrollbar-track {
    background: var(--bg-surface-2);
}

::-webkit-scrollbar-thumb {
    background: var(--primary-light);
    border-radius: 10px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--primary-color);
}

/* Dark Mode Sophistication - driven by the explicit account-dropdown toggle
   (data-bs-theme, set server-side in includes/header.php), not just OS
   preference, so it matches Bootstrap 5.3's own native dark-mode attribute. */
[data-bs-theme="dark"] {
    --bg-page: #0f1113;
    --bg-surface-1: #1a1c1e;
    --bg-surface-2: #232629;
    --text-primary: #e1e2e5;
    --text-secondary: #aeb1b6;
    --text-muted: #7a7f85;
    --bg-glass: rgba(26, 28, 30, 0.8);

    /* Invert the grayscale ramp - components below reference these for both
       text (higher numbers = darker in light mode) and borders/backgrounds
       (lower numbers = lighter in light mode), so flipping the ramp once
       here fixes tables, modals, badges etc. without touching each rule. */
    --gray-100: #26282b;
    --gray-200: #2f3236;
    --gray-300: #3a3d42;
    --gray-400: #53575c;
    --gray-500: #74787d;
    --gray-600: #9a9ea3;
    --gray-700: #b7bac0;
    --gray-800: #d3d5d9;
    --gray-900: #eceef0;
}

[data-bs-theme="dark"] .card {
    border: 1px solid rgba(255, 255, 255, 0.05);
}

[data-bs-theme="dark"] .sidebar {
    background: rgba(26, 28, 30, 0.95);
    border-right-color: rgba(255, 255, 255, 0.05);
}

[data-bs-theme="dark"] .sidebar-brand {
    background: rgba(26, 28, 30, 0.95);
    border-bottom-color: rgba(255, 255, 255, 0.05);
}

[data-bs-theme="dark"] .settings-nav {
    background: rgba(26, 28, 30, 0.95);
}

/* Bootstrap's .bg-white/.bg-light/.text-dark utilities are !important and
   hardcode literal white/light-gray/near-black regardless of theme (used
   throughout admin/family/tutor pages for card headers, footer, etc.) -
   they need an explicit, equally-specific override here rather than
   relying on the token system, since they never reference tokens. */
[data-bs-theme="dark"] .bg-white {
    background-color: var(--bg-surface-1) !important;
}

[data-bs-theme="dark"] .bg-light {
    background-color: var(--bg-surface-2) !important;
}

[data-bs-theme="dark"] .text-dark {
    color: var(--text-primary) !important;
}

[data-bs-theme="dark"] .notification-dropdown .dropdown-item,
[data-bs-theme="dark"] .list-group-item {
    border-bottom-color: rgba(255, 255, 255, 0.06);
}

[data-bs-theme="dark"] .notification-dropdown .dropdown-item:hover {
    background-color: rgba(255, 255, 255, 0.04);
}

[data-bs-theme="dark"] .table-striped tbody tr:nth-of-type(odd) {
    background-color: rgba(255, 255, 255, 0.02);
}

/* Alert text colors were tuned for near-white backgrounds - too dark to
   read against the translucent tint they keep on a dark page. */
[data-bs-theme="dark"] .alert-success {
    color: #4ade80;
}

[data-bs-theme="dark"] .alert-danger {
    color: #fb7185;
}

[data-bs-theme="dark"] .alert-warning {
    color: #fbbf24;
}

[data-bs-theme="dark"] .alert-info {
    color: #38bdf8;
}

/* RTL Layout Mirroring - most of the app already runs on Bootstrap's
   logical spacing utilities (ms-/me-/ps-/pe-, offcanvas-start, float-end)
   which auto-flip under dir="rtl" with zero changes (Bootstrap's own RTL
   build, swapped in by includes/header.php, handles all of that). These
   overrides cover this stylesheet's own hardcoded physical left/right
   properties, which don't auto-flip. */
[dir="rtl"] .sidebar {
    left: auto;
    right: 0;
    border-right: none;
    border-left: 1px solid rgba(0, 0, 0, 0.05);
}

/* .row's default negative margin-right (Bootstrap's gutter calc) was never
   neutralized for LTR, since nothing sits at the row's right edge there.
   Now that the sidebar sits at right:0 in RTL, that same un-zeroed negative
   margin pulls the content row in behind it - collapsing the gap between
   the topbar/content and the sidebar. Mirrors the margin-left:0 fix above
   (~line 927) that already exists for the LTR case. */
@media (min-width: 768px) {
    [dir="rtl"] .container-fluid>.row {
        margin-right: 0;
    }
}

/* .container-fluid's own default left gutter padding (Bootstrap's
   --bs-gutter-x, ~0.75rem) was never an issue in LTR since the fixed
   sidebar sits right on top of it at left:0, hiding it completely. In RTL
   the sidebar moves to the right instead, so that same unaddressed padding
   is now exposed on the left as a real gap between the browser edge and
   the header/content - not covered by any fix above, which only touched
   .row's margin, not .container-fluid's own padding. Left only: the
   padding on the sidebar's own (right) side is the small intentional gap
   already confirmed to match the LTR layout's look, and must stay. */
@media (min-width: 768px) {
    [dir="rtl"] body > .container-fluid {
        padding-left: 0;
    }
}

/* main's ms-sm-auto (margin-inline-start: auto) relies on Bootstrap's own
   RTL-aware grid math to push it to the correct side of the fixed-position
   sidebar - that auto-margin calculation doesn't reliably account for the
   sidebar's width once it's taken out of normal flow (position: fixed),
   producing an extra gap between the sidebar and main content that only
   shows up in RTL. Setting the offset explicitly (matching the sidebar's
   own col-md-3/col-lg-2 width exactly) sidesteps that auto-margin
   calculation entirely instead of depending on it working correctly. */
@media (min-width: 768px) {
    [dir="rtl"] main.col-md-9 {
        margin-right: 25%;
        margin-left: 0;
    }
}
@media (min-width: 992px) {
    [dir="rtl"] main.col-lg-10 {
        margin-right: 16.66666667%;
        margin-left: 0;
    }
}

[data-bs-theme="dark"][dir="rtl"] .sidebar {
    border-left-color: rgba(255, 255, 255, 0.05);
}

[dir="rtl"] .sidebar .nav-link i {
    margin-right: 0;
    margin-left: 0.875rem;
}

[dir="rtl"] .alert i {
    margin-right: 0;
    margin-left: 0.75rem;
}

[dir="rtl"] .nav-pills .nav-link {
    margin-right: 0;
    margin-left: 0.5rem;
}

[dir="rtl"] .settings-nav .nav-link i {
    margin-right: 0;
    margin-left: 0.75rem;
}

[dir="rtl"] .nav-tabs .nav-link::after {
    left: auto;
    right: 0;
}

[dir="rtl"] .search-result-icon {
    margin-right: 0;
    margin-left: 0.9rem;
}

[dir="rtl"] .search-result-arrow {
    margin-left: 0;
    margin-right: 0.5rem;
    transform: translateX(3px);
}

[dir="rtl"] .search-result-item:hover .search-result-arrow {
    transform: translateX(0);
}

[dir="rtl"] #sidebar.sidebar-collapsed .nav-link i:first-child {
    margin-right: 0;
    margin-left: 0;
}

/* Google Calendar-style weekly grid blocks - shared by admin/schedules.php,
   family/schedule.php, tutor/schedule.php and admin/tutor-details.php's
   weekly grid. Each event/booking renders as one real rowspan-merged <td>
   spanning its full duration (see buildDayColumnPlan() in public/js/app.js)
   instead of a smear of identically-shaded single-hour cells. The grid
   stays a plain <table>, so column order and borders already flip
   correctly under [dir="rtl"] with no extra rules needed here. */
.gcal-block {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100%;
    border-radius: 0.375rem;
    padding: 0.35rem 0.5rem;
    line-height: 1.3;
}

.gcal-today-col {
    background-color: rgba(13, 110, 253, 0.05);
}

.gcal-now-marker {
    position: absolute;
    left: 0;
    right: 0;
    height: 2px;
    background: #ea4335;
    z-index: 2;
    pointer-events: none;
}

.gcal-now-marker::before {
    content: '';
    position: absolute;
    left: -4px;
    top: -3px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #ea4335;
}