/* ============================================================
   Modern SaaS Theme — Bootstrap 5.3 CSS variable overrides
   Loaded after global.css / responsive.css from index.tpl
   ============================================================ */

/* ── Global tokens (CSS variables Bootstrap 5.3 reads everywhere) ── */
:root {
    /* Typography */
    --bs-body-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
    --bs-body-font-size: 0.9375rem;     /* 15px */
    --bs-body-line-height: 1.55;
    --bs-body-color: #0f172a;            /* slate-900 */
    --bs-body-bg: #e6eaee;               /* slate-50 */
    --bs-secondary-color: #64748b;       /* slate-500 */
    --bs-tertiary-color: #94a3b8;        /* slate-400 */

    /* Borders & radii */
    --bs-border-radius-sm: 0.5rem;       /* 8px  */
    --bs-border-radius:    0.625rem;     /* 10px */
    --bs-border-radius-lg: 0.875rem;     /* 14px */
    --bs-border-radius-xl: 1rem;         /* 16px */
    --bs-border-color: #e2e8f0;          /* slate-200 */
    --bs-border-color-translucent: rgba(15, 23, 42, 0.08);

    /* Brand palette — neutral-modern (Linear / Vercel / Notion vibe) */
    --bs-primary:        #0f172a;        /* slate-900 — primary buttons, links */
    --bs-primary-rgb:    15, 23, 42;
    --bs-success:        #10b981;        /* emerald-500 */
    --bs-success-rgb:    16, 185, 129;
    --bs-danger:         #ef4444;        /* red-500 */
    --bs-danger-rgb:     239, 68, 68;
    --bs-warning:        #f59e0b;        /* amber-500 */
    --bs-warning-rgb:    245, 158, 11;
    --bs-info:           #06b6d4;        /* cyan-500 */
    --bs-info-rgb:       6, 182, 212;
    --bs-secondary:      #64748b;
    --bs-secondary-rgb:  100, 116, 139;
    --bs-light:          #f8fafc;
    --bs-light-rgb:      248, 250, 252;

    /* Soft, layered shadows */
    --bs-box-shadow-sm: 0 1px 2px 0 rgb(15 23 42 / 0.04);
    --bs-box-shadow:    0 1px 3px 0 rgb(15 23 42 / 0.07), 0 1px 2px -1px rgb(15 23 42 / 0.05);
    --bs-box-shadow-lg: 0 10px 25px -5px rgb(15 23 42 / 0.08), 0 8px 10px -6px rgb(15 23 42 / 0.05);

    /* Focus ring */
    --bs-focus-ring-color: rgba(15, 23, 42, 0.15);

    /* Replace Bootstrap's #cfe2ff (default primary-bg-subtle) with soft gray-blue */
    --bs-primary-bg-subtle: #eef2f6;
}

/* .table-primary uses --bs-table-bg locally; override that variant too */
.table-primary {
    --bs-table-bg: #eef2f6;
}

/* ── Accordion ── clean white, hover gray-blue, no Bootstrap default tints ──
   Bootstrap defines --bs-accordion-bg etc. on the .accordion selector itself.
   Use !important on the custom properties so the override wins regardless of
   source order or any future inline override. */
.accordion,
body .accordion {
    --bs-accordion-bg:           #fff !important;
    --bs-accordion-active-bg:    #fff !important;
    --bs-accordion-active-color: var(--bs-body-color) !important;
    --bs-accordion-btn-bg:       #fff !important;
    --bs-accordion-btn-color:    var(--bs-body-color) !important;
}
.accordion-button,
.accordion-button:not(.collapsed) {
    background-color: #fff !important;
    color: var(--bs-body-color) !important;
}
.accordion-button:hover {
    background-color: #eef2f6 !important;
}

/* ── Body / typography ── */
body {
    font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: var(--bs-body-color);
    background-color: var(--bs-body-bg);
}
h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 {
    color: #0f172a;
    letter-spacing: -0.015em;
}
h1, .h1 { font-weight: 700; letter-spacing: -0.025em; }
h2, .h2 { font-weight: 700; letter-spacing: -0.022em; }
h3, .h3 { font-weight: 600; }
h4, .h4 { font-weight: 600; }
h5, .h5 { font-weight: 600; font-size: 1rem; }
h6, .h6 { font-weight: 600; font-size: 1rem; }
.text-muted { color: var(--bs-secondary-color) !important; }

/* Links */
a { color: #0f172a; text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: #334155; }

/* ── Page title block (used across the app) ── */
.page-title h1 {
    font-size: 1.625rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    color: #0f172a;
}

/* ── Cards ── */
.card {
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius-lg);
    box-shadow: var(--bs-box-shadow-sm);
    background: #fff;
    overflow: hidden;
}
.card-header {
    background: #fff;
    border-bottom: 1px solid var(--bs-border-color);
    padding: 1rem 1.25rem;
    font-weight: 600;
}
.card-header.bg-light {
    background: #f8fafc !important;
}
.card-header h5,
.card-header .h5 {
    font-size: 0.9375rem;
    font-weight: 600;
    color: #0f172a;
    margin-bottom: 0;
}
.card-body { padding: 1.25rem; }
.card-footer {
    background: #fff;
    border-top: 1px solid var(--bs-border-color);
    padding: 0.875rem 1.25rem;
}
.card.border-secondary-subtle {
    border-color: var(--bs-border-color) !important;
}

/* ── Buttons ── */
.btn {
    border-radius: 4px;
    padding: 0.55rem 1.1rem;
    font-weight: 500;
    font-size: 0.9375rem;
    line-height: 1.4;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
}
.btn:active { transform: translateY(1px); }
.btn-sm {
    padding: 0.35rem 0.8rem;
    font-size: 0.8125rem;
    border-radius: 4px;
}
.btn-lg {
    padding: 0.75rem 1.4rem;
    font-size: 1rem;
    border-radius: 4px;
}

.btn-primary,
body a.btn-primary,
body a.btn-primary:link,
body a.btn-primary:visited {
    background-color: #0f172a;
    border-color: #0f172a;
    color: #fff !important;
}
.btn-primary:hover, .btn-primary:focus,
body a.btn-primary:hover, body a.btn-primary:focus {
    background-color: #1e293b;
    border-color: #1e293b;
    color: #fff !important;
}
.btn-success,
body a.btn-success,
body a.btn-success:link,
body a.btn-success:visited {
    background-color: #10b981;
    border-color: #10b981;
    color: #fff !important;
}
.btn-success:hover, .btn-success:focus,
body a.btn-success:hover, body a.btn-success:focus {
    background-color: #059669;
    border-color: #059669;
    color: #fff !important;
}
.btn-danger,
body a.btn-danger,
body a.btn-danger:link,
body a.btn-danger:visited {
    background-color: #ef4444;
    border-color: #ef4444;
    color: #fff !important;
}
.btn-danger:hover, .btn-danger:focus,
body a.btn-danger:hover, body a.btn-danger:focus {
    background-color: #dc2626;
    border-color: #dc2626;
    color: #fff !important;
}
.btn-warning,
body a.btn-warning,
body a.btn-warning:link,
body a.btn-warning:visited {
    background-color: #f59e0b;
    border-color: #f59e0b;
    color: #fff !important;
}
.btn-warning:hover, .btn-warning:focus,
body a.btn-warning:hover, body a.btn-warning:focus {
    background-color: #d97706;
    border-color: #d97706;
    color: #fff !important;
}
.btn-outline-secondary,
body a.btn-outline-secondary {
    border-color: var(--bs-border-color);
    color: #475569 !important;
    background: #fff;
}
.btn-outline-secondary:hover,
body a.btn-outline-secondary:hover,
.btn-outline-secondary:hover i,
body a.btn-outline-secondary:hover i {
    background: #f1f5f9;
    border-color: #cbd5e1;
    color: #0f172a !important;
}
.btn-outline-primary {
    border-color: #0f172a;
    color: #0f172a;
}

.btn-outline-primary:hover {
    background: #0f172a;
    color: #fff;
}
.btn-back {
    width: 38px;
    height: 38px;
    border-radius: var(--bs-border-radius);
    border: 1px solid var(--bs-border-color);
    background: #fff;
    color: #475569;
}
.btn-back:hover {
    background: #f1f5f9;
    color: #0f172a;
}

/* ── Form controls ── */
.form-control,
.form-select {
    border-radius: var(--bs-border-radius);
    border: 1px solid var(--bs-border-color);
    padding: 0.55rem 0.85rem;
    font-size: 0.9375rem;
    color: #0f172a;
    background-color: #fff;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-control::placeholder { color: #94a3b8; }

/* Required-field asterisk — drop-in replacement for the larger
   `<span class="badge-required">required</span>` chip. */
.required-asterisk {
    color: #991b1b;
    font-weight: 700;
    margin-left: 2px;
}

/* Select2 + Bootstrap is-valid / is-invalid bridge.
   Select2 hides the underlying <select> and renders .select2-container
   right after it, so Bootstrap's border colors normally never reach the
   visible widget. The sibling-selector rules below paint the Select2
   selection box red or green to match the hidden <select>'s state. */
.is-invalid + .select2-container--default .select2-selection,
.is-invalid + .select2-container--default .select2-selection--single,
.is-invalid + .select2-container--default .select2-selection--multiple {
    border-color: #dc3545 !important;
}
.is-invalid + .select2-container--default.select2-container--focus .select2-selection,
.is-invalid + .select2-container--default.select2-container--open  .select2-selection {
    box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
}
.is-valid + .select2-container--default .select2-selection,
.is-valid + .select2-container--default .select2-selection--single,
.is-valid + .select2-container--default .select2-selection--multiple {
    border-color: #198754 !important;
}
.is-valid + .select2-container--default.select2-container--focus .select2-selection,
.is-valid + .select2-container--default.select2-container--open  .select2-selection {
    box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
}
.form-control:focus,
.form-select:focus {
    border-color: #0f172a;
    box-shadow: 0 0 0 3px var(--bs-focus-ring-color);
}
.form-control-plaintext {
    color: #0f172a;
    padding: 0.55rem 0;
}
.form-label {
    font-weight: 500;
    color: #334155;
    margin-bottom: 6px;
    font-size: 1rem;
}
.form-label.fw-semibold { font-weight: 600; color: #1e293b; }
.form-text {
    color: var(--bs-secondary-color);
    font-size: 0.8125rem;
}

/* Validation states */
.form-control.is-valid,
.was-validated .form-control:valid {
    border-color: #10b981;
}
.form-control.is-invalid,
.was-validated .form-control:invalid {
    border-color: #ef4444;
}
.form-control.is-valid:focus,
.form-control.is-invalid:focus {
    box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.08);
}

/* Form check (checkbox / radio).
   Flexbox layout — Bootstrap's default padding-left + negative
   margin-left dance is fragile when the control is sized in `em`
   (our 1.1em vs default 1em) and breaks differently across
   viewports. Flex with `gap` gives a consistent gutter and a clean
   baseline-vs-center vertical alignment across desktop + mobile. */
.form-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding-left: 0;
    margin-bottom: 0.25rem;
    min-height: auto;
}
.form-check .form-check-input {
    margin-left: 0;
    margin-top: 0;
    flex-shrink: 0;
}
.form-check-input {
    border-color: #cbd5e1;
    border-radius: 0.35rem;
    width: 1.1em;
    height: 1.1em;
}

.form-check-input:focus {
    border-color: #0f172a;
    box-shadow: 0 0 0 3px var(--bs-focus-ring-color);
}
.form-check-label {
    font-size: 0.9375rem;
    color: #1e293b;
    cursor: pointer;
    user-select: none;
}

/* Input group buttons (password show / generate) */
.input-group > .btn {
    border-color: var(--bs-border-color);
}
.input-group > .form-control:focus {
    z-index: 3;
}

/* ── Tables ── */
.table {
    --bs-table-color: #0f172a;
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--bs-border-color);
    margin-bottom: 0;
}
.table > thead {
    background: #f5f7fa;
}
.table > thead > tr > th {
    background: #f5f7fa;
    color: #667085;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 700;
    padding: 0.5rem;
    border-bottom: 1px solid #dfe6ee;
    vertical-align: middle;
}
.table > thead > tr > th a {
    color: inherit;
    border-bottom: 0;
    text-decoration: none;
}
.table > tbody > tr > td {
    padding: 0.6rem 0.5rem;
    vertical-align: middle;
    border-color: var(--bs-border-color);
}

/* ── Payment status chips (global) ──
   Single source of truth used on /roster/ Pay status, /orders/ Status,
   /dashboard/ Recent Rosters payment, and the Cards table inside
   /roster/details/ (.order-status-* are aliases of the same look).
   Pure CSS — markup is unchanged across pages.

   Rendered as a small rounded-rect chip:
     • bg + fg per state (soft, low-contrast bg / readable fg)
     • leading icon via Bootstrap Icons font (::before, no markup edit)
     • inline-flex so the chip sits cleanly inside <li>, <span>, or <td>
*/
.pay-paid,
.pay-notpaid,
.pay-partial,
.pay-voided,
.order-status-paid,
.order-status-notpaid,
.order-status-partial,
.order-status-voided {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 4px;
    border: 0;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    vertical-align: middle;
}
.pay-paid::before,
.pay-notpaid::before,
.pay-partial::before,
.pay-voided::before,
.order-status-paid::before,
.order-status-notpaid::before,
.order-status-partial::before,
.order-status-voided::before {
    display: inline-block;
    font-family: bootstrap-icons !important;
    font-style: normal;
    font-weight: normal !important;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    font-size: 12px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Paid — green, plain check (no surrounding circle) */
.pay-paid,
.order-status-paid          { background: #e7f6ec; color: #248a3a; }
.pay-paid::before,
.order-status-paid::before  { content: "\f26e"; font-size: 14px; } /* bi-check, slightly larger so the bare glyph reads clearly */

/* Not paid — red, outline triangle */
.pay-notpaid,
.order-status-notpaid           { background: #fde8ea; color: #c8202a; }
.pay-notpaid::before,
.order-status-notpaid::before   { content: "\f33b"; } /* bi-exclamation-triangle */

/* Partially paid — amber, outline clock */
.pay-partial,
.order-status-partial          { background: #fff4e5; color: #a14b00; }
.pay-partial::before,
.order-status-partial::before  { content: "\f293"; } /* bi-clock */

/* Voided — neutral grey, outline dash-circle */
.pay-voided,
.order-status-voided          { background: #f1f3f5; color: #4b5563; }
.pay-voided::before,
.order-status-voided::before  { content: "\f2e6"; } /* bi-dash-circle */

/* ── "Pay now" action button (global, in-table only) ──
   Unifies the Pay now link across /orders/, /roster/, /dashboard/ and
   the Cards table inside /roster/details/. Selector targets the link
   by its destination URL (`/roster/participants/materialspay`) so no
   markup changes are needed — green Bootstrap btn, bare anchor, and
   the .btn-status-action--pay variant all collapse to the same chip.

   Brand blue (not green): green is reserved for the resulting Paid
   chip in the same column.

   `!important` is used on the visuals because the existing markup ships
   with .btn / .btn-success / .btn-status-action classes whose CSS would
   otherwise win on equal specificity. */
.table a[href*="/roster/participants/materialspay"],
.archive .main .table a[href*="/roster/participants/materialspay"] {
    display: inline-flex !important;
    align-items: center;
    gap: 6px;
    height: 28px;
    padding: 0 12px !important;
    background-color: #2c6ecb !important;
    color: #fff !important;
    border: 1px solid #1d5dbb !important;
    border-radius: 4px !important;
    font-size: 12px !important;
    font-weight: 500 !important;
    line-height: 1 !important;
    text-decoration: none !important;
    box-shadow: none !important;
    transform: none !important;
    transition: background-color 120ms ease, border-color 120ms ease;
    white-space: nowrap;
    vertical-align: middle;
}
.table a[href*="/roster/participants/materialspay"]::before,
.archive .main .table a[href*="/roster/participants/materialspay"]::before {
    content: "\f2dc"; /* bi-credit-card (outline) */
    display: inline-block;
    font-family: bootstrap-icons !important;
    font-style: normal;
    font-weight: normal !important;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    font-size: 12px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
.table a[href*="/roster/participants/materialspay"]:hover,
.archive .main .table a[href*="/roster/participants/materialspay"]:hover {
    background-color: #1d5dbb !important;
    border-color: #154a99 !important;
    color: #fff !important;
    text-decoration: none !important;
}
.table a[href*="/roster/participants/materialspay"]:focus-visible,
.archive .main .table a[href*="/roster/participants/materialspay"]:focus-visible {
    outline: 2px solid #2c6ecb;
    outline-offset: 2px;
}

/* ── Stats row pattern (global) ──
   Light, bordered pill of label/value pairs separated by a thin vertical
   bar. Used at the top of /orders/ for sales totals and in the Cards
   section of /roster/details/ for roster KPIs. The roster_details.tpl
   block has its own `.archive .main`-scoped copy that wins on that page
   (kept for documentation); the unscoped rules below let any page reuse
   the markup without duplicating CSS. */
.roster-stats-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    padding: 0.75rem 1rem;
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 6px;
    margin-bottom: 1.25rem;
    font-size: 0.85rem;
}
.roster-stats-row .roster-stat {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}
.roster-stats-row .roster-stat-label {
    color: #667085;
}
.roster-stats-row .roster-stat-value {
    font-weight: 700;
    color: #1f2937;
}
.roster-stats-row .roster-stat + .roster-stat::before {
    content: '';
    display: inline-block;
    width: 1px;
    height: 14px;
    background: #d0d5dd;
    margin-right: 0.35rem;
}

/* ── ID column (global) ──
   Tables across the site have an ID column carrying the numeric primary
   key. Keep TH using the global compact thead style (so all headers look
   uniform); shrink ONLY the TD value to 10px so 6-digit ids don't dominate
   the row visually. Matches the participants/cards .col-id baseline. */
.table > tbody > tr > td.col-id,
table tbody tr td.col-id {
    font-size: 10px;
    padding: 5px;
    white-space: nowrap;
}

/* ── Accordion body content polish (FAQ + similar) ──
   Bootstrap's `.accordion-body` is just a plain padding box — lists and
   paragraphs inside need explicit list-style + margin-left so they don't
   collapse against the body's left edge. Mirrors the standard `.answer`
   styling but scoped to `.accordion-body` so all accordion-rendered
   user-content (FAQ, Support knowledge base, etc.) gets the same
   typographic baseline. */
.accordion-body ul,
.accordion-body ol {
    padding-left: 1.5rem;
    margin: 0 0 0.85rem;
    line-height: 1.6;
}
.accordion-body ul { list-style: disc; }
.accordion-body ol { list-style: decimal; }
.accordion-body ul ul,
.accordion-body ol ol,
.accordion-body ul ol,
.accordion-body ol ul {
    margin-bottom: 0.25rem;
}
.accordion-body li {
    margin-bottom: 0.25rem;
}
.accordion-body p {
    margin-bottom: 0.75rem;
    line-height: 1.6;
}
.accordion-body p:last-child {
    margin-bottom: 0;
}
.accordion-body a {
    color: #0d6efd;
    text-decoration: underline;
}
.accordion-body a:hover {
    color: #0a58ca;
}
.accordion-body code {
    background: #f1f3f5;
    color: #d6336c;
    padding: 0.1em 0.35em;
    border-radius: 4px;
    font-size: 0.9em;
}
.accordion-body blockquote {
    border-left: 3px solid #dee2e6;
    margin: 0 0 0.85rem;
    padding: 0.25rem 0 0.25rem 1rem;
    color: #495057;
}
.table > tbody > tr:hover {
    background: #f8fafc;
}
.table-light {
    --bs-table-bg: #f8fafc;
    --bs-table-color: #475569;
}

/* ── Modals (Bootstrap 5) — global baseline ── */
.modal-content {
    background-color: #fff;
    border: 1px solid var(--bs-border-color);
    border-radius: 16px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
    overflow: hidden;
}
.modal-header {
    background: #fff;
    border-bottom: 1px solid #e9ecef;
    padding: 1.15rem 1.5rem;
    align-items: center;
}
.modal-header .btn-close {
    padding: 0.5rem;
    margin: -0.25rem -0.25rem -0.25rem auto;
}
.modal-body {
    padding: 1.25rem 1.5rem;
    background: #fff;
}
.modal-footer {
    background: #fff;
    border-top: 1px solid #e9ecef;
    padding: 1rem 1.5rem;
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
}
.modal-title {
    font-weight: 600;
    font-size: 1.0625rem;
    color: #0f172a;
}
.modal-backdrop.show { opacity: 0.45; }

/* Modals without explicit header/footer — give body more room */
.modal-content > .modal-body:first-child { padding-top: 1.5rem; }
.modal-content > .modal-body:last-child  { padding-bottom: 1.5rem; }

/* ── Remodal (legacy popups) — match Bootstrap modal look ── */
.remodal-overlay {
    background: rgba(15, 23, 42, 0.45) !important;
}
.remodal {
    background: #fff !important;
    border-radius: 16px !important;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08) !important;
    padding: 0 !important;
    max-width: 500px;
}
.remodal .head {
    padding: 1.15rem 1.5rem;
    border-bottom: 1px solid #e9ecef;
    font-weight: 600;
    font-size: 1.0625rem;
    color: #0f172a;
}
.remodal [data-div="modal_content"],
.remodal form {
    padding: 1.25rem 1.5rem;
}
.remodal .remodal-close {
    top: 12px;
    right: 14px;
}
.remodal .form-row {
    margin-bottom: 0.75rem;
}
.remodal .form-label,
.remodal .form-row .form-label {
    font-weight: 500;
    color: #334155;
    margin-bottom: 4px;
}
.remodal input[type="text"],
.remodal input[type="number"],
.remodal textarea,
.remodal select {
    border-radius: var(--bs-border-radius, 0.625rem);
    border: 1px solid var(--bs-border-color, #e2e8f0);
    padding: 0.55rem 0.85rem;
    font-size: 0.9375rem;
    width: 100%;
}
.remodal input:focus,
.remodal textarea:focus,
.remodal select:focus {
    border-color: #0f172a;
    outline: none;
    box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.1);
}

/* ── Badges (vivid solid backgrounds + white text for status pills) ── */
.badge {
    font-weight: 600;
    padding: 0.4em 0.7em;
    border-radius: var(--bs-border-radius-sm);
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    line-height: 1.2;
}
.badge.text-bg-secondary { background: #6b7280 !important; color: #fff !important; }  /* gray-500 — DRAFT / UNKNOWN */
.badge.text-bg-success   { background: #198754 !important; color: #fff !important; }  /* green — APPROVED / SUCCESS */
.badge.text-bg-primary   { background: #0d6efd !important; color: #fff !important; }  /* blue — REVIEWING */
.badge.text-bg-warning   { background: #ffc107 !important; color: #1f2937 !important; }  /* yellow — REJECTED / ERROR (dark text for contrast) */
.badge.text-bg-danger    { background: #dc2626 !important; color: #fff !important; }  /* red-600 */
.badge.text-bg-info      { background: #0d6efd !important; color: #fff !important; }  /* same blue as primary */
.badge.text-bg-light     { background: #e2e8f0 !important; color: #1e293b !important; }
.badge.text-bg-dark      { background: #0f172a !important; color: #fff !important; }

/* ── Alerts ── */
.alert {
    border-radius: var(--bs-border-radius);
    border: 1px solid transparent;
    padding: 0.875rem 1rem;
    font-size: 0.9375rem;
}
.alert-warning {
    background: #fffbeb;
    border-color: #fde68a;
    color: #92400e;
}
.alert-warning a { color: #78350f; font-weight: 600; }
.alert-success {
    background: #ecfdf5;
    border-color: #a7f3d0;
    color: #065f46;
}
.alert-danger {
    background: #fef2f2;
    border-color: #fecaca;
    color: #991b1b;
}
.alert-info {
    background: #f0f9ff;
    border-color: #bae6fd;
    color: #075985;
}

/* ── Nav pills (used by Edit Account vertical tabs) ── */
.nav-pills .nav-link {
    color: #475569;
    border-radius: var(--bs-border-radius);
    font-weight: 500;
    padding: 0.55rem 0.9rem;
}
.nav-pills .nav-link:hover {
    background: #f1f5f9;
    color: #0f172a;
}
.nav-pills .nav-link.active,
.nav-pills .show > .nav-link {
    background: #0f172a;
    color: #fff;
}

/* Edit Account custom tab links */
.edit-tab-link {
    color: #475569 !important;
    background: transparent;
    border: none;
    padding: 0.6rem 0.95rem;
    border-radius: var(--bs-border-radius);
    text-align: left;
    font-weight: 500;
    font-size: 0.9rem;
    transition: background 0.15s ease, color 0.15s ease;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
}
.edit-tab-link:hover {
    background: #f1f5f9;
    color: #0f172a !important;
}
.edit-tab-link.active {
    background: #0d6efd !important;
    color: #fff !important;
}
.edit-tab-link i { font-size: 1rem; }

/* ── Drop-shadow on the main content area ── */
.content {
    background: var(--bs-body-bg);
}

/* ── Custom-progress wizard (Roster Add, Add Account) ── */
/* Active circle uses the new primary color */
.custom-progress .step.active a .number,
.custom-progress .step.active p .number {
    background-color: #0f172a;
    box-shadow: 0 0 0 4px rgba(15, 23, 42, 0.08);
}
.custom-progress .custom-progress-bar {
    background-color: #e2e8f0;
}
.custom-progress .custom-progress-bar.progress-bar-active:before {
    background-color: #10b981;
}

/* ── Pwd checklist polish ── */
.pwd-checklist li::before {
    background: #cbd5e1;
}
.pwd-checklist li.passed::before {
    background: #10b981;
}

/* ── Login / forgot password page polish ── */
.login-card,
.passwordreminder-card {
    border-radius: var(--bs-border-radius-xl);
    box-shadow: var(--bs-box-shadow-lg);
}

/* ── Side menu (preserve layout, just refresh the look) ── */
.side-menu,
#side_menu,
.sidebar {
    background: #fff;
    border-right: 1px solid var(--bs-border-color);
}

/* ── Pagination ── */
.pagination .page-link {
    border-radius: var(--bs-border-radius);
    border: 1px solid var(--bs-border-color);
    color: #475569;
    margin: 0 2px;
}
.pagination .page-item.active .page-link {
    background: #0f172a;
    border-color: #0f172a;
    color: #fff;
}

/* ── Selection color ── */
::selection {
    background: rgba(15, 23, 42, 0.12);
    color: #0f172a;
}

/* ── Scrollbar (Webkit) ── */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; border: 2px solid #f8fafc; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }

/* ============================================================
   Responsive safety nets — prevent horizontal scroll on narrow
   viewports. Loaded last so these overrides win over Bootstrap
   and legacy global.css.
   ============================================================ */

/* Hard kill on viewport-level horizontal scroll. The portal has
   many legacy templates with intrinsic-width tables, fixed-px
   widgets (elFinder), and hardcoded `width=` attributes on <th>;
   without this guard any of them spills over on phones. */
html, body {
    overflow-x: hidden;
    max-width: 100%;
}

/* Media + embeds must never push the layout. */
img,
video,
iframe,
embed,
object {
    max-width: 100%;
    height: auto;
}

/* Tables: keep `<table class="table-responsive-…">` markup behaving
   the way Bootstrap intended, but cap raw <table> width so an
   un-wrapped legacy table can't overflow either. */
table {
    max-width: 100%;
}
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Form controls inherit the 100% cap so a nominal `width: 250px`
   inline style doesn't push past a 320-px screen. Covers both
   Bootstrap form classes AND raw legacy inputs/selects/textareas
   (registration.tpl, mails_settings.tpl, courses_add.tpl etc. use
   inline `style="width: 300-400px"` without any class). */
.form-control,
.form-select,
.input-group,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="url"],
input[type="number"],
select,
textarea {
    max-width: 100%;
}

/* Long emails / URLs / IDs in alerts, comments, and ticket bodies
   were the second-most-common overflow source. Allow break inside
   words only at the boundaries that matter (no mid-word breaks for
   normal text).

   IMPORTANT: scope deliberately narrow — `.archive .content` (the
   former entry) caught EVERY descendant on every list/details
   page, including all table cells, which made browsers split
   short labels like "ID" → "I / D" and dates "10/22/2025"
   → "10/22/ \n 2025". Tables get the explicit table-cell reset
   below to undo any inherited break behaviour. */
.alert,
.accordion-body,
.modal-body,
.comment-body,
.ticket-body {
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Code / preformatted blocks scroll inside their own box. */
pre, code {
    max-width: 100%;
    overflow-x: auto;
    white-space: pre-wrap;
    word-wrap: break-word;
}

/* Bootstrap buttons: allow wrapping on very narrow screens so a
   long CTA label doesn't widen the button past its container.
   Pills/badges that count items (e.g. "N docs uploaded") keep
   white-space: nowrap explicitly via text-nowrap on the element. */
@media (max-width: 575.98px) {
    .btn:not(.text-nowrap) {
        white-space: normal;
    }
}

/* Bootstrap 5.3 ships responsive utilities for many properties but
   NOT for width — so `w-md-auto` silently no-ops and the paired
   `w-100` keeps the element full-width on every breakpoint. Define
   the missing variant here so stacked-on-mobile / inline-on-md
   layouts (e.g. document row action buttons + Save Documents CTA)
   actually shrink to content width on md+. */
@media (min-width: 768px) {
    .w-md-auto { width: auto !important; }
}

/* Site container: belt-and-braces against any descendant that
   tries to negative-margin out of the layout. */
.site-container {
    overflow-x: clip;
}

/* ── Tables — sitewide layout polish ──
   1. 10 px bottom margin so adjacent content (pagination,
      "N participants" footers, action button rows) doesn't sit
      flush against the last row. Targets BOTH .table-responsive
      (the wrapper) and .table — needs to win against
      `.table { margin-bottom: 1rem }` in elements.css and
      Bootstrap defaults. */
table,
.table,
.table-responsive {
    margin-bottom: 10px;
}

/* The CSS spec requires that when one axis is `auto/scroll/hidden`,
   the other axis is promoted from `visible` to `auto`. So setting
   `overflow-x: auto` on .table-responsive (Bootstrap default + our
   own rules) silently turns on a vertical scrollbar too — visible
   on Roster Details Participants table even though no row actually
   exceeds the height. Pin overflow-y: hidden so only the horizontal
   axis scrolls. */
.table-responsive {
    overflow-y: hidden;
}

/* Sort arrow always on the right of the column label, never
   below it. The legacy markup puts <img class="sort"> inside
   the <a> AFTER the label text; we make that anchor a flex
   row so text + arrow stay on one baseline regardless of
   column width. */
table thead th a {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    text-decoration: none;
}
table thead th a img.sort {
    margin-left: auto;
    flex-shrink: 0;
    vertical-align: middle;
}

/* Table cells must NOT inherit the break-word/anywhere rule that
   exists for alerts/modals — otherwise short labels ("ID",
   "INVENTORY") get split letter-by-letter and dates wrap mid-
   segment ("10/22/ \n 2025"). Force normal word boundaries. */
table th,
table td,
.table th,
.table td {
    word-break: normal;
    overflow-wrap: normal;
}

/* Column headers stay on one line whenever possible; if the
   designer wants a forced break they put an explicit <br>
   in the markup and `keep-all` lets that <br> work without
   the browser also breaking individual words. */
table thead th,
.table thead th {
    white-space: normal;       /* allow explicit <br> to take effect */
    word-break: keep-all;      /* but never split a word mid-letter */
    overflow-wrap: normal;
}

/* Date-shaped values ("10/22/2025", "01/01/2020") are single
   tokens with no spaces — keep them on one line via the
   `.col-date` and `.col-data` hooks already used by the
   roster table, plus a generic helper class anyone can apply. */
.nowrap-cell,
table td.nowrap-cell,
table th.nowrap-cell {
    white-space: nowrap;
}

/* ============================================================
   Roster Documents — design-handoff implementation.
   See .design/README.md for the design spec.

   STEP 1: scoping wrapper (isolation guard, no visible change).
   STEP 2 (current): design tokens declared as CSS custom
   properties on .docs-block. Tokens are SCOPED — they only
   affect descendants of .docs-block, never bleed to the rest
   of the page. Still no visible change yet because no rule
   consumes the tokens.
   ============================================================ */
.docs-block {
    /* Layout safety */
    isolation: isolate;
    min-width: 0;
    max-width: 100%;

    /* Backgrounds */
    --db-bg-card:        #ffffff;
    --db-bg-info:        #f1f5fc;
    --db-bg-row-alt:     #fafbfd;
    --db-bg-row-hover:   #f7f9fc;

    /* Borders */
    --db-border:         #e6eaf0;
    --db-border-strong:  #d6dde6;
    --db-border-soft:    #eef1f5;

    /* Text */
    --db-text-primary:   #0f172a;
    --db-text-secondary: #475569;
    --db-text-tertiary:  #6b7689;
    --db-text-muted:     #94a0b1;

    /* Brand */
    --db-primary:        #2563eb;
    --db-primary-hover:  #1d4ed8;
    --db-primary-soft:   #e8efff;
    --db-primary-ink:    #1e40af;

    /* Status colors */
    --db-danger:         #dc2626;
    --db-danger-soft:    #fee2e2;
    --db-danger-ink:     #991b1b;
    --db-success:        #059669;
    --db-success-soft:   #d1fae5;
    --db-success-ink:    #065f46;
    --db-warning:        #b45309;
    --db-warning-soft:   #fef3c7;

    /* Radii */
    --db-radius-sm: 6px;
    --db-radius:    10px;
    --db-radius-lg: 14px;

    /* Shadows */
    --db-shadow-xs: 0 1px 0 rgba(15, 23, 42, 0.04);
    --db-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 1px rgba(15, 23, 42, 0.04);
    --db-shadow-md: 0 8px 24px -8px rgba(15, 23, 42, 0.18), 0 2px 6px rgba(15, 23, 42, 0.06);
    --db-shadow-lg: 0 24px 48px -16px rgba(15, 23, 42, 0.24), 0 4px 12px rgba(15, 23, 42, 0.08);

    /* Motion */
    --db-duration: 160ms;
    --db-ease:     cubic-bezier(0.2, 0.6, 0.2, 1);
}

/* ── Step 3: info banners (replaces .alert.alert-primary) ──
   The two info banners at the top of the Documents section now
   render as `.docs-banner` inside `.docs-banners`. Light primary
   tint + soft border + Bootstrap Icons left-aligned. */
.docs-block .docs-banners {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-bottom: 20px;
}
.docs-block .docs-banner {
    display: flex;
    gap: 12px;
    padding: 12px 14px;
    border-radius: var(--db-radius);
    background: var(--db-bg-info);
    border: 1px solid #e1e9f5;
    color: var(--db-primary-ink);
    font-size: 13px;
    line-height: 1.5;
}
.docs-block .docs-banner > .bi {
    flex: 0 0 auto;
    color: var(--db-primary);
    font-size: 16px;
    margin-top: 1px;
}
.docs-block .docs-banner b {
    font-weight: 600;
}

/* ── Step 4-5: header row + data row tweaks (Bootstrap-grid based) ──
   Markup uses Bootstrap's row + col-md-* classes. Only minor
   visual polish here. */
.docs-block .docs-row-head {
    padding: 12px 4px;
    margin: 0 !important;
    border-bottom: 1px solid var(--db-border-soft);
    font-size: 11px;
    font-weight: 600;
    color: var(--db-text-tertiary);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.docs-block .docs-row {
    margin: 0 !important;
    padding: 14px 4px;
    border-bottom: 1px solid var(--db-border-soft);
    transition: background var(--db-duration) var(--db-ease);
}
.docs-block .docs-row:hover { background: var(--db-bg-row-hover); }

@media (max-width: 720px) {
    .docs-block .docs-row-head { display: none; }
}

/* ── Step 10b: dropzone hover / active-drag states ──
   Bootstrap doesn't ship hover utilities for borders/backgrounds —
   one scoped block here handles both states for the dropzone. */
.docs-block [id^="drop_area_container_"] {
    transition: border-color var(--db-duration) var(--db-ease),
                background-color var(--db-duration) var(--db-ease),
                color var(--db-duration) var(--db-ease);
}
.docs-block [id^="drop_area_container_"]:hover {
    border: 2px solid #2563eb !important;
    background-color: #e8efff !important;
    color: #1e40af !important;
}
/* Make the inner <label> transparent on hover so the container's
   hover background actually shows through (the label fills the
   dropzone area; if it has its own bg, it masks the parent). */
.docs-block [id^="drop_area_container_"]:hover [id^="drop_area_label_"] {
    background-color: transparent !important;
    color: inherit !important;
}

/* Active drag — handleDragEnterOver JS adds `.bg-secondary` on
   the inner <label>. We use :has() to catch it on the container
   AND override the grey label tint with the primary-soft design. */
.docs-block [id^="drop_area_container_"]:has(.bg-secondary) {
    border-color: var(--db-primary) !important;
    border-style: solid !important;
    background-color: var(--db-primary-soft) !important;
    color: var(--db-primary-ink) !important;
}
.docs-block [id^="drop_area_container_"]:has(.bg-secondary) [id^="drop_area_label_"] {
    background-color: transparent !important;
    color: var(--db-primary-ink) !important;
}

/* Flex shrinkability — REQUIRED for inner scroll containers
   (.table-responsive, <pre>, modal bodies) to actually expose
   their own horizontal scrollbar when the content is wider than
   the viewport.

   Without this, `div.content { flex: 1 }` (global.css) inherits
   the default `min-width: auto`, which means it CANNOT shrink
   below its intrinsic content width. Wide tables push .content
   past the viewport, where it then gets silently clipped by
   `.site-container { overflow-x: clip }` above — content is
   hidden, no scrollbar appears. Setting `min-width: 0` lets the
   flex item shrink, so any inner `overflow-x: auto` actually
   triggers a scrollbar instead of pushing the layout. */
.main,
.main > .content,
.archive,
.archive > .content,
div.content {
    min-width: 0;
}

/* ── Select2 responsive override ──
   Select2 v4 computes its rendered width once at .select2() init
   from the container's width at that moment. It does NOT track
   container resize, and it does NOT re-read CSS afterwards. This
   produces two visible bugs on this portal:
     1. On first load the widget sometimes initialises before layout
        has settled (fonts/css still applying), so the picker is
        rendered at the wrong width — and only "snaps into place"
        after a hard refresh once everything is cached.
     2. On viewport narrowing, the rendered widget keeps its
        original px width and gets clipped by the parent.
   Forcing width:100% on the Select2 wrapper makes it follow the
   container at every reflow — no JS resize listener needed. The
   `!important` is required because Select2 writes inline
   `style="width: <px>"` on the wrapper which would otherwise win.
   Scoped via `:where()` so the specificity stays at 0 and any
   intentionally-narrow Select2 instance can still opt out by
   setting an explicit `style` on the wrapper itself.
*/
.select2-container,
.select2-container--default,
.select2-container--bootstrap-5 {
    max-width: 100% !important;
    box-sizing: border-box;
}
:where(.form-row, .form-field, .form-group, .row, .col, [class^="col-"], [class*=" col-"]) > .select2-container,
:where(.form-row, .form-field, .form-group, .row, .col, [class^="col-"], [class*=" col-"]) .select2-container {
    width: 100% !important;
}
/* The dropdown is teleported to <body> on open — give it the same
   max-width guard so it can't push the viewport when the picker
   sits near the right edge on a narrow screen. */
.select2-container--open .select2-dropdown {
    max-width: calc(100vw - 16px);
}

/* ============================================================
   Required / Optional badges — global reusable utility.
   Soft, low-aggression styling per design handoff
   (.design/README.md → "Required badge" / "Optional badge").

   Use as:   <span class="badge-required">required</span>
             <span class="badge-optional">optional</span>

   Both opt out of Bootstrap's solid `.text-bg-danger` /
   `.text-bg-secondary` colour pairs in favour of soft tints.
   ============================================================ */
.badge-required,
.badge-optional {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.01em;
    white-space: nowrap;
}
.badge-required {
    background-color: #fee2e2;
    color: #991b1b;
}
.badge-optional {
    background-color: #eef1f5;
    color: #6b7689;
}

/* ============================================================
   Global primary CTA button polish — soft lift + tinted shadow
   on hover/focus, matching the Documents-block Save button.
   Applied globally to .btn-primary (blue, color preserved) and
   .btn-success (green, retuned to #059669 design token).
   ============================================================ */
.btn-primary,
.btn-success {
    border: 1px solid transparent !important;
    transition: background-color 160ms cubic-bezier(0.2,0.6,0.2,1),
                border-color    160ms cubic-bezier(0.2,0.6,0.2,1),
                color           160ms cubic-bezier(0.2,0.6,0.2,1),
                transform       160ms cubic-bezier(0.2,0.6,0.2,1),
                box-shadow      160ms cubic-bezier(0.2,0.6,0.2,1) !important;
}

/* Blue (.btn-primary) — colour stays Bootstrap default; we only
   add the tinted shadow and lift-on-hover affordance. */
.btn-primary {
    box-shadow: 0 1px 2px rgba(37, 99, 235, 0.25),
                0 4px 8px rgba(37, 99, 235, 0.12) !important;
}
.btn-primary:hover:not(:disabled):not(.disabled),
.btn-primary:focus-visible:not(:disabled):not(.disabled) {
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(37, 99, 235, 0.35),
                0 2px 4px  rgba(37, 99, 235, 0.18) !important;
}

/* Green (.btn-success) — full retune to design token #059669
   with green-tinted shadow, hover deepens to #047857 + lift. */
.btn-success {
    background-color: #059669 !important;
    border-color: #059669 !important;
    color: #fff !important;
    box-shadow: 0 1px 2px rgba(5, 150, 105, 0.25),
                0 4px 8px rgba(5, 150, 105, 0.12) !important;
}
.btn-success:hover:not(:disabled):not(.disabled),
.btn-success:focus-visible:not(:disabled):not(.disabled) {
    background-color: #047857 !important;
    border-color: #047857 !important;
    color: #fff !important;
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(5, 150, 105, 0.35),
                0 2px 4px  rgba(5, 150, 105, 0.18) !important;
}

/* Press / active — settle back to baseline. */
.btn-primary:active:not(:disabled):not(.disabled),
.btn-success:active:not(:disabled):not(.disabled) {
    transform: translateY(0);
}

/* Disabled — neutral grey, no shadow, no transform. */
.btn-primary:disabled,
.btn-primary.disabled,
.btn-success:disabled,
.btn-success.disabled {
    background-color: #cbd5e1 !important;
    border-color: #cbd5e1 !important;
    color: #fff !important;
    box-shadow: none !important;
    cursor: not-allowed;
    transform: none !important;
}

/* ============================================================
   Drag-and-drop dropzone — global field appearance.
   Targets every roster-documents drop-area instance on the site
   by id-prefix (the markup convention is shared across the
   constructor's Step 3, the Roster Details Documents block, and
   any future DnD that uses the same id naming).
   Covers idle / hover / active-drag / error states only — no
   surrounding card/footer/chip styling, so it works on legacy
   markup too without requiring structural changes.
   ============================================================ */
[id^="drop_area_container_"] {
    border-color: #adb5bd !important;
    background: #fff;
    min-height: 60px;
    transition: border-color 160ms cubic-bezier(0.2,0.6,0.2,1),
                background-color 160ms cubic-bezier(0.2,0.6,0.2,1),
                color 160ms cubic-bezier(0.2,0.6,0.2,1);
}

[id^="drop_area_container_"]:hover {
    border-color: #2563eb !important;
    border-style: solid !important;
    background-color: #e8efff !important;
    color: #1e40af !important;
}

[id^="drop_area_container_"]:hover [id^="drop_area_label_"] {
    color: #1e40af !important;
}

/* Active-drag state — JS handlers attach `.bg-secondary` to the
   inner <label> while a file is being dragged over the dropzone. */
[id^="drop_area_container_"]:has(.bg-secondary) {
    border-color: #2563eb !important;
    border-style: solid !important;
    background-color: #e8efff !important;
}
[id^="drop_area_container_"]:has(.bg-secondary) [id^="drop_area_label_"] {
    background-color: transparent !important;
    color: #1e40af !important;
}

/* Validation error (non-PDF / oversized drop). The .docs-dropzone-error
   class is added by validateDropBatch() on the modern template; legacy
   templates without that JS won't trigger this branch. */
[id^="drop_area_container_"].docs-dropzone-error {
    border-color: #dc2626 !important;
    border-style: solid !important;
    background-color: #fff5f5 !important;
    color: #991b1b !important;
}
[id^="drop_area_container_"].docs-dropzone-error [id^="drop_area_label_"] {
    color: #991b1b !important;
}
[id^="drop_area_container_"].docs-dropzone-error [id^="drop_area_label_"] .text-muted {
    color: #b91c1c !important;
}
[id^="drop_area_container_"].docs-dropzone-error .bi-cloud-arrow-up {
    color: #dc2626 !important;
}

[id^="drop_area_label_"] {
    color: #475467;
    font-weight: 500;
}

/* Legacy JS handler (handleDragEnterOver) attaches `.bg-secondary
   text-white fw-bold` to the inner <label> on drag-over to highlight
   it. With the new container-level hover/drag styling those classes
   add a dark-grey overlay that fights the light-blue bg. Neutralize
   them — the container already paints itself. */
[id^="drop_area_label_"].bg-secondary {
    background-color: transparent !important;
    color: #1e40af !important;
}
[id^="drop_area_container_"]:hover [id^="drop_area_label_"],
[id^="drop_area_container_"]:has(.bg-secondary) [id^="drop_area_label_"] {
    background-color: transparent !important;
}

/* ============================================================
   Upload spinner (.progress-spinner) — global so every PDF-upload
   surface (Roster Documents block, constructor Step 3, profile
   Instructor Essentials / Certificates 2025) renders the same
   conic-gradient circle while the form is processing.
   ============================================================ */
.progress-spinner {
    width: 100px;
    height: 100px;
    background: conic-gradient(#fff, #91b9ff, #005dff);
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
}
.progress-spinner::after {
    content: '';
    background: white;
    width: 100%;
    height: 100%;
    transform: scale(0.6);
    border-radius: 50%;
}
.spinning {
    display: flex;
    animation: progress-spinner-rotate 2s linear infinite;
}
@keyframes progress-spinner-rotate {
    0%   { transform: rotate(0deg);   }
    100% { transform: rotate(360deg); }
}
/* Backward-compat alias — older templates use animation: rotate. */
@keyframes rotate {
    0%   { transform: rotate(0deg);   }
    100% { transform: rotate(360deg); }
}

/* ==========================================================================
   Pagination footer — shared across all listing pages.
   Layout: "Showing N of M items" left, "← Previous Next →" right (same row);
   numbered page links flow underneath, left-aligned.
   Buttons render with white background + gray border, gray on hover, dark
   gray for the active page.
   ========================================================================== */
.paging {
    position: relative;
    padding-top: 0.25rem;
}
.paging .paging-summary {
    position: absolute;
    top: 0.25rem;
    left: 0;
    margin: 0;
    color: #6c757d;
    font-size: 0.875rem;
}
.paging > p {
    text-align: right;
    margin: 0 0 0.5rem 0;
}
.paging .pagination .page-link {
    background-color: #fff !important;
    border: 1px solid #dee2e6 !important;
    color: #0d6efd !important;
}
.paging .pagination .page-link:hover,
.paging .pagination .page-link:focus {
    background-color: #e9ecef !important;
    border-color: #dee2e6 !important;
    color: #0a58ca !important;
}
.paging .pagination strong.page-link,
.paging .pagination .page-item.active .page-link {
    background-color: #495057 !important;
    border-color: #495057 !important;
    color: #fff !important;
}
