/* =============================================================================
   Modern Custom Styles for ASAS Global System
   Bootstrap 5.3.8 Compatible | RTL Arabic Support | Universal Module Support

   BREAKPOINT STRATEGY (mobile-first):
     Base rules target phones (≤576px). Use @media (min-width: ...) to enhance
     for larger viewports. Bootstrap 5 breakpoints:
       sm ≥ 576px   md ≥ 768px   lg ≥ 992px   xl ≥ 1200px   xxl ≥ 1400px

     Historical max-width queries remain for mobile-only stacking of
     table-based original layouts; new components should prefer min-width.
   ============================================================================= */

/* Fonts are loaded via <link rel="preconnect"> + <link> in _AppLayout.cshtml — faster than an
   @import here (which blocks CSS parsing and serialises the request behind this file's download).
   The --font-primary stack below references them. */

/* CSS Custom Properties for consistent theming */
:root {
    /* Comprehensive Arabic Font Stack - Tajawal primary with modern Arabic fallbacks */
    --font-primary: 'Tajawal', 'Cairo', 'El Messiri', 'Amiri', 'Droid Arabic Kufi', 'Al Bayan', 'Geeza Pro', 'Arabic Typesetting', 'Traditional Arabic', 'Simplified Arabic', 'Tahoma', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', system-ui, sans-serif;

    /* Spacing. --navbar-height must match the ACTUAL rendered navbar: the header's bell/user
       controls carry the 44px touch minimum, so the bar renders ~64px, not the old 56px — a short
       offset left content tucked under the fixed navbar. */
    --navbar-height: 64px;
    --header-height: var(--navbar-height);
    --spacing-xs: 0.25rem;
    --spacing-sm: 0.5rem;
    /* Phone values are the BASE (mobile-first); the @media (min-width: 576px) block below
       enlarges them for sm and up. Was the reverse — a max-width:576px override. */
    --spacing-md: 0.75rem;
    --spacing-lg: 1rem;
    --spacing-xl: 3rem;

    /* Transitions */
    --transition-fast: 0.15s ease;
    --transition-normal: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    --transition-slow: 0.5s ease;

    /* Shadows */
    --shadow-soft: 0 2px 4px -1px rgb(0 0 0 / 0.06), 0 1px 2px -1px rgb(0 0 0 / 0.1);
    --shadow-medium: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
    --shadow-strong: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
    --shadow-glow: 0 0 0 1px rgb(13 110 253 / 0.5);  /* brand var(--asas-primary), paired with outline: var(--bs-primary) */

    /* Border Radius */
    --radius-sm: 0.375rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;

    /* Typography - Tajawal font weights */
    --font-weight-light: 300;
    --font-weight-regular: 400;
    --font-weight-medium: 500;
    --font-weight-semibold: 600;
    --font-weight-bold: 700;
    --line-height-tight: 1.25;

    /* ASAS palette — semantic colour tokens. Defined as raw hex (matching the
       original literals across the codebase) so adopting a token does not change
       any visuals. Future theme work changes one token instead of chasing 200+
       hardcoded hex literals across custom.css and the per-page style blocks.
       Where a token mirrors Bootstrap, the BS variable name is noted for orientation. */

    /* Brand */
    --asas-primary:        #0d6efd;  /* mirrors --bs-primary */
    --asas-primary-strong: #0a58ca;  /* gradient end / link-hover */
    --asas-primary-hover:  #0b5ed7;  /* button-hover */
    --asas-primary-soft:   #f3f7ff;  /* hover/tint surface (no BS equivalent) */

    /* Content links: Bootstrap's default #0d6efd is ~4.5:1 on white — right on the AA line and
       failing at the 14px sizes used for breadcrumbs/inline links. Darken to the -strong blue
       (≈6.4:1) with a darker hover. Navbar links set their own colour, so they're unaffected. */
    --bs-link-color:             #0a58ca;
    --bs-link-color-rgb:         10, 88, 202;
    --bs-link-hover-color:       #084298;
    --bs-link-hover-color-rgb:   8, 66, 152;

    /* Status */
    --asas-success:        #198754;  /* mirrors --bs-success */
    --asas-success-strong: #157347;
    --asas-danger:         #dc3545;  /* mirrors --bs-danger */
    --asas-danger-strong:  #b02a37;
    --asas-danger-darker:  #a71d2a;  /* used by danger gradients/links */
    --asas-warning:        #ffc107;  /* mirrors --bs-warning */
    --asas-warning-strong: #997404;

    /* Surfaces */
    --asas-surface:        #fff;
    --asas-surface-muted:  #f8f9fa;  /* mirrors --bs-tertiary-bg */
    --asas-surface-subtle: #e9ecef;  /* mirrors --bs-secondary-bg */

    /* Borders */
    --asas-border:         #dee2e6;  /* mirrors --bs-border-color */
    --asas-border-soft:    #e9ecef;
    --asas-border-input:   #ced4da;

    /* Text */
    --asas-text:           #212529;  /* mirrors --bs-body-color */
    --asas-text-muted:     #6c757d;  /* mirrors --bs-secondary-color */
    --asas-text-strong:    #495057;  /* form labels, stat values */

    /* Status tints — BS4 alert-subtle values, used by alerts, the wizard's
       complete-step marker, and the submission-success card. Each tint pairs a
       background with an emphasis text. */
    --asas-tint-info-bg:      #cfe2ff;
    --asas-tint-info-text:    #052c65;
    --asas-tint-success-bg:   #d4edda;
    --asas-tint-success-text: #155724;
}

/* Fallback font handled by system fonts in font stack - no external dependencies needed */

/* Base Layout - Apply Tajawal font globally */
body {
    font-family: var(--font-primary);
    padding-top: var(--header-height);
    background-color: var(--asas-surface-muted);
    font-weight: var(--font-weight-regular);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    margin: 0;
    padding-bottom: 0;
}

/* Main grows to push the footer to the viewport bottom on short pages */
main {
    flex: 1 0 auto;
    padding-bottom: 2rem;
}

/* Footer follows content */
footer {
    flex-shrink: 0;
    width: 100%;
    position: relative;
    z-index: 10;
}

/* Focus management for accessibility — only show the ring for keyboard
   navigation. :focus-visible already excludes mouse/programmatic focus, so a
   separate *:focus rule + :not(:focus-visible) reset would be redundant.
   Keep this in sync with the identical first-paint rule in _AppLayout.cshtml's
   inline <style>, which (being later in source order) wins — so the global ring
   is outline-only; no box-shadow glow here or it would be dead anyway. */
*:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

/* =============================================================================
   Login Form Styling Enhancements
   ============================================================================= */

/* Mobile-first: phones already have tight column padding; restore roomy padding on ≥md */
@media (min-width: 768px) {
    .container .col-md-6 {
        padding-left: var(--spacing-lg);
        padding-right: var(--spacing-lg);
    }
}

/* Button width in card */
.card-body .btn {
    width: 100% !important;
    margin-top: var(--spacing-sm);
}

/* Inline buttons next to form controls (e.g. dropdown + "تحديث" rows) keep
   their natural width — the full-width rule above is for centered card
   action buttons, not flex-row companions. */
.card-body .d-flex > .btn,
.card-body .input-group > .btn,
.card-body .btn-toolbar .btn {
    width: auto !important;
    margin-top: 0;
}

/* A button occupying its own grid column (e.g. a search/filter bar's submit
   button in a .row > .col) keeps its full column width but must drop the
   stacked-action margin-top above, or it sits lower than the input sharing
   its row and breaks align-items-center alignment. Covers both a .row nested
   inside .card-body and the combined `class="card-body row"` form. */
.card-body .row > [class*="col"] > .btn,
.card-body.row > [class*="col"] > .btn {
    margin-top: 0;
}

/* =============================================================================
   Component Styles
   ============================================================================= */

/* Enhanced Card Components with modern styling */
.card {
    border-radius: var(--radius-lg);
    border: 1px solid var(--bs-border-color);
    box-shadow: var(--shadow-soft);
    transition: var(--transition-normal);
    overflow: hidden;
}

.card:hover {
    box-shadow: var(--shadow-strong);
    border-color: var(--bs-primary-border-subtle);
}

.card-header {
    font-family: var(--font-primary);
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: 1px solid var(--bs-border-color-translucent);
    font-weight: var(--font-weight-semibold);
    background: linear-gradient(135deg, var(--bs-light), var(--bs-gray-100));
    padding: var(--spacing-lg);
}

/* Flush header — drops the gradient fill and divider (and the bottom padding) so the
   title reads as one piece with the card body. `background: none` is needed because the
   base rule paints a gradient *image*, which .bg-transparent (color only) can't clear. */
.card-header.card-header-flush {
    background: none;
    border-bottom: 0;
    padding-bottom: 0;
}

/* Primary-background card headers — gradient fill with strong white-on-blue
   contrast. This single base rule replaces three separate, scattered
   `.card-header.bg-primary {}` blocks that had accreted over successive
   contrast fixes (background/border + min-height/flex + a lone color reset). */
.card-header.bg-primary {
    background: linear-gradient(135deg, var(--asas-primary), var(--asas-primary-strong)) !important;
    color: #ffffff !important;
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    min-height: 60px;
    display: flex;
    align-items: center;
}

.card-header.bg-primary h1,
.card-header.bg-primary h2,
.card-header.bg-primary h3,
.card-header.bg-primary h4,
.card-header.bg-primary h5,
.card-header.bg-primary h6 {
    color: #ffffff !important;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    margin-bottom: 0;
}

.card-header.bg-primary .card-title {
    color: #ffffff !important;
    font-weight: var(--font-weight-semibold);
}

.card-header.bg-primary i {
    color: #ffffff !important;
    opacity: 0.9;
}

.card-header.bg-primary .bi {
    font-size: 1.2rem;
    filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.2));
}

/* Catch-all: force white on any other element sitting on the solid fill. */
.card-header.bg-primary *:not(.dropdown-menu):not(.dropdown-menu *):not(.dropdown-item):not(.btn):not(.btn *) {
    color: #ffffff !important;
}

/* Fallback for any missed elements on a SOLID primary surface. Three classes
   of element are excluded because they do NOT sit on the solid fill:
     - .dropdown-menu *  → dropdown panels render on their own white background
     - .btn *            → buttons (e.g. btn-light "دخول") carry their own bg/color
     - bg-opacity-*       → a tinted .bg-primary (e.g. message bubbles using
                            `bg-primary bg-opacity-10`) is pale, so white text
                            would vanish; only solid bg-primary gets white text. */
.bg-primary:not([class*="bg-opacity"]) .card-title:not(.dropdown-menu *):not(.btn *),
.bg-primary:not([class*="bg-opacity"]) h1:not(.dropdown-menu *):not(.btn *), .bg-primary:not([class*="bg-opacity"]) h2:not(.dropdown-menu *):not(.btn *), .bg-primary:not([class*="bg-opacity"]) h3:not(.dropdown-menu *):not(.btn *),
.bg-primary:not([class*="bg-opacity"]) h4:not(.dropdown-menu *):not(.btn *), .bg-primary:not([class*="bg-opacity"]) h5:not(.dropdown-menu *):not(.btn *), .bg-primary:not([class*="bg-opacity"]) h6:not(.dropdown-menu *):not(.btn *),
.bg-primary:not([class*="bg-opacity"]) span:not(.dropdown-menu *):not(.btn *),
.bg-primary:not([class*="bg-opacity"]) i:not(.dropdown-menu *):not(.btn *),
.bg-primary:not([class*="bg-opacity"]) small:not(.dropdown-menu *):not(.btn *) {
    color: #ffffff !important;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}

.card-body {
    padding: var(--spacing-lg);
}

.card-footer {
    background-color: var(--asas-surface-muted);
    border-top: 1px solid var(--bs-border-color-translucent);
    padding: var(--spacing-md) var(--spacing-lg);
}

/* Navigation Bar Enhancements */
/* No backdrop-filter: the navbar is a solid .bg-primary surface, so a blur is
   invisible and only costs an extra compositing layer. */
.navbar {
    box-shadow: var(--shadow-medium);
    transition: var(--transition-normal);
}

.navbar-brand {
    font-family: var(--font-primary);
    font-weight: var(--font-weight-semibold);
    font-size: 1.25rem;
    transition: var(--transition-fast);
}

/* Improved navbar toggler */
.navbar-toggler {
    border: none;
    padding: var(--spacing-sm);
    border-radius: var(--radius-md);
    transition: var(--transition-fast);
}

.navbar-toggler:hover,
.navbar-toggler:focus {
    background-color: rgba(255, 255, 255, 0.1);
}

/* Modern Navigation Links */
.navbar-dark .navbar-nav .nav-link {
    color: rgba(255, 255, 255, 0.95) !important;
    font-size: 1rem;
    font-weight: var(--font-weight-medium);
    padding: var(--spacing-sm) var(--spacing-md);
    margin: 0 var(--spacing-xs);
    border-radius: var(--radius-sm);
    transition: var(--transition-fast);
    position: relative;
    overflow: hidden;
}

.navbar-dark .navbar-nav .nav-link::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(255, 255, 255, 0.1);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition-fast);
    z-index: -1;
}

.navbar-dark .navbar-nav .nav-link:hover::before,
.navbar-dark .navbar-nav .nav-link:focus::before {
    transform: scaleX(1);
}

.navbar-dark .navbar-nav .nav-link:hover,
.navbar-dark .navbar-nav .nav-link:focus {
    color: #ffffff !important;
}

.navbar-dark .navbar-nav .nav-link.active {
    color: var(--bs-primary) !important;
    background: #ffffff;
    font-weight: var(--font-weight-semibold);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}

/* On the white active pill the icon must be primary too — the bg-primary
   solid-surface fallback (forces icons to #fff) would otherwise wash it out. */
.navbar-dark .navbar-nav .nav-link.active .bi {
    color: var(--bs-primary) !important;
    text-shadow: none !important;
}

/* Keep the navbar user-dropdown toggle looking at home on a primary navbar,
   including when Bootstrap would otherwise invert btn-outline-light to white */
.navbar-dark .dropdown-toggle.btn-outline-light,
.navbar-dark .dropdown-toggle.btn-outline-light:hover,
.navbar-dark .dropdown-toggle.btn-outline-light:focus,
.navbar-dark .dropdown-toggle.btn-outline-light:active,
.navbar-dark .dropdown-toggle.btn-outline-light.show {
    background-color: rgba(255, 255, 255, 0.12) !important;
    border-color: rgba(255, 255, 255, 0.4) !important;
    color: #ffffff !important;
    box-shadow: none !important;
}

/* Restore dropdown-menu colors inside navbar-dark */
.navbar .dropdown-menu {
    --bs-dropdown-color: #212529;
    --bs-dropdown-link-color: #212529;
    --bs-dropdown-link-hover-color: #1e2125;
    --bs-dropdown-link-hover-bg: #f8f9fa;
    color: var(--asas-text);
}
.navbar .dropdown-menu .dropdown-item {
    color: var(--asas-text) !important;
}
.navbar .dropdown-menu .dropdown-item:hover,
.navbar .dropdown-menu .dropdown-item:focus {
    color: #1e2125 !important;
    background-color: var(--asas-surface-muted);
}
/* Reset span/i/small inside dropdown items — overrides any .bg-primary * rules.
   Exclude .badge so Bootstrap's white-on-colored-bg contrast survives. */
.navbar .dropdown-menu .dropdown-item span:not(.badge),
.navbar .dropdown-menu .dropdown-item i,
.navbar .dropdown-menu .dropdown-item small {
    color: inherit !important;
    text-shadow: none !important;
}

/* Notification-bell unread badge — pinned to the bell's top-right (the free side, away from
   the user menu). Physical `right` is deliberate: the logical start/end utilities get flipped
   by RTLCSS, which parked the badge in the gap between the bell and the user menu. */
.notif-badge {
    top: .2rem;
    right: .2rem;
    font-size: .6rem;
    line-height: 1;
    padding: .25em .4em;
}

/* Fix any regular links in navbar — exclude dropdown-item (white bg) */
.navbar-dark a:not(.nav-link):not(.btn):not(.dropdown-item) {
    color: rgba(255, 255, 255, 0.95) !important;
    text-decoration: none;
    transition: color 0.2s ease;
    padding: 0.5rem 0.75rem;
    border-radius: 0.375rem;
    margin: 0 0.25rem;
}

    .navbar-dark a:not(.nav-link):not(.btn):not(.dropdown-item):hover {
        color: #ffffff !important;
        background-color: rgba(255, 255, 255, 0.1);
        text-decoration: none;
    }

/* Ensure navbar list items without proper nav-item class still look good */
.navbar-dark .navbar-nav li > a:not(.nav-link):not(.dropdown-item) {
    color: rgba(255, 255, 255, 0.95) !important;
    display: block;
    padding: 0.5rem 1rem;
    text-decoration: none;
    border-radius: 0.375rem;
    transition: all 0.2s ease;
}

    .navbar-dark .navbar-nav li > a:not(.nav-link):not(.dropdown-item):hover {
        color: #ffffff !important;
        background-color: rgba(255, 255, 255, 0.1);
    }

/* Navbar action buttons (الرسائل / الحساب / خروج):
   Hover uses a solid dark-blue bg + white text for unambiguous contrast, with
   transform/font-weight locked so the button doesn't resize or jump on hover. */
.navbar-dark .btn-outline-light,
.navbar-dark .btn-light,
.navbar-dark .btn-info {
    font-weight: 500 !important;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.navbar-dark .btn-outline-light:hover,
.navbar-dark .btn-outline-light:focus,
.navbar-dark .btn-outline-light:active,
.navbar-dark .btn-light:hover,
.navbar-dark .btn-light:focus,
.navbar-dark .btn-light:active,
.navbar-dark .btn-info:hover,
.navbar-dark .btn-info:focus,
.navbar-dark .btn-info:active {
    color: #ffffff !important;
    background-color: var(--asas-tint-info-text) !important;
    border-color: var(--asas-tint-info-text) !important;
    transform: none !important;
    box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.15) !important;
}

.navbar-dark .btn-outline-light > .bi,
.navbar-dark .btn-light > .bi,
.navbar-dark .btn-info > .bi {
    color: inherit;
}

.social-links img {
    width: 24px;
    height: 24px;
    margin: 0 4px;
}

/* Form enhancements */
.form-control,
.form-select {
    font-family: var(--font-primary);
    border-radius: var(--radius-sm);
    border: 2px solid var(--bs-border-color);
    transition: var(--transition-fast);
    font-weight: var(--font-weight-regular);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--bs-primary);
    box-shadow: var(--shadow-glow);
}

/* Button enhancements */
.btn {
    font-family: var(--font-primary);
    border-radius: var(--radius-sm);
    font-weight: var(--font-weight-medium);
    padding: var(--spacing-sm) var(--spacing-md);
    transition: var(--transition-fast);
    position: relative;
    overflow: hidden;
}

.btn:hover {
    box-shadow: var(--shadow-medium);
}

/* =============================================================================
   Responsive Design
   ============================================================================= */

/* Kept as a max-width block deliberately. These three rules restyle elements that have NO base
   rule in this file (`.card-header h4`, `.social-links-container`) or whose desktop form comes from
   Bootstrap (`.navbar-nav .nav-link`), so inverting them would mean hardcoding Bootstrap's own
   defaults as "restore" values — e.g. h4's responsive `calc(1.275rem + .3vw)` — which then silently
   drifts when Bootstrap changes. The boundary IS fixed: 767.98px, not 768px (see note below). */
@media (max-width: 767.98px) {
    .card-header h4 {
        font-size: 1.1rem;
    }

    .navbar-nav .nav-link {
        padding: var(--spacing-md) var(--spacing-lg);
        border-radius: 0;
        border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .social-links-container {
        justify-content: center;
        margin-top: var(--spacing-md);
    }
}

/* Enlarge the phone-sized spacing scale from sm up (the base values live in :root).
   The old `.container` padding override that sat beside this was dropped as a proven no-op: it set
   var(--spacing-md) = 0.75rem, which is exactly Bootstrap's own `.container` padding
   (calc(--bs-gutter-x * .5) = 0.75rem, and --bs-gutter-x is not overridden anywhere in this file). */
@media (min-width: 576px) {
    :root {
        --spacing-md: 1rem;
        --spacing-lg: 1.5rem;
    }
}

/* High contrast mode support */
@media (prefers-contrast: high) {
    .card {
        border-width: 2px;
    }
    
    .btn {
        border-width: 2px;
    }
    
    .form-control,
    .form-select {
        border-width: 2px;
    }
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
    :root {
        --transition-fast: 0s;
        --transition-normal: 0s;
        --transition-slow: 0s;
    }
    
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* Shared spin keyframes used by .page-loading-spinner */
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* =============================================================================
   Modern Form Enhancements
   ============================================================================= */

.form-floating {
    position: relative;
}

.form-floating > .form-control:focus ~ label,
.form-floating > .form-control:not(:placeholder-shown) ~ label {
    opacity: 0.65;
    transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

/* :user-invalid / :user-valid (vs :invalid / :valid) so a fresh form
 * doesn't show red on empty required fields before the user has even
 * interacted. Fires only after the user has committed a change to the
 * field. Browser support: Chrome 119+, Firefox 88+, Safari 16.5+. */
.form-control:user-invalid {
    border-color: var(--bs-danger);
    box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}

/* Green "valid" feedback is scoped to :required. A submit attempt (e.g. a
 * بحث / export button on a filter form) flips ALL fields into the user-valid
 * state, which would otherwise paint empty, optional, never-touched inputs
 * (date-range filters, optional text) green. Confirming a required field is
 * satisfied is useful feedback; greening an optional filter is just noise. */
.form-control:required:user-valid {
    border-color: var(--bs-success);
    box-shadow: 0 0 0 0.2rem rgba(25, 135, 84, 0.25);
}

.invalid-feedback {
    display: block;
    width: 100%;
    margin-top: 0.25rem;
    font-size: 0.875em;
    color: var(--bs-danger);
    animation: slideIn var(--transition-fast);
}

.valid-feedback {
    display: block;
    width: 100%;
    margin-top: 0.25rem;
    font-size: 0.875em;
    color: var(--bs-success);
    animation: slideIn var(--transition-fast);
}

@keyframes slideIn {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Custom checkbox and radio styles */
.form-check-input {
    width: 1.25em;
    height: 1.25em;
    margin-top: 0.125em;
    border-radius: var(--radius-sm);
    transition: var(--transition-fast);
}

.form-check-input:checked {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    box-shadow: var(--shadow-glow);
}

/* Shared fadeIn keyframes used by chat UI */
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* =============================================================================
   Tajawal Font Application - Ensure all elements use primary font
   ============================================================================= */

/* Typography - Apply Tajawal to all headings */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-primary);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
}

/* Placeholder text styling — font-family inherits from html; weight/opacity are the intent */
::placeholder {
    font-weight: var(--font-weight-light);
    opacity: 0.7;
}

/* font-family is set once on <html> and inherits everywhere (Bootstrap's Reboot
   already sets `font-family: inherit` on form controls/headings, so no per-element
   re-declaration is needed). An explicit `* { font-family: inherit }` would add
   nothing and would strip the monospace default from <code>/<pre>/<kbd>. */
html {
    font-family: var(--font-primary);
    height: 100%;
}

/* Enhanced Arabic text rendering optimizations */
[lang="ar"], [dir="rtl"], .arabic-text {
    font-family: var(--font-primary);
    text-rendering: optimizeLegibility;
    -webkit-font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
    font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Specific Arabic typography improvements */
.arabic-content {
    line-height: 1.6;
    letter-spacing: 0.01em;
}

/* Ensure minimum content height */
.container.my-4 {
    min-height: calc(100vh - var(--header-height) - 150px);
}

/* =============================================================================
   MOBILE-FIRST UX FOUNDATION
   Additive rules that enforce consistent touch targets, RTL-safe spacing
   utilities, and table-to-stack fallbacks for pre-Bootstrap form markup.
   ============================================================================= */

/* Touch-target minimum — WCAG 2.5.5 / mobile HIG recommend 44px.
   Applies to all .btn and form inputs unless explicitly .btn-sm. */
.btn {
    min-height: 44px;
}

.btn-sm {
    min-height: 36px;
}

.btn-lg {
    min-height: 52px;
}

.form-control,
.form-select {
    min-height: 44px;
}

.form-control-sm,
.form-select-sm {
    min-height: 36px;
}

/* =============================================================================
   SHARED PAGE CHROME — the few cross-page helpers still in use.
   ============================================================================= */

.section-title {
    font-size: 18px;
    font-weight: 600;
    color: var(--asas-text);
    margin-bottom: 20px;
    padding-bottom: 10px;
    border-bottom: 2px solid var(--asas-primary);
}

/* Page header — title + breadcrumb row above the page body. The title icon takes
   the brand colour; the breadcrumb is pushed to the opposite edge via .ms-auto in
   markup. Mobile-safe: the markup row carries flex-wrap so the crumb drops below
   the title on narrow screens. */
.page-header {
    margin-bottom: var(--spacing-lg);
    padding-bottom: var(--spacing-md);
    border-bottom: 1px solid var(--asas-border);
}

.page-header h1 {
    font-size: 1.5rem;
    font-weight: var(--font-weight-semibold);
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.page-header h1 .bi {
    color: var(--asas-primary);
}

.page-header .breadcrumb {
    font-size: 0.875rem;
}

/* Empty-state panel (shared _EmptyState partial): muted icon + message shown when a list/section
   has nothing to render. Centralises the icon size/tone so the "no data" look is uniform — before
   this rule .empty-state-icon was a convention class with no styling (icons rendered tiny). */
.empty-state-icon {
    display: block;
    margin-bottom: var(--spacing-sm);
    font-size: 3rem;
    line-height: 1;
    color: var(--asas-text-muted);
    opacity: 0.7;
}

/* Preserve author/user line breaks (ticket bodies, replies, free-text notes) without an inline
   style attribute — keeps presentation in CSS per the no-inline-style convention. */
.preserve-whitespace {
    white-space: pre-wrap;
}

/* =============================================================================
   TSHKYLAT (teacher formation request) — legacy visual mirror
   Reproduces the look of the old WebForms TshkylatTlb wizard on the new
   /mb/tshkylat[/apply|/rghbat] pages: read-only info grid, score→document
   upload sections, instructions/pledge box, coloured application-status pills,
   the summary card, and the rghbat preference sorter. Presentation only —
   routing, handlers, and field names are unchanged. All colours resolve to the
   --asas-* tokens above (warning/danger tints inlined: no token exists yet).
   ============================================================================= */

/* Section card (old .form-section) — white padded box; pair with .section-title. */
.tshkylat .form-section {
    background: var(--asas-surface);
    border: 1px solid var(--asas-border);
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 20px;
}

/* Read-only basic-data grid (old Step 3). */
.info-item {
    display: flex;
    flex-direction: column;
    padding: 10px 0;
    border-bottom: 1px solid var(--asas-border-soft);
}
.info-label {
    font-size: 13px;
    color: var(--asas-text-muted);
    margin-bottom: 2px;
}
.info-value {
    font-size: 15px;
    font-weight: 600;
    color: var(--asas-text-strong);
}

/* Score → document upload section (old Step 4). */
.upload-section {
    padding: 20px;
    margin-bottom: 20px;
    background: var(--asas-surface-muted);
    border: 1px solid var(--asas-border);
    border-radius: 8px;
}
.upload-label {
    display: block;
    font-weight: 600;
    color: var(--asas-text-strong);
    margin-bottom: 6px;
}
.score-input { max-width: 200px; }
.score-input-row { margin-bottom: 12px; }
.score-helper-text { display: block; margin-top: 4px; }
.score-instructions {
    display: block;
    margin-bottom: 8px;
    line-height: 1.6;
    color: var(--asas-text);
    font-weight: 500;
}
.required-star { color: var(--asas-danger); font-weight: 700; }
.doc-note {
    display: block;
    margin-bottom: 8px;
    line-height: 1.6;
    color: var(--asas-text);
}
.doc-note .bi { color: var(--asas-primary); margin-inline-end: 4px; }

/* Checkbox tile grid — the target-category / doc-requirements pickers on FrshEdit &
   FrshEdaryhEdit. CSS Grid (not flex-wrap) keeps tiles aligned into real columns even when some
   carry a note of different length and others don't. */
.checkbox-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.6rem;
    align-items: start;
}
.checkbox-tile {
    border: 1px solid var(--asas-border);
    border-radius: 0.5rem;
    padding: 0.6rem 0.85rem;
    background: var(--asas-surface);
    transition: border-color var(--transition-fast), background var(--transition-fast);
}
.checkbox-tile:has(.form-check-input:checked) {
    border-color: var(--asas-primary);
    background: var(--asas-primary-soft);
}
.checkbox-tile-all { background: var(--asas-surface-muted); }
.checkbox-tile .form-check { margin-bottom: 0; }
.checkbox-tile-note {
    font-size: 0.8rem;
    color: var(--asas-text-muted);
    margin-inline-start: 1.75rem;
    margin-top: 0.25rem;
    line-height: 1.5;
}

/* Custom Arabic file chooser — the native input is .d-none inside the label button so its
   English "Choose File / No file chosen" never shows; .upload-filename carries the Arabic state. */
.upload-picker { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.upload-filename { font-size: 14px; }

/* Uploaded-file confirmation row. */
.file-info {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 8px;
    color: var(--asas-success);
    font-size: 14px;
}
.file-remove-link {
    color: var(--asas-danger);
    text-decoration: underline;
    cursor: pointer;
    font-size: 13px;
}

/* Instructions + pledge box (old Step 2). */
.instructions-box {
    background: var(--asas-surface-muted);
    padding: 20px;
    border-radius: 6px;
    margin-bottom: 20px;
}
.instructions-list {
    line-height: 2;
    font-size: 15px;
    color: var(--asas-text);
    margin-bottom: 0;
}
.pledge-text { line-height: 1.8; margin-bottom: 15px; }
.agreement-row {
    margin-top: 15px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.agreement-label {
    font-size: 16px;
    font-weight: 600;
    /* 16px/600 is normal text (bold-large starts at 18.66px) → needs 4.5:1; -strong (#0a58ca) clears it. */
    color: var(--asas-primary-strong, #0a58ca);
    cursor: pointer;
    user-select: none;
}

/* Application-status pill (old dashboard). */
.frsh-status-badge {
    display: inline-block;
    padding: 3px 12px;
    border-radius: 12px;
    font-size: 13px;
    font-weight: 600;
}
.frsh-status-new      { background: #fff3cd;                    color: #856404; }
.frsh-status-open     { background: var(--asas-tint-info-bg);   color: var(--asas-tint-info-text); }
.frsh-status-closed   { background: var(--asas-surface-subtle); color: var(--asas-text-muted); }
.frsh-status-rejected { background: #f8d7da;                    color: #842029; }
.frsh-status-accepted { background: var(--asas-tint-success-bg); color: var(--asas-tint-success-text); }
.frsh-status-assigned { background: var(--asas-primary);        color: #fff; }

/* Summary card (old Step 8). */
.summary-section {
    background: var(--asas-surface-muted);
    border: 1px solid var(--asas-border);
    border-radius: 8px;
    padding: 20px;
}
.summary-item {
    display: flex;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--asas-border-soft);
}
.summary-label { font-weight: 600; color: var(--asas-text-muted); min-width: 120px; }
.summary-value { color: var(--asas-text-strong); }

/* Rghbat (school-preference) sorter (old Steps 6–7). */
.ehtyaj-badge {
    background: var(--asas-success);
    color: #fff;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 12px;
    display: inline-block;
}
.legend-row {
    display: flex;
    gap: 20px;
    align-items: center;
    margin-bottom: 12px;
    font-size: 13px;
    color: var(--asas-text-strong);
}
.legend-item { display: flex; align-items: center; gap: 6px; }
.legend-swatch {
    display: inline-block;
    width: 18px;
    height: 18px;
    min-width: 18px;
    border-radius: 3px;
}
.legend-swatch.has-ehtyaj { background-color: var(--asas-tint-success-bg); border: 1px solid #b5ccb5; }
.legend-swatch.no-ehtyaj  { background-color: var(--asas-surface);          border: 1px solid #ccc; }
.rghbat-sort-list { list-style: none; padding: 0; margin: 0; }
.rghbat-sort-item {
    display: flex;
    align-items: center;
    padding: 10px 15px;
    margin-bottom: 6px;
    background: var(--asas-surface-muted);
    border: 1px solid var(--asas-border);
    border-radius: 6px;
    gap: 10px;
}
.rghbat-sort-item:hover { background: var(--asas-surface-subtle); }
.rghbat-sort-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    background: var(--asas-primary);
    color: #fff;
    border-radius: 50%;
    font-weight: 700;
    font-size: 14px;
    flex-shrink: 0;
}
.rghbat-sort-name { flex: 1; font-size: 15px; }
.rghbat-sort-actions { display: flex; gap: 4px; flex-shrink: 0; }
.rghbat-sort-actions button {
    border: none;
    background: transparent;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 4px;
    font-size: 16px;
    line-height: 1;
}
.rghbat-sort-actions button:hover { background: var(--asas-border); }
.rghbat-sort-actions .btn-remove:hover { background: #f8d7da; color: var(--asas-danger); }

/* =============================================================================
   DASHBOARD — KPI cards + tinted stat tiles
   The landing page composes a welcome card, two top KPI cards, and several
   role-gated section cards whose bodies are rows of coloured stat tiles.
   ============================================================================= */

/* Dashboard chrome — moderately tighter than the global card defaults, scoped so other
   pages keep the roomier spacing. */
.dashboard .card { margin-bottom: 1rem; }
.dashboard .card-header { padding: 0.7rem 1rem; }
.dashboard .card-body { padding: 1rem; }

/* Top KPI card: centred icon → number → muted label (inherits .card hover lift). */
.kpi-card { height: 100%; }
.kpi-card .card-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.2rem;
    padding: 1rem 0.85rem;
}
.kpi-icon { font-size: 1.7rem; line-height: 1; margin-bottom: 0.1rem; }
.kpi-num {
    font-size: 1.85rem;
    font-weight: var(--font-weight-bold);
    line-height: 1;
    color: var(--asas-text);
    font-variant-numeric: tabular-nums;
}
.kpi-label { font-size: 0.82rem; color: var(--asas-text-muted); }

/* Tinted stat tile inside a section card. Neutral by default; colour variants below. */
.stat-tile {
    display: block;
    height: 100%;
    border-radius: var(--radius-md);
    padding: 0.9rem 0.5rem;
    text-align: center;
    text-decoration: none;
    background: var(--asas-surface-muted);
    border: 1px solid transparent;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.stat-tile .stat-num {
    font-size: 1.6rem;
    font-weight: var(--font-weight-bold);
    line-height: 1.1;
    color: var(--asas-text);
    font-variant-numeric: tabular-nums;
}
.stat-tile .stat-label { font-size: 0.78rem; color: var(--asas-text-muted); margin-top: 0.15rem; }

/* Only clickable tiles lift on hover (they wrap an <a>). */
a.stat-tile:hover,
a.stat-tile:focus-visible {
    transform: translateY(-2px);
    box-shadow: var(--shadow-medium);
    color: inherit;
}

/* Colour variants — Bootstrap 5.3 subtle palette: subtle background, same-hue subtle
   border, and the darker emphasis token for BOTH the number and the label (label kept
   smaller for hierarchy but in-hue, not greyed). */
.stat-tile-primary { background: color-mix(in srgb, var(--bs-primary-bg-subtle) 40%, #fff); border-color: var(--bs-primary-border-subtle); }
.stat-tile-primary .stat-num, .stat-tile-primary .stat-label { color: var(--bs-primary-text-emphasis); }
.stat-tile-info    { background: color-mix(in srgb, var(--bs-info-bg-subtle) 40%, #fff); border-color: var(--bs-info-border-subtle); }
.stat-tile-info    .stat-num, .stat-tile-info .stat-label { color: var(--bs-info-text-emphasis); }
.stat-tile-success { background: color-mix(in srgb, var(--bs-success-bg-subtle) 40%, #fff); border-color: var(--bs-success-border-subtle); }
.stat-tile-success .stat-num, .stat-tile-success .stat-label { color: var(--bs-success-text-emphasis); }
.stat-tile-warning { background: color-mix(in srgb, var(--bs-warning-bg-subtle) 40%, #fff); border-color: var(--bs-warning-border-subtle); }
.stat-tile-warning .stat-num, .stat-tile-warning .stat-label { color: var(--bs-warning-text-emphasis); }
.stat-tile-danger  { background: color-mix(in srgb, var(--bs-danger-bg-subtle) 40%, #fff); border-color: var(--bs-danger-border-subtle); }
.stat-tile-danger  .stat-num, .stat-tile-danger .stat-label { color: var(--bs-danger-text-emphasis); }

/* Grouped report tables (the Mb rpt-* pages, via _GroupedReport) — compact rows only
   (font + cell padding); background colours left at the .asas-grid / table-secondary
   defaults. Two-class selectors out-specify the base .asas-grid rules. */
.report-table { font-size: 0.82rem; }
.asas-grid.report-table th,
.asas-grid.report-table td { padding: 0.3rem 0.5rem; }

/* Collapsible section header (معلومات المنشأة): the chevron points down when expanded and
   rotates to point up (RTL-neutral) when collapsed, tracking Bootstrap's [aria-expanded] state. */
.collapse-toggle { box-shadow: none; }
.collapse-caret { transition: transform 0.15s ease-in-out; }
.collapse-toggle[aria-expanded="false"] .collapse-caret { transform: rotate(-180deg); }
/* Default-collapsed on screen, but always show collapsed report sections (معلومات المنشأة،
   التشكيلات) on the printout. */
@media print { .report-collapse { display: block !important; } }

/* معلومات المنشأة info panel — tint the pairs block so it reads as one boxed section.
   It sits on a .row, whose negative gutter margins (-.5 * --bs-gutter-x each side) would bleed the
   tinted box past the container edges, out of line with the section headings above it — so cancel
   them. The row's dt/dd already carry the half-gutter (0.75rem) as padding, so the panel adds only
   a small horizontal pad of its own; the two together match the vertical 0.75rem. */
.mnshah-info-panel {
    background: #e3edf7;
    border: 1px solid #c7d9ec;
    border-radius: 0.375rem;
    margin-inline: 0;
    padding: 0.75rem 0.25rem;
}
@media print {
    .mnshah-info-panel { background: #e3edf7 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* Name/label columns (school, establishment, tier) stay on one line so the full name shows;
   the .table-responsive wrapper scrolls horizontally when the table outgrows its container. */
.asas-grid.report-table th.report-label-col,
.asas-grid.report-table td.report-label-col { white-space: nowrap; }

/* Value-based cell shading for signed report cells — shared by the grouped report tables
   (opt-in via GroupedReport.SignColored) and the _MnshahDetail matrices. Graded by magnitude:
   tier 1 (small) → 3 (large). Green = positive (surplus), red = negative (deficit); zero/
   non-numeric cells stay unshaded. The two-class selectors out-specify the base .asas-grid td
   background. print-color-adjust keeps the fills on paper. */
.asas-grid td.cell-pos-1 { background: #eef7ee; }
.asas-grid td.cell-pos-2 { background: #d8eed8; }
.asas-grid td.cell-pos-3 { background: #bfe2bf; }
.asas-grid td.cell-neg-1 { background: #fcecec; }
.asas-grid td.cell-neg-2 { background: #f8d9d9; }
.asas-grid td.cell-neg-3 { background: #f2c4c4; }
.asas-grid td[class*="cell-pos-"],
.asas-grid td[class*="cell-neg-"] {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
}

/* احتياج المدرسة single-school matrices (_MnshahDetail) — the vivid surplus/deficit palette (green/red,
   light→dark), stronger than the grouped reports' pale shading above. Scoped to .matrix-table so ONLY
   these reports change; the grouped sector reports keep the base palette. Tier thresholds live in MnshahDetail.SignKind
   (>1 / >2) and the Excel export mirrors these ARGB in MnshahDetailExport. The light and mid tiers keep the
   inherited body text; the darkest DEFICIT tier does not. #cc3333 under #212529 measures 3.0:1,
   below the 4.5:1 AA floor — and this grid is 0.82rem, so bold weight does not reach WCAG's
   large-text exemption. It flips to #fff (5.1:1). The darkest SURPLUS tier deliberately stays
   black: #33aa33 under #212529 is already 5.1:1, and white on it would fall back to 3.0:1. */
.asas-grid.matrix-table td.cell-pos-1 { background: #cceecc; }
.asas-grid.matrix-table td.cell-pos-2 { background: #99dd99; }
.asas-grid.matrix-table td.cell-pos-3 { background: #33aa33; }
.asas-grid.matrix-table td.cell-neg-1 { background: #ffcccc; }
.asas-grid.matrix-table td.cell-neg-2 { background: #ff9999; }
.asas-grid.matrix-table td.cell-neg-3 { background: #cc3333; color: #fff; }

/* المعلمون roster per-cell highlights: blue = a non-teacher work role on العمل, orange = teaches no
   subject on المادة, red-dark on الحالة + red-light on الاسم for an inactive teacher. Applied per cell
   via cell-<kind>; print-color-adjust keeps them on paper. */
.asas-grid td.cell-blue { background: #cce5ff; }
.asas-grid td.cell-orange { background: #ffd9b3; }
.asas-grid td.cell-red-light { background: #ffcccc; }
/* #fff, not the inherited #212529 — the same 3.0:1 AA failure on #cc3333 as the matrix deficit tier above. */
.asas-grid td.cell-red-dark { background: #cc3333; color: #fff; }
.asas-grid td.cell-blue,
.asas-grid td.cell-orange,
.asas-grid td.cell-red-light,
.asas-grid td.cell-red-dark {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
}

/* Leaflet school-map legend (RptEhtyaj / RptTshkylat / RptTshkylatTtwyr): keys the pin colors
   to their زيادة/عجز tiers. Rendered as an L.control (bottom-right); each dot's fill comes
   inline from the same colorFor() the pins use, so the key can never drift from the map. */
.map-legend {
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid #bbb;
    border-radius: 0.375rem;
    padding: 0.3rem 0.6rem;
    font-size: 0.78rem;
    line-height: 1.9;
}
.map-legend-item { display: block; white-space: nowrap; }
.map-legend-dot {
    display: inline-block;
    width: 0.8rem;
    height: 0.8rem;
    border-radius: 50%;
    border: 1px solid #333;
    vertical-align: -0.1rem;
    margin-inline-end: 0.4rem;
}

/* Synced top horizontal scrollbar for wide tables. The _AppLayout script auto-injects a
   .table-scroll-top before every .table-responsive (no per-page markup), sizes its inner
   spacer to the table width, mirrors scrollLeft between the two bars, and shows it only when
   the table overflows. Carries .no-print so it never prints. */
.table-scroll-top { overflow-x: auto; overflow-y: hidden; }
.table-scroll-top > div { height: 1px; }

/* Bootstrap doesn't set a pointer cursor on form-check labels by default, so
   clicking the label text feels unresponsive. Replaces inline `style="cursor:pointer"`
   on `<label class="form-check-label" for="...">` across the codebase. */
.form-check-label[for] {
    cursor: pointer;
}

/* Anonymous-form captcha image — token-based border/surface (was inline hex
   #ced4da / 6px / #f1f3f5; the first two map exactly to the tokens below). */
.captcha-img {
    border: 1px solid var(--asas-border-input);
    border-radius: var(--radius-sm);
    background: var(--asas-surface-muted);
}

/* Narrow numeric inputs (e.g. the ترتيب / order field in editable grid rows). */
.input-num-narrow {
    width: 70px;
}

/* Sidebar menu panel — used by as_Resalh (messaging) and as_Nshrh (newsletter).
   Responsive pill-bar overrides on ≤768px are further below. */
.rs-menu-section {
    background: var(--asas-surface);
    border-radius: 8px;
    padding: 15px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

/* =============================================================================
   Messaging (Resalh) — chat UI (mobile-first)
   .chat-list-*    : conversation list rows
   .chat-avatar    : circular initials badge, shared with .chat-row
   .chat-thread    : bubble stream
   .chat-bubble-*  : message bubble + meta row
   .chat-compose-* : compose form polish
   ============================================================================= */

.chat-list-card {
    overflow: hidden;
}
.chat-list {
    margin: 0;
    padding: 0;
}
.chat-list li + li {
    border-top: 1px solid var(--bs-gray-200);
}
.chat-list-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    color: inherit;
    text-decoration: none;
    transition: background 0.15s;
}
.chat-list-item:hover,
.chat-list-item:focus-visible {
    background: var(--bs-gray-100);
    color: inherit;
}
.chat-list-item.chat-list-unread {
    background: var(--asas-primary-soft);
}
.chat-list-item.chat-list-unread:hover {
    background: #e8efff;
}
.chat-list-content {
    flex-grow: 1;
    min-width: 0;
}
.chat-list-top {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.15rem;
}
.chat-list-sender {
    font-weight: 500;
    color: var(--bs-body-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex-grow: 1;
    min-width: 0;
}
.chat-list-unread .chat-list-sender {
    font-weight: 700;
}
.chat-list-time {
    flex-shrink: 0;
    font-size: 0.75rem;
    color: var(--bs-gray-600);
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1.1;
}
.chat-list-date {
    font-size: 0.68rem;
    color: var(--bs-gray-500);
    margin-top: 0.1rem;
}
.chat-list-subject {
    font-size: 0.92rem;
    color: var(--bs-gray-700);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-bottom: 0.1rem;
}
.chat-list-unread .chat-list-subject {
    color: var(--bs-body-color);
    font-weight: 600;
}
.chat-list-preview {
    font-size: 0.82rem;
    color: var(--bs-gray-600);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Sender grouping (WhatsApp/Telegram style). A non-collapsed row starts a
 * new sender cluster; a collapsed row continues the current one and hides
 * the avatar + name + recipient-count pill so adjacent threads with the
 * same person stack tightly. The thread-message-count badge inside the
 * subject line stays visible -- it's per-thread, not per-sender. */
.chat-list > li:not(:first-child) .chat-list-item:not(.chat-list-collapsed) {
    border-top: 1px solid var(--bs-border-color, #e9ecef);
}
.chat-list-collapsed {
    padding-top: 0.35rem;
    padding-bottom: 0.35rem;
}
.chat-list-collapsed .chat-avatar { visibility: hidden; }
.chat-list-collapsed .chat-list-top .chat-list-sender,
.chat-list-collapsed .chat-list-top .chat-list-thread-count { display: none; }
.chat-list-unread-dot {
    flex-shrink: 0;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--bs-primary);
    display: inline-block;
}

/* Numeric unread badge — shown on a conversation row when the thread has
   unread messages. Replaces the flat dot once the inbox groups by thread. */
.chat-list-unread-badge {
    flex-shrink: 0;
    min-width: 1.5rem;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    background: var(--bs-primary);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 600;
    text-align: center;
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
    display: inline-block;
}

/* Thread-length counter — subtle grey pill next to the subject when a
   conversation contains more than one message. */
.chat-list-thread-count {
    display: inline-block;
    padding: 0.05rem 0.4rem;
    margin-inline-start: 0.35rem;
    border-radius: 999px;
    background: var(--bs-gray-200);
    color: var(--bs-gray-700);
    font-size: 0.7rem;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
}

/* Circular avatar with initials — shared by list rows and thread bubbles.
   Background is a soft tint so it reads as UI chrome, not a real photo. */
.chat-avatar {
    flex-shrink: 0;
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--asas-tint-info-bg), #9ec5fe);
    color: var(--asas-primary-strong);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.9rem;
    letter-spacing: 0.02em;
}
.chat-row-sent .chat-avatar {
    background: linear-gradient(135deg, #d1e7dd, #a3cfbb);
    color: #0f5132;
}
/* When the user has uploaded a real avatar, an <img class="chat-avatar-img">
   replaces the initials text inside the same .chat-avatar circle. Fill the
   parent and crop with object-fit so portraits, squares, and wide photos all
   land as a tidy circular badge. */
.chat-avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 50%;
    display: block;
}

/* Inline reply-bubble avatar (as_Tthkrh my/view + cp/view). Sized to match
   the surrounding small text so it sits flush with the author name. */
.reply-avatar-img {
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    object-fit: cover;
    vertical-align: middle;
    margin-left: 0.15rem;
    margin-right: 0.15rem;
}

/* Inline staff-list avatar (as_Tthkrh cp/staff). One step larger than the
   reply-bubble variant since the table row is normal-sized text. */
.staff-avatar-img {
    width: 1.8rem;
    height: 1.8rem;
    border-radius: 50%;
    object-fit: cover;
    vertical-align: middle;
}
.staff-avatar-fallback { font-size: 1.6rem; vertical-align: middle; }

/* Account-manage avatar editor: larger preview circle next to the upload
   control. The .avatar-preview shell mirrors .chat-avatar's centering so the
   bi-person-circle fallback icon also lands tidy. */
.avatar-preview {
    width: 5rem;
    height: 5rem;
    border-radius: 50%;
    background: var(--bs-gray-200);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}
.avatar-preview-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Chat thread — vertical scroll area, roomy enough on desktop to
   read a long conversation without the whole page scrolling. */
.chat-panel {
    overflow: hidden;
}
.chat-panel-header .chat-panel-title h2 { line-height: 1.2; }
.chat-thread {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    background: var(--bs-gray-100);
    padding: 1rem;
    max-height: 60vh;
    overflow-y: auto;
    scroll-behavior: smooth;
}
.chat-row {
    display: flex;
    gap: 0.6rem;
    align-items: flex-end;
    max-width: 100%;
}
.chat-row-received {
    justify-content: flex-start;
}
.chat-row-sent {
    justify-content: flex-end;
    flex-direction: row-reverse;
}
.chat-bubble {
    max-width: 85%;
    padding: 0.6rem 0.85rem;
    border-radius: var(--radius-lg);
    background: var(--asas-surface);
    box-shadow: var(--shadow-soft);
    word-wrap: break-word;
    overflow-wrap: anywhere;
}
/* Flat "tail" corner on the edge side. Page is RTL, so sent bubbles sit
   on the left and received on the right — tail corner follows accordingly. */
.chat-row-sent .chat-bubble {
    background: #d1e7dd;
    border-bottom-left-radius: var(--radius-sm);
}
.chat-row-received .chat-bubble {
    border-bottom-right-radius: var(--radius-sm);
}
.chat-bubble-sender {
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--bs-primary);
    margin-bottom: 0.2rem;
}
.chat-row-sent .chat-bubble-sender {
    color: #0f5132;
}
.chat-bubble-body {
    font-size: 0.95rem;
    line-height: 1.55;
    color: var(--bs-body-color);
    white-space: pre-wrap;
}
.chat-bubble-meta {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.4rem;
    margin-top: 0.35rem;
    font-size: 0.7rem;
    color: var(--bs-gray-600);
}
.chat-bubble-time { font-variant-numeric: tabular-nums; }
.chat-bubble-date { opacity: 0.75; }
.chat-bubble-status .bi { font-size: 0.85rem; }

/* Per-bubble action buttons (forward / delete). Hidden by default,
 * fade in on bubble hover (Telegram-style). margin-inline-start:auto
 * pushes them to the trailing edge of the meta row. */
.chat-bubble-actions {
    display: inline-flex;
    gap: 0.15rem;
    margin-inline-start: auto;
    opacity: 0;
    transition: opacity 0.15s;
}
.chat-bubble:hover .chat-bubble-actions,
.chat-bubble:focus-within .chat-bubble-actions { opacity: 1; }
.chat-bubble-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--bs-gray-600);
    border-radius: 50%;
    text-decoration: none;
    line-height: 1;
}
.chat-bubble-action:hover,
.chat-bubble-action:focus-visible {
    background: rgba(0, 0, 0, 0.06);
    color: var(--bs-body-color);
    text-decoration: none;
}
/* Touch devices have no hover, so the hover-reveal pattern above strands
 * the actions invisible. Always-visible there. */
@media (hover: none) {
    .chat-bubble-actions { opacity: 1; }
}

/* Trash review (?trash=1): bubbles render dimmed and struck-through so
 * the operator knows they're looking at deleted history. Per-bubble
 * delete in this mode hard-purges the row. */
.chat-row-trash .chat-bubble {
    opacity: 0.55;
    border: 1px dashed var(--bs-border-color, var(--asas-border));
    box-shadow: none;
}
.chat-row-trash .chat-bubble-body { text-decoration: line-through; }
.chat-panel-trash .chat-thread { background: var(--bs-tertiary-bg, #f6f8fa); }

/* Inline reply composer — textarea grows on focus, send/forward sit flush right. */
.chat-reply-body {
    resize: none;
    min-height: 2.6rem;
    transition: min-height 0.15s ease;
}
.chat-reply-body:focus { min-height: 5rem; }

@media (min-width: 768px) {
    .chat-thread {
        max-height: 68vh;
        padding: 1.25rem;
    }
    .chat-bubble { max-width: 72%; }
}

@media (min-width: 1200px) {
    .chat-bubble { max-width: 60%; }
}

/* Mobile: tighter card chrome + hide the back arrow only on desktop. */
@media (max-width: 575.98px) {
    .chat-list-item { padding: 0.75rem 0.85rem; gap: 0.6rem; }
    .chat-avatar { width: 2.1rem; height: 2.1rem; font-size: 0.8rem; }
    .chat-thread { padding: 0.75rem; max-height: 62vh; }
    .chat-bubble { padding: 0.55rem 0.75rem; }
    .chat-bubble-body { font-size: 0.92rem; }
}

/* Compose — same card base, textarea grows vertically only. */
.chat-compose-card .chat-compose-body {
    resize: vertical;
    min-height: 8rem;
}

/* =============================================================================
   SHARED GRID STYLES — admin data tables (.asas-grid)
   A thin theme over Bootstrap's .table (every grid already carries .table +
   utilities in markup): grey centered header, comfortable cell padding, soft
   row hover. No !important needed — custom.css loads after Bootstrap, so these
   equal-specificity rules win on source order; the .bg-primary white-text
   enforcer never targets table cells, so cell colours are safe too.
   ============================================================================= */

.asas-grid th,
.asas-grid th * {
    color: var(--asas-text);
}

/* Mobile-first: the compact phone padding is the base; md and up gets the roomier desktop cells. */
.asas-grid th {
    background: var(--asas-surface-subtle);
    font-weight: 600;
    padding: 8px 6px;
    text-align: center;
}

.asas-grid td {
    padding: 8px 6px;
    vertical-align: middle;
    text-align: center;
}

.asas-grid tr:hover {
    background: var(--asas-primary-soft);
}

@media (min-width: 768px) {
    .asas-grid th {
        padding: 12px 15px;
    }

    .asas-grid td {
        padding: 10px 15px;
    }
}

/* Frsh / FrshEdaryh opportunity lists — 13-15 columns squeeze the auto table layout onto whatever
   column has wrappable text (اسم الفرصة), collapsing it to ~1 word per line. Give the name column a
   floor width + start-align (reads better wrapped than centered) and the table itself a min-width so
   excess columns cause horizontal scroll (via the .table-responsive wrapper) instead of squeeze. */
.frsh-list-table { min-width: 950px; }
.frsh-list-table .col-num { width: 3%; }
.frsh-list-table th.col-frsh-name,
.frsh-list-table td.col-frsh-name {
    min-width: 200px;
    text-align: start;
}

/* =============================================================================
   FORM-FIELD COMPONENT
   Canonical form field structure for unified label / input / help / error UX.

   Canonical markup (wrap each field in .form-field):

       <div class="form-field">
           <label class="form-field-label" asp-for="X">
               النص <span class="form-field-required">*</span>
           </label>
           <input class="form-control" asp-for="X" />
           <small class="form-field-help">نص المساعدة الاختياري</small>
           <span class="form-field-error" asp-validation-for="X"></span>
       </div>

   The wrapper provides consistent spacing, label typography, required-star
   styling, and validation error positioning across all packages.
   ============================================================================= */

.form-field {
    margin-bottom: var(--spacing-md);
    display: flex;
    flex-direction: column;
}

.form-field-label {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-weight: var(--font-weight-medium);
    font-size: 0.95rem;
    color: var(--bs-dark);
    line-height: var(--line-height-tight);
}

.form-field-required {
    color: var(--bs-danger);
    margin-inline-start: 0.25rem;
    font-weight: var(--font-weight-bold);
}

.form-field .form-control,
.form-field .form-select {
    width: 100%;
}

/* …but inside an input-group the control must keep Bootstrap's flex sizing, or the
   width:100% above leaves no room for the addon (e.g. password-reveal) and wraps it. */
.form-field .input-group > .form-control {
    width: 1%;
}

.form-field-help {
    display: block;
    margin-top: var(--spacing-xs);
    font-size: 0.8125rem;
    color: var(--bs-secondary);
    line-height: 1.4;
}

.form-field-error {
    display: block;
    margin-top: var(--spacing-xs);
    font-size: 0.8125rem;
    color: var(--bs-danger);
    font-weight: var(--font-weight-medium);
}

.form-field-error:empty {
    display: none;
}

/* Invalid state on the input when a sibling .form-field-error has content */
.form-field:has(.form-field-error:not(:empty)) .form-control,
.form-field:has(.form-field-error:not(:empty)) .form-select {
    border-color: var(--bs-danger);
}

/* Horizontal variant — mobile-first: stacked is the BASE, label moves beside the input from md up. */
.form-field-horizontal {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--spacing-md);
}

.form-field-horizontal .form-field-label {
    flex: 0 0 auto;
    padding-top: 0;
    margin-bottom: 0;
    text-align: start;
}

.form-field-horizontal > *:not(.form-field-label) {
    flex: 1;
}

@media (min-width: 768px) {
    .form-field-horizontal {
        flex-direction: row;
    }

    .form-field-horizontal .form-field-label {
        flex: 0 0 30%;
        padding-top: 0.625rem;
        text-align: end;
    }
}

/* =============================================================================
   RESPONSIVE SIDEBAR — .rs-menu-section (as_Resalh / messaging module)
   On phones the vertical menu consumes 50%+ of viewport height. Turn it into
   a horizontal scrollable pill bar so main content is immediately visible.
   ============================================================================= */

/* Kept as a max-width block deliberately: the desktop form is a plain vertical `ul.mn` inherited
   from Bootstrap/base styles, so inverting would mean writing an explicit "undo" for
   display:flex/overflow-x/nowrap and the pill chrome — more fragile than the threshold it replaces.
   Boundary fixed to 767.98px so it no longer double-applies with the min-width:768px blocks. */
@media (max-width: 767.98px) {
    .rs-menu-section {
        padding: var(--spacing-sm) !important;
        margin-bottom: var(--spacing-md);
    }

    .rs-menu-section ul,
    .rs-menu-section ul.mn {
        display: flex !important;
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: var(--spacing-sm);
        padding: var(--spacing-xs) 0;
        margin: 0;
        list-style: none;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }

    .rs-menu-section ul li,
    .rs-menu-section ul.mn li {
        flex: 0 0 auto;
        margin: 0;
    }

    .rs-menu-section ul a,
    .rs-menu-section ul.mn a {
        display: inline-block;
        white-space: nowrap;
        padding: var(--spacing-sm) var(--spacing-md);
        background: var(--bs-gray-100);
        border-radius: var(--radius-md);
        color: var(--bs-dark);
        text-decoration: none;
        font-weight: var(--font-weight-medium);
        font-size: 0.875rem;
        border: 1px solid var(--bs-border-color);
        min-height: 40px;
        line-height: 1.4;
    }

    .rs-menu-section ul a:hover,
    .rs-menu-section ul a.active,
    .rs-menu-section ul.mn a:hover,
    .rs-menu-section ul.mn a.active {
        background: var(--bs-primary);
        /* !important defeats the active folder's `text-primary` utility (also !important) from the
           markup, which otherwise painted the label blue-on-blue → invisible on the active tab. */
        color: #fff !important;
        border-color: var(--bs-primary);
    }
}

/* =============================================================================
   BUTTON SEMANTIC GUIDE (Bootstrap 5)
   Use these conventions project-wide for consistency. Never use inline
   background-color / color for action buttons — pick the semantic class:

       Save / Submit / Confirm         →  btn btn-primary
       Cancel / Back / Close           →  btn btn-secondary
       Delete / Remove / Destructive   →  btn btn-danger
       Export / Download / Print       →  btn btn-outline-primary
       Edit                            →  btn btn-outline-secondary (or btn-warning)
       Add new / Create                →  btn btn-success
       Info / View details             →  btn btn-info
       Small inline actions            →  btn btn-sm <variant>

   Touch targets: all .btn are enforced to min-height 44px above. Do not
   override with style="height:30px" — use .btn-sm (min-height 36px) instead.
   ============================================================================= */

/* Icon-in-button consistent spacing (replaces ad-hoc margin-left on <i>) */
.btn > i,
.btn > .bi {
    margin-inline-end: 0.35rem;
    vertical-align: -0.125em;
}

.btn > i:last-child,
.btn > .bi:last-child {
    margin-inline-end: 0;
    margin-inline-start: 0.35rem;
}

.btn > i:only-child,
.btn > .bi:only-child {
    margin: 0;
}

/* =============================================================================
   TOP PROGRESS BAR
   Global DB-activity indicator: a thin bar across the top shown during full-page
   navigations and htmx async requests, so the user isn't staring at a frozen UI
   during long stored-procedure calls. RTL-aware (grows from the right). Markup
   and JS controller live in _AppLayout.cshtml.
   ============================================================================= */

#topProgressBar {
    position: fixed;
    top: 0;
    inset-inline: 0;
    height: 3px;
    z-index: 10001;            /* above the fixed-top navbar (Bootstrap z 1030) */
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s ease;
}

#topProgressBar[data-show] {
    opacity: 1;
}

#topProgressBar .bar {
    height: 100%;
    /* Indeterminate: a short segment sweeps across the track on a loop — honest signal for a
       stored-proc round-trip of unknown duration (no faked percentage). Red contrasts on the
       primary-blue fixed-top navbar (a same-hue blue would be invisible) and on light pages. */
    width: 35%;
    background: var(--bs-danger);
    box-shadow: 0 0 8px rgba(var(--bs-danger-rgb), 0.7);
    /* On this RTL page the segment is pinned to the RIGHT edge by default (inline-start), so it
       already occupies [65%,100%] of the track. translateX % is relative to its OWN width (35% of
       track) and is physical (positive = right), unaffected by dir=rtl.
       Off-right  = left edge >= 100%  -> translateX(100%).
       Off-left   = right edge <= 0%   -> move a full track-width = 100/35 ≈ 286% -> translateX(-286%). */
    transform: translateX(100%);
}

#topProgressBar[data-show] .bar {
    animation: topBarSweep 1.1s ease-in-out infinite;
}

/* RTL sweep: enter from off the right edge and exit fully past the left edge. */
@keyframes topBarSweep {
    0%   { transform: translateX(100%); }
    100% { transform: translateX(-286%); }
}

/* =============================================================================
   SKELETON LOADER  (convention-driven — default for any htmx-swapped region)
   Placeholder shimmer shown while a region reloads via htmx — paints the
   content's shape instantly so the swap feels faster than a spinner.

   Opt in with markup only, no per-page JS:
     <div data-skeleton>
       <div class="skel-live"> ...the htmx swap target (real content)... </div>
       <div class="skel-ghost" aria-hidden="true"> ...placeholder shape... </div>
     </div>
   The global controller in _AppLayout adds .is-loading to the [data-skeleton]
   host while its swap is in flight (after a 150ms anti-flash delay) and
   auto-suppresses the top progress bar for that region (one indicator at a time).
   Reusable ghosts: _SkeletonTable.cshtml (lists) / _SkeletonLines.cshtml (forms).
   ============================================================================= */

[data-skeleton] > .skel-ghost { display: none; }
[data-skeleton].is-loading > .skel-live  { display: none; }
[data-skeleton].is-loading > .skel-ghost { display: block; }

/* Table-row variant: the host is a <table data-skeleton> holding two tbodies —
   tbody.skel-live (the real htmx swap target) and tbody.skel-ghost (shimmer rows).
   A shown tbody must be table-row-group, not block, or the table layout collapses. */
[data-skeleton] > tbody.skel-ghost { display: none; }
[data-skeleton].is-loading > tbody.skel-ghost { display: table-row-group; }

.skel-line {
    height: 0.85rem;
    border-radius: 4px;
    background-color: var(--bs-gray-200);
    background-image: linear-gradient(90deg, var(--bs-gray-200) 0, var(--bs-gray-100) 40px, var(--bs-gray-200) 80px);
    background-size: 600px 100%;
    background-repeat: no-repeat;
    animation: skelShimmer 1.2s ease-in-out infinite;
}

@keyframes skelShimmer {
    0%   { background-position: 300px 0; }   /* RTL: sweep right → left */
    100% { background-position: -300px 0; }
}

@media (prefers-reduced-motion: reduce) {
    .skel-line { animation: none; }
}

/* =============================================================================
   WHATSAPP FLOATING BUTTON
   Used in _AppLayout.cshtml. Mobile-first: compact by default, roomier on
   desktop. Landscape phones get the smallest variant so fixed chrome doesn't
   eat the already-short viewport.
   ============================================================================= */

.whatsapp-float {
    position: fixed;
    bottom: 16px;
    left: 16px;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    /* #128c7e (WhatsApp's own dark brand green), NOT the bright #25d366. On #25d366 the white glyph
       measures 1.98:1 and the button's edge against a white page is also 1.98:1 — both under the 3:1
       WCAG 1.4.11 floor for graphical objects and UI-component boundaries, so on a light page the
       control read as a pale blob. #128c7e puts both at 4.14:1 while staying inside the brand palette. */
    background: #128c7e;
    color: #fff;
    text-decoration: none;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.whatsapp-float:hover,
.whatsapp-float:focus-visible {
    color: #fff;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
}

.whatsapp-float svg {
    width: 26px;
    height: 26px;
}

@media (min-width: 576px) {
    .whatsapp-float {
        bottom: 24px;
        left: 24px;
        width: 56px;
        height: 56px;
    }
    .whatsapp-float svg {
        width: 30px;
        height: 30px;
    }
}

/* =============================================================================
   LANDSCAPE PHONE COMPRESSION
   Phones in landscape have very little vertical room (e.g., 375px tall).
   Fixed navbar + fixed WhatsApp button can consume ~30% of viewport height.
   Shrink both aggressively so content gets priority.
   ============================================================================= */
@media (max-height: 500px) and (orientation: landscape) {
    :root {
        /* Can't go below the header buttons' 44px touch minimum + minimal padding, so ~52px is the
           real floor here — the old 44px left content under the bar in landscape. */
        --navbar-height: 52px;
        --header-height: 52px;
    }
    /* Trim the navbar's own padding so it actually reaches ~52px in landscape. */
    .navbar {
        padding-top: 0.25rem;
        padding-bottom: 0.25rem;
    }
    .whatsapp-float {
        width: 40px;
        height: 40px;
        bottom: 8px;
        left: 8px;
    }
    .whatsapp-float svg {
        width: 22px;
        height: 22px;
    }
    /* Trim nav-link vertical padding to match 44px navbar */
    .navbar-nav .nav-link {
        padding-top: 0.35rem;
        padding-bottom: 0.35rem;
    }
}

/* Print: hide the floating button (already handled elsewhere, kept local for discoverability) */
@media print {
    .whatsapp-float {
        display: none !important;
    }
}


/* =============================================================================
   RESPONSIVE CARDS — opt-in card layout for data tables on phones
   Add class="responsive-cards" to a <table> or any ancestor of a table. On
   ≤575px viewports, rows become stacked cards with per-cell labels (taken from
   <thead> automatically by JS in _AppLayout.cshtml). Above 576px, the table
   renders normally.

   Usage:
     <table class="table responsive-cards"> ... </table>
   or
     <div class="responsive-cards"> <table> ... </table> </div>
   ============================================================================= */

/* Raised to 767.98px (was 575.98px): between 576 and 767 (landscape phones, small tablets) a wide
   listing table used to render full-width with neither the card transform NOR a scroll wrapper,
   forcing horizontal body scroll. Turning it into cards through 767px closes that band. */
@media (max-width: 767.98px) {
    .responsive-cards table {
        display: block;
        width: 100% !important;
        overflow-x: visible;
    }

    .responsive-cards thead {
        /* Hide headers but keep them in the DOM so JS can read labels */
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .responsive-cards tbody,
    .responsive-cards tfoot {
        display: block;
        width: 100%;
    }

    .responsive-cards tbody tr {
        display: block;
        width: 100%;
        margin-bottom: var(--spacing-md);
        border: 1px solid var(--bs-border-color);
        border-radius: var(--radius-md);
        padding: var(--spacing-sm);
        background: var(--asas-surface);
        box-shadow: var(--shadow-soft);
    }

    .responsive-cards td {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: var(--spacing-xs) 0;
        border: none;
        gap: var(--spacing-sm);
        text-align: start;
        width: auto !important;
    }

    .responsive-cards td:not(:last-child) {
        border-bottom: 1px dashed var(--bs-border-color-translucent);
    }

    .responsive-cards td::before {
        content: attr(data-label);
        font-weight: var(--font-weight-semibold);
        color: var(--bs-secondary);
        flex: 0 0 40%;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Cells without a label (edit/delete action columns) get the full width */
    .responsive-cards td[data-label=""]::before,
    .responsive-cards td:not([data-label])::before {
        display: none;
    }

    /* Empty cells — collapse */
    .responsive-cards td:empty {
        display: none;
    }

    /* Pager stays put; don't try to card-ify it */
    .responsive-cards .pagination {
        display: flex;
        justify-content: center;
        flex-wrap: wrap;
    }
}

@media (prefers-reduced-motion: reduce) {
    /* The sweep IS the indicator, so disabling it would leave a dead bar. Fall back to a static
       full-width tinted bar: still signals "busy", just without motion. */
    #topProgressBar .bar {
        width: 100%;
        transform: none;
        animation: none;
    }
    #topProgressBar[data-show] .bar {
        animation: none;
    }
}

/* =============================================================================
   TOUCH-DEVICE FALLBACKS
   Hover effects don't fire on touch until tap, producing a sticky "stuck hover"
   and hiding interactive affordances. On (hover: none) devices:
     - Remove lift/transform hints that only hover triggers
     - Surface interactive cues in the default state (so users see them)
     - Give tactile feedback via :active instead of :hover
   ============================================================================= */

@media (hover: none) {
    /* Cards: no hover lift; compress transition for snappier taps */
    .card {
        transition: background-color var(--transition-fast), box-shadow var(--transition-fast);
    }

    .card:hover {
        transform: none;
        box-shadow: var(--shadow-soft);
    }

    .card:active {
        box-shadow: var(--shadow-medium);
        transform: scale(0.995);
    }

    /* Buttons: prefer :active feedback over :hover */
    .btn:hover {
        transform: none;
    }

    .btn:active {
        transform: scale(0.97);
        opacity: 0.9;
    }

    /* Data grid rows: show row affordance via alternating background instead of hover */
    .asas-grid tr:hover {
        background-color: inherit;
    }

    .asas-grid tr:active {
        background-color: rgba(13, 110, 253, 0.08);
    }

    /* Navbar links: underline always visible on touch so the target is obvious */
    .navbar-dark .navbar-nav .nav-link::before {
        opacity: 0.4;
    }

    /* Hint images: drop hover scale (a tap shouldn't feel like a drag) */
    .social-links img:hover {
        transform: none;
    }

    /* Prevent 300ms tap delay and double-tap zoom on interactive controls */
    .btn,
    .nav-link,
    .card,
    a[role="button"] {
        touch-action: manipulation;
    }
}

/* =============================================================================
   PRINT STYLES
   Strip chrome (navbar, footer, floating buttons) and render content
   black-on-white for maximum ink efficiency. Applies to all pages.
   ============================================================================= */

@media print {
    /* Hide non-content chrome */
    header,
    footer,
    .navbar,
    .skip-to-main,
    #topProgressBar,
    .no-print,
    /* WhatsApp floating button */
    a[href*="wa.me"] {
        display: none !important;
    }

    /* Reset page layout for print */
    body {
        padding-top: 0 !important;
        background: var(--asas-surface) !important;
        color: #000 !important;
        font-size: 11pt;
    }

    main,
    main.container,
    .container.my-4 {
        max-width: 100% !important;
        padding: 0 !important;
        margin: 0 !important;
        min-height: 0 !important;
    }

    /* Strip shadows, gradients, hover effects */
    .card,
    .card-header,
    .card-body,
    .card-footer,
    .rs-menu-section {
        box-shadow: none !important;
        border: 1px solid #999 !important;
        background: var(--asas-surface) !important;
        background-image: none !important;
        transform: none !important;
    }

    /* Preserve table legibility */
    table {
        border-collapse: collapse !important;
        page-break-inside: auto;
    }

    thead {
        display: table-header-group;
    }

    tr {
        page-break-inside: avoid;
    }

    /* Links: show URL after text (except local fragments) */
    a[href]:not([href^="#"]):not([href^="javascript"])::after {
        content: " (" attr(href) ")";
        font-size: 0.85em;
        color: #555;
    }

    /* Hide interactive-only controls inside printed reports */
    .btn,
    button,
    input[type="button"],
    input[type="submit"] {
        display: none !important;
    }

    /* But allow print-only buttons to survive */
    .print-only {
        display: revert !important;
    }

    /* Arabic typography at print size */
    body, h1, h2, h3, h4, h5, h6, p, span, div, td, th {
        font-family: 'Traditional Arabic', 'Simplified Arabic', 'Tajawal', 'Cairo', Tahoma, serif !important;
    }

    /* Page margins */
    @page {
        margin: 1.5cm 1cm;
    }
}

/* Override Bootstrap Icons' @font-face to use font-display: swap instead of
   block. The vendor stylesheet ships with `block`, which hides glyphs for ~3s
   while the font loads (FOIT). `swap` shows fallback text immediately and
   swaps to the icon font when ready — better perceived perf with no downside
   here since the icons aren't text-content-critical. Same src; last-
   declaration-wins means this replaces the earlier face. */
@font-face {
    font-family: "bootstrap-icons";
    font-display: swap;
    src: url("./fonts/bootstrap-icons.woff2") format("woff2"),
         url("./fonts/bootstrap-icons.woff") format("woff");
}

/* Public bulletin listing (qNshrh) — compact list rows. Thumb on the
   leading edge (RTL: right), title + category + date + view-count on
   the left, chevron at the trailing edge. Each row is a full-area link
   to the detail page; rows behave as list-group items. */
.nshrh-list .list-group-item {
    padding: 14px 16px;
    border-color: var(--asas-border-soft);
    color: inherit;
    transition: background-color .12s ease;
}
.nshrh-list .list-group-item:hover,
.nshrh-list .list-group-item:focus {
    background: var(--asas-primary-soft);
    color: inherit;
}
.nshrh-list-thumb {
    width: 96px;
    height: 72px;
    object-fit: cover;
    border-radius: 6px;
    flex-shrink: 0;
    border: 1px solid var(--asas-border-soft);
    background: var(--asas-surface-muted, #f8f9fa);
}
.nshrh-list-thumb-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--asas-text-muted);
    font-size: 1.4rem;
}
.nshrh-list-title {
    font-weight: 600;
    color: var(--asas-text);
    font-size: 1rem;
    line-height: 1.4;
    /* Two-line clamp keeps long Arabic titles from breaking the row height */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.nshrh-list-meta i { opacity: .7; }
.min-width-0 { min-width: 0; }

/* Newsletter cards — as_Nshrh (detail + listing). Body HTML is admin-authored rich content. */
.nshrh-card {
    border-radius: 6px;
    border: 1px solid var(--asas-border-soft);
    margin-bottom: 16px;
}
.nshrh-card .card-header {
    background: var(--asas-surface-muted);
    border-bottom: 2px solid var(--asas-primary);
    border-radius: 6px 6px 0 0;
    padding: 12px 16px;
}
/* Detail title renders as <h1 class="h4 card-title"> — keep Bootstrap's .h4 sizing,
   bump weight/colour. (Was a dead `h3` selector that matched no markup.) */
.nshrh-card .card-header .card-title {
    font-weight: 600;
    color: var(--asas-text);
}
.nshrh-card .card-body {
    padding: 16px;
    font-size: 14px;
    color: var(--asas-text-strong);
    line-height: 1.7;
}
.nshrh-card .card-body img {
    max-width: 100%;
    height: auto;
    display: block;
}

/* =============================================================================
   Choices.js — align the wrapped combobox with Bootstrap 5 form-select in RTL.
   Initialized globally by _AppLayout.cshtml for every <select> inside <main>.
   Base styles come from Content/choices/choices.min.css; keep this block minimal.
   ============================================================================= */
.choices {
    margin-bottom: 0;
    font-family: var(--font-primary);
    font-size: 1rem;
}
.choices__inner {
    font-family: var(--font-primary);
    font-size: 1rem;
    min-height: calc(1.5em + 0.75rem + 2px);
    padding: 0.375rem 0.75rem 0.375rem 2.25rem;
    background-color: var(--asas-surface);
    border: 1px solid var(--asas-border);
    border-radius: 0.375rem;
    line-height: 1.5;
    text-align: right;
}
.choices.is-focused .choices__inner,
.choices.is-open .choices__inner {
    border-color: #86b7fe;
    box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%);
    outline: 0;
}
/* Caret on the LEFT for RTL — Bootstrap's .form-select arrow sits on the left
   in bootstrap.rtl.min.css; mirror that so Choices visually matches. */
.choices[data-type*="select-one"]::after {
    right: auto;
    left: 11.5px;
}
.choices[data-type*="select-one"].is-open::after {
    right: auto;
    left: 11.5px;
}
.choices__list--single {
    padding: 0;
    text-align: right;
}
.choices__list--dropdown,
.choices__list[aria-expanded] {
    text-align: right;
    font-family: var(--font-primary);
}
.choices__list--dropdown .choices__item,
.choices__list[aria-expanded] .choices__item {
    text-align: right;
}
/* Mark the already-selected option in the OPEN dropdown. Choices leaves .is-selected
   visually undistinguished by default, so among many options the current value looks
   unselected. Tint it + bold it + add a check so it stands out (hover .is-highlighted
   still layers on top). */
.choices__list--dropdown .choices__item--choice.is-selected,
.choices__list[aria-expanded] .choices__item--choice.is-selected {
    background-color: var(--asas-primary-soft);
    font-weight: 600;
}
.choices__list--dropdown .choices__item--choice.is-selected::after,
.choices__list[aria-expanded] .choices__item--choice.is-selected::after {
    content: "\2713";
    float: left;
    margin-inline-start: .5rem;
    color: var(--asas-primary-strong);
}
.choices__input {
    font-family: var(--font-primary);
    background-color: var(--asas-surface);
    text-align: right;
}
/* Validator error outline propagates from the hidden <select> to the wrapper.
   Choices nests the original <select> INSIDE .choices, so the adjacent-sibling rule
   only fires for un-wrapped selects; the :has rule below covers the Choices case. */
.input-validation-error + .choices .choices__inner,
.choices:has(select.input-validation-error) .choices__inner {
    border-color: var(--asas-danger);
}
/* Direct fallback when a control isn't wrapped by Choices (disabled/skipped selects, inputs). */
.form-select.input-validation-error,
.form-control.input-validation-error {
    border-color: var(--asas-danger);
}
/* Non-colour cue on an invalid field: a danger exclamation icon next to the red border, so the
   error state isn't signalled by colour alone (WCAG 1.4.1). Text inputs + textarea only — selects
   keep the border (their caret owns the inline-start corner) and input-group members opt out below
   (the appended button would overlap the icon). The app is dir=rtl, so the icon sits on the left. */
input.input-validation-error:not([type=checkbox]):not([type=radio]):not([type=file]),
textarea.input-validation-error {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23dc3545'%3E%3Cpath d='M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zM7 4a1 1 0 0 1 2 0v4a1 1 0 0 1-2 0V4zm1 7.25a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left 0.6rem center;
    background-size: 1rem 1rem;
    padding-left: 2.2rem;
}
textarea.input-validation-error {
    background-position: left 0.6rem top 0.6rem;
}
.input-group input.input-validation-error {
    background-image: none;
    padding-left: 0.75rem;
}
/* Containers with overflow:hidden (e.g. .card for rounded-corner clipping)
   truncate a Choices dropdown that opens past their edge. Lift the clip on
   ANY ancestor while a dropdown is open — scoped to .is-open, so normal
   clipping resumes once the menu closes. */
:has(> .choices.is-open),
:has(.choices.is-open) {
    overflow: visible !important;
}
/* Lift the wrapper above later-painted siblings (Bootstrap .input-group
   members in a bulk-actions strip, adjacent cards, etc.) while the menu is
   open. The base Choices CSS only sets z-index:1 on .choices__list--dropdown,
   which is scoped to the wrapper's own stacking context and loses to any
   following position:relative element. */
.choices.is-open {
    z-index: 1021;
}
/* .card:hover applies translateY which creates a stacking context, trapping
   the 1021 above inside the card's local layer. At root level the card then
   competes with following siblings (e.g. cp/queue's bulk-actions strip) by
   document order and loses. Lift the card itself so the open dropdown wins
   against everything painted after it. */
.card:has(.choices.is-open) {
    z-index: 1021;
}

/* ============================================================
   Dashboard landing (_asas/home/dashboard)
   ============================================================ */
.dashboard-avatar {
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--asas-border, #e5e7eb);
    background-color: #fff;
}
/* Icon shown when the user has no avatar — sized to match .dashboard-avatar. */
.dashboard-avatar-fallback {
    font-size: 2.75rem;
    line-height: 1;
}
/* "متصلون الآن" drill-down row avatar — a compact variant of .dashboard-avatar. */
.online-user-avatar {
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--asas-border, #e5e7eb);
    background-color: #fff;
    flex-shrink: 0;
}
.online-user-avatar-fallback {
    font-size: 2rem;
    line-height: 1;
}
/* ============================================================
   Bulletin thumbnails — small cover image (+ icon placeholder for rows
   without a cover) shared by the dashboard/portal bulletin lists.
   ============================================================ */
.bulletin-thumb {
    width: 64px;
    height: 48px;
    object-fit: cover;
    border-radius: .35rem;
    flex-shrink: 0;
    border: 1px solid var(--asas-border, #e5e7eb);
    background: var(--asas-surface-muted, #f8f9fa);
}
.bulletin-thumb-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--asas-text-muted, #6c757d);
    font-size: 1.15rem;
}
.bulletin-title {
    font-weight: 600;
    color: var(--asas-text);
    display: block;
}

/* ============================================================
   Public portal landing (as_Portal/_home/_default)
   ============================================================ */

/* Welcome / sign-in band — the landing's primary greeting + CTA. Soft primary
   tint that also reads as the top hero when no carousel slides are configured. */
.portal-welcome {
    background: linear-gradient(135deg, var(--asas-primary-soft, #f3f7ff), var(--asas-surface, #fff));
    border: 1px solid var(--asas-border, #dee2e6);
}
.portal-welcome-icon {
    font-size: 2.5rem;
    color: var(--asas-primary, #0d6efd);
    line-height: 1;
    flex-shrink: 0;
}
.portal-welcome-title {
    font-size: 1.2rem;
    font-weight: 700;
    /* primary-strong (#0a58ca, ≈5.7:1 on the tinted band) clears AA; plain --asas-primary fails at this size. */
    color: var(--asas-primary-strong, #0a58ca);
    margin: 0 0 .25rem;
}
@media (min-width: 768px) {
    .portal-welcome-title { font-size: 1.4rem; }
}

.portal-quick-card {
    border: 1px solid var(--asas-border, #e5e7eb);
    color: inherit;
    transition: transform .12s ease, box-shadow .12s ease;
}
.portal-quick-card:hover,
.portal-quick-card:focus {
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .075);
    color: inherit;
}
.portal-quick-icon {
    font-size: 2.25rem;
    color: var(--asas-primary, #0d6efd);
}
.portal-quick-title {
    font-size: 1.05rem;
    font-weight: 600;
    /* #0a58ca clears AA (≈5.7:1 on white); plain --asas-primary (#0d6efd) is ≈3.9:1 and fails for ~17px text. */
    color: var(--asas-primary-strong, #0a58ca);
}
.portal-section-title {
    font-weight: 700;
    color: var(--asas-primary, #0d6efd);
}
.portal-section-title i {
    margin-inline-end: .35rem;
}
.portal-bulletin-card {
    border: 1px solid var(--asas-border, #e5e7eb);
    color: inherit;
    overflow: hidden;
    transition: transform .12s ease, box-shadow .12s ease;
}
.portal-bulletin-card:hover,
.portal-bulletin-card:focus {
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .075);
    color: inherit;
}
.portal-bulletin-thumb {
    aspect-ratio: 16 / 9;
    background-color: var(--asas-surface-hover, #f8f9fa);
    background-size: cover;
    background-position: center;
}
.portal-bulletin-thumb-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--asas-muted, #6c757d);
    font-size: 3rem;
}
/* No-cover variant: drops the empty thumb, runs an accent rule down the
   inline-start edge, and lets the title own the card. RTL-safe via the
   logical border property. */
.portal-bulletin-card.portal-bulletin-card-textonly {
    border-inline-start: 4px solid var(--asas-primary, #0d6efd);
}
.portal-bulletin-card.portal-bulletin-card-textonly .card-body {
    padding-top: 1.25rem;
    padding-bottom: 1.25rem;
}
.portal-bulletin-card.portal-bulletin-card-textonly .portal-bulletin-title {
    font-size: 1.15rem;
    -webkit-line-clamp: 3;
}
.portal-bulletin-title {
    font-size: 1rem;
    font-weight: 600;
    /* #0a58ca clears AA (≈5.7:1 on white); plain --asas-primary (#0d6efd) is ≈3.9:1 and fails for 16px text. */
    color: var(--asas-primary-strong, #0a58ca);
    margin: 0 0 .5rem;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.portal-bulletin-meta i {
    margin-inline-end: .25rem;
}
/* Pause/play toggle for the auto-advancing portal carousel (WCAG 2.2.2). */
/* Fluid hero carousel: the image fills the container width edge-to-edge and the
   slide takes the image's natural height, so the whole image shows with no
   letterbox bars and nothing is trimmed when the page is resized. */
#portalCarousel .carousel-item a {
    display: block;
}
#portalCarousel .carousel-item img {
    width: 100%;
    height: auto;
    display: block;
}

.portal-carousel-pause {
    position: absolute;
    bottom: .75rem;
    inset-inline-end: .75rem;
    z-index: 5;
    opacity: .85;
    padding: .25rem .5rem;
    line-height: 1;
}
.portal-carousel-pause:hover,
.portal-carousel-pause:focus {
    opacity: 1;
}
.portal-footer .card {
    border: 1px solid var(--asas-border, #e5e7eb);
}
.portal-socials {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
}
.portal-social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    border: 1px solid var(--asas-border, #e5e7eb);
    background: var(--asas-surface, #fff);
    color: var(--asas-primary, #0d6efd);
    font-size: 1.25rem;
    text-decoration: none;
    transition: transform .12s ease, background-color .12s ease, color .12s ease;
}
.portal-social-link:hover,
.portal-social-link:focus {
    background: var(--asas-primary, #0d6efd);
    color: #fff;
}
.portal-social-link img {
    max-width: 1.5rem;
    max-height: 1.5rem;
    object-fit: contain;
}
.portal-maintenance {
    border: 1px solid #ffc107;
    background: #fff8e1;
}
.portal-maintenance-icon {
    font-size: 3.5rem;
    color: #b97a04;
    line-height: 1;
}
.portal-maintenance-title {
    font-size: 1.5rem;
    font-weight: 700;
    color: #b97a04;
}
.portal-maintenance-message {
    font-size: 1rem;
    color: #664d03;
    line-height: 1.7;
    white-space: pre-line;
}

/* =========================================================================
   Desktop side-rail navigation (authenticated only).
   Below lg the #mainMenu offcanvas slides in via the hamburger; at lg+
   offcanvas-lg renders it inline and these rules turn it into a sticky
   side rail beside the page content.
   ========================================================================= */
:root {
    --sidebar-width: 280px;
    /* Width the side rail + its gap steal from the content row. The authenticated
       shell/header widen by exactly this (via .container-app below) so the content
       column lands at the same width as the public .container. */
    --rail-offset: calc(var(--sidebar-width) + 1.5rem);
}

/* Authenticated canvas widening (rail-offset compensation).
   The header and .app-shell carry `.container .container-app`. At lg+ — where the
   rail sits in-flow and eats --rail-offset from the row — .container-app bumps the
   Bootstrap container max-width by that same offset, so .app-main ends up the width
   the public .container would have had. max-width only caps wide viewports, so on
   narrower screens the shell still uses ~100% width (no overflow); the residual
   squeeze at the low end of lg is geometric and unavoidable. Below lg the rail is an
   offcanvas (out of flow) and .container-app adds nothing, matching the public width.
   Offsets = Bootstrap container max-width (960 / 1140 / 1320) + --rail-offset. */
@media (min-width: 992px) {
    .container-app { max-width: calc(960px + var(--rail-offset)); }
}
@media (min-width: 1200px) {
    .container-app { max-width: calc(1140px + var(--rail-offset)); }
}
@media (min-width: 1400px) {
    .container-app { max-width: calc(1320px + var(--rail-offset)); }
}

/* The shell is the growing body child, so it carries the sticky-footer flex
   chain (body{flex column} -> .app-shell{flex:1 0 auto} -> footer stays put). */
.app-shell {
    display: flex;
    flex-direction: column;
    flex: 1 0 auto;
}

@media (min-width: 992px) {
    .app-shell {
        flex-direction: row;
        align-items: flex-start;
        gap: 1.5rem;
    }

    .app-sidebar {
        flex: 0 0 var(--sidebar-width);
        position: sticky;
        /* Float the card 1.5rem below the header and leave a matching bottom gap. */
        top: calc(var(--header-height) + 1.5rem);
        margin-block: 1.5rem;
        /* No max-height/overflow-y: the rail grows to its full natural height and
           the page scrolls, so the menu never gets its own vertical scrollbar.
           overflow-x:hidden stays so the menu text wraps, never scrolls sideways. */
        overflow-x: hidden;
        /* Card-style rail matching the app's .card (radius/border/shadow). Bootstrap
           forces .offcanvas-lg transparent at lg+, so restore the white surface here.
           Mobile keeps the plain slide-in offcanvas — this whole block is lg+ only. */
        background-color: var(--asas-surface, #fff) !important;
        border: 1px solid var(--bs-border-color, #dee2e6);
        border-radius: var(--radius-lg, .75rem);
        box-shadow: var(--shadow-soft);
    }

    /* Bootstrap sets .offcanvas-lg .offcanvas-body { display:flex } at lg+, which
       would shrink the accordion to its content width — force a full-width block. */
    .app-sidebar .offcanvas-body {
        display: block;
    }

    .app-main {
        flex: 1 1 auto;
        /* min-width:0 lets wide .responsive-cards tables shrink instead of
           overflowing the flex item and forcing horizontal page scroll. */
        min-width: 0;
    }

    /* data-toggle-sidebar (asas-behaviors.js) adds this to .app-shell so a dense page
       (e.g. cert-positions) can reclaim the rail's width. flex `gap` doesn't reserve
       space for a display:none child, so .app-main grows into it with no extra rule. */
    .app-shell.sidebar-hidden .app-sidebar {
        display: none;
    }
}

/* Cert-positions coordinate table (cert-positions) — nowrap field labels + narrow numeric
   inputs so all six columns fit without horizontal scroll at typical widths. */
.pos-table { font-size: .75rem; }
.pos-table th, .pos-table td { padding: .25rem .3rem; vertical-align: middle; }
.pos-table td:first-child, .pos-table th:first-child { white-space: nowrap; }
.pos-table input[type="number"] { width: 3.25rem; padding-inline: .25rem; text-align: center; font-size: inherit; }
.pos-table select { width: 4.25rem; padding-inline-start: .25rem; padding-inline-end: 1.25rem; font-size: inherit; }
.pos-table .form-check-input { margin: 0; }

/* Cert-positions drag/resize overlay (JPG and PDF templates — PDF previews as a rasterized
   image too, Positions.cshtml's nonced script). Boxes are positioned in plain physical
   left/top/width/height px (the template's own raster coordinate space is always LTR
   regardless of the page's dir="rtl"), never logical properties. */
.pos-preview-wrap { position: relative; }
.pos-box {
    position: absolute; box-sizing: border-box;
    border: 2px solid var(--pos-color, var(--bs-primary));
    background-color: color-mix(in srgb, var(--pos-color, var(--bs-primary)) 15%, transparent);
    display: flex; align-items: center; justify-content: center;
    font-size: .7rem; color: var(--pos-color, var(--bs-primary));
    cursor: move; touch-action: none; user-select: none;
}
.pos-box--excluded { opacity: .35; pointer-events: none; }
/* overflow/white-space clipping lives on the label, not .pos-box itself — the box must stay
   unclipped or the resize handles below (positioned half outside its border) get cut off. */
.pos-box-label { overflow: hidden; white-space: nowrap; max-width: 100%; }
.pos-box .pos-handle {
    position: absolute; top: 0; bottom: 0; width: 10px; cursor: ew-resize; touch-action: none;
}
.pos-box .pos-handle-start { left: -5px; }
.pos-box .pos-handle-end { right: -5px; }
/* The handle's hit area (10px wide, full box height) stays draggable on every box; the visible
   square marker only appears on the active (last clicked/focused) field, so the preview isn't
   cluttered with 16 handle marks at once. */
.pos-box--active { border-width: 3px; }
.pos-box--active .pos-handle::after {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 10px; height: 10px; border-radius: 2px;
    background: var(--pos-color, var(--bs-primary));
    border: 1.5px solid #fff;
    box-shadow: 0 0 0 1px var(--pos-color, var(--bs-primary));
}

/* -------------------------------------------------------------------------
   Borderless menu chrome. Applies to both the desktop rail and the mobile
   offcanvas (both carry .app-sidebar). Strips the accordion/list-group
   divider lines and the expanded-group highlight so the menu reads as one
   clean list; affordance comes from hover/active backgrounds instead.
   ------------------------------------------------------------------------- */
.app-sidebar .accordion,
.app-sidebar .accordion-item,
.app-sidebar .accordion-button,
.app-sidebar .list-group,
.app-sidebar .list-group-item {
    background-color: transparent;
    border: 0 !important;
}

/* Group headers read in the primary color in every state (matches the reference
   menu where both collapsed and expanded groups are blue). */
.app-sidebar .accordion-button,
.app-sidebar .accordion-button:not(.collapsed) {
    padding: .75rem 1rem;
    font-weight: var(--font-weight-semibold, 600);
    /* Main-nav group headers at 16px/600 sit right on the 4.5:1 line with plain --asas-primary;
       -strong (#0a58ca, ≈5.7:1) clears AA with margin. */
    color: var(--asas-primary-strong, #0a58ca);
    box-shadow: none;
}
.app-sidebar .accordion-button:focus {
    box-shadow: none;
}

.app-sidebar .list-group-item {
    padding: .5rem 1rem;
    margin: .125rem .5rem;
    border-radius: var(--radius-sm, .5rem) !important;
    color: var(--asas-text, #212529);
    transition: background-color .12s ease, color .12s ease;
}
.app-sidebar .list-group-item-action:hover,
.app-sidebar .list-group-item-action:focus {
    background-color: var(--asas-surface-muted, #f8f9fa);
    /* Hover text is 16px/400 on the muted (#f8f9fa) surface → -strong keeps it ≥4.5:1. */
    color: var(--asas-primary-strong, #0a58ca);
}
.app-sidebar .list-group-item.active {
    background-color: var(--bs-primary-bg-subtle, #cfe2ff);
    color: var(--bs-primary-text-emphasis, #052c65);
    font-weight: var(--font-weight-semibold, 600);
}

/* Small circular chevron at the trailing (RTL: left) end of each navigable item;
   fills with the primary color on hover/active and nudges forward on hover. */
.app-sidebar .menu-item-arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    background-color: var(--asas-surface-muted, #f8f9fa);
    color: var(--asas-primary, #0d6efd);
    font-size: .7rem;
    transition: background-color .12s ease, color .12s ease, transform .12s ease;
}
.app-sidebar .list-group-item-action:hover .menu-item-arrow {
    transform: translateX(-3px);
}

/* Desktop rail card header (lg+ only — d-none hides it on mobile, which keeps the
   offcanvas-header instead). Sticky so it stays pinned while the menu list scrolls. */
.app-sidebar-header {
    position: sticky;
    top: 0;
    z-index: 1;
    align-items: center;
    gap: .5rem;
    padding: .85rem 1rem;
    font-weight: var(--font-weight-semibold, 600);
    color: var(--asas-text, #212529);
    background-color: var(--asas-surface, #fff);
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}
.app-sidebar-header i {
    color: var(--asas-primary, #0d6efd);
    font-size: 1.1rem;
}

/* Status badge for the terminal "مكلف" (granted/assigned) application state. Indigo keeps it
   distinct from "جديد" (primary blue) and "مقبول" (success green) — the two states it would
   otherwise be confused with — while still reading as a final, positive outcome. .badge already
   defaults its text to white, so only the background is set here. Returned by the BadgeClass
   switches in TshkylatTlb / TshkylatTlbDetail. */
.bg-tklyf {
    background-color: #6610f2 !important;  /* mirrors --bs-indigo */
}

/* =============================================================================
   TOUCH ERGONOMICS — applied on touch devices only (no hover pointer). Lifts the
   controls whose tap targets fell below the ~44px floor, and bumps small inputs to
   16px so iOS Safari doesn't zoom-and-reflow the page on focus. Desktop (hover)
   keeps the compact sizing.
   ============================================================================= */
@media (hover: none) {
    /* iOS zoom-on-focus fires for any <input>/<select> under 16px; the -sm controls
       (inline edit rows, filter bars) were left at Bootstrap's 14px. */
    .form-control-sm,
    .form-select-sm {
        font-size: 16px;
    }

    /* Choices.js hides the 44px-min native <select> behind a ~39px control — bump the
       visible box so the real tap target reaches the floor. */
    .choices__inner {
        min-height: 44px;
    }

    /* Header user-menu / notifications / message-badge items are plain .dropdown-item
       (not .btn), so they miss the global 44px minimum — grow the vertical padding. */
    .dropdown-item {
        padding-top: 0.6rem;
        padding-bottom: 0.6rem;
    }

    /* Bare <button> actions that don't inherit .btn's 44px minimum. The rghbat
       school-reorder controls are the primary interaction on that page → full 44px;
       the chat bubble forward/delete are secondary + space-constrained → 40px. */
    .rghbat-sort-actions button {
        min-width: 44px;
        min-height: 44px;
    }
    .chat-bubble-action {
        width: 2.5rem;
        height: 2.5rem;
    }
}
