/* Global application font - applied to every page via _Layout/_Head.
   Kept in one place so navigating between pages never changes the typeface. */
html,
body,
.page-wrapper,
.page-content,
.panel,
.form-control,
.btn,
.input-group-text,
.nav,
.dropdown-menu,
table,
input,
select,
textarea,
button {
    font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif !important;
}

/* Bootstrap Validation Summary Override */
.validation-summary-valid {
    display: none;
}

.validation-summary-errors ul {
    margin-bottom: 0;
    padding-left: 1rem;
}

.alert.validation-summary-errors {
    margin-bottom: 1rem;
}

/* Reference classes, no implementation */
.js-waves-off, .js-waves-on, .js-get-date, .js-collapse, .js-modal-settings, .js-filter-message, .js-buttons, .js-icon-class, .js-icon-copy, .base, .needs-validation, .flex-start, .text-faded, .app-container-demo, .has-tooltip, .has-popover {
}

.js-bg-color, .js-bg-gradient, .js-bg-gradient-demo, .js-bg-target {
}

/* Enter custom CSS rules for your application here */
table .vam {
    vertical-align: middle;
    padding-left: 0.75rem;
}

table .hal {
    text-align: left;
}

table .vac {
    text-align: center;
}

table .var {
    text-align: right;
}

/* .vat is written in the views (top-aligned grid cell) but was never implemented, so those
   cells fell back to whatever the theme happened to give them and drifted out of line with
   their .vam neighbours. Same padding as .vam so the two can sit side by side in one row. */
table .vat {
    vertical-align: top;
    padding-left: 0.75rem;
}

/* Grid cells that stack several "Label : Value" pairs (HRMS Leave Action, Local Visit, ...)
   nest a plain <table class="table"> inside the <td>. Bootstrap's selectors are descendant
   based, so that inner table inherited the outer grid's borders, striping and hover tint plus
   0.75rem cell padding and a 1rem bottom margin - every pair rendered as its own boxed row and
   the cell grew about three times taller than its content. .clsCellList strips it back to a
   compact, border-free list: first <td> is the label (fixed width, never wraps), second is the
   ":" separator, third is the value. */
.clsCellList {
    width: 100%;
    margin: 0;
}

.table .clsCellList {
    background-color: transparent;
}

/* Alternate the pairs inside the cell so the eye can follow one line of the list across the
   Location / Application / Approval columns. The cells are top-aligned and every pair is a
   single line, so the tints line up as bands straight across the grid row.
   Both rules have to outrank two descendant selectors that reach into the nested table from the
   outer grid - .table-striped tbody tr:nth-of-type(odd) and .table-hover tbody tr:hover, each
   (0,2,2) - hence .table + .clsCellList + :nth-of-type. The even rows are explicitly transparent
   so the outer row's own colour (hover tint, or the purple profile header) shows through. */
.table .clsCellList > tbody > tr:nth-of-type(odd),
.table .clsCellList > tr:nth-of-type(odd) {
    background-color: #f1eef8;
}

.table .clsCellList > tbody > tr:nth-of-type(even),
.table .clsCellList > tr:nth-of-type(even) {
    background-color: transparent;
}

.clsCellList > tbody > tr > td,
.clsCellList > tr > td {
    border: 0 !important;
    padding: 3px 0;
    line-height: 1.4;
}

/* Labels are right aligned so they sit hard against the ":" column: the separators line up in
   one straight run and the values all start at the same x, whatever the label's length. */
.clsCellList > tbody > tr > td:first-child,
.clsCellList > tr > td:first-child {
    width: 7rem;
    white-space: nowrap;
    text-align: right;
    padding-left: 4px;
    padding-right: 8px;
}

.clsCellList > tbody > tr > td:nth-child(2),
.clsCellList > tr > td:nth-child(2) {
    width: 10px;
    padding-right: 8px;
}

/* File-picker form rows used to stretch the whole page width. Bootstrap gives
   .input-group > .custom-file `flex: 1 1 auto`, so the picker soaked up every pixel the row
   had left over, while the textbox/dropdown rows above and below it are capped by each view's
   .clsDControl (20rem in 498 of the 509 views that set it). Cap the picker to the same width
   so an upload row lines up with the rest of the form; it still shrinks on narrow screens.
   Scoped to .frame-wrap so full-width pickers outside the form rows (e.g. the Import dialog)
   are untouched. */
.frame-wrap .input-group > .custom-file {
    flex: 0 1 20rem;
    max-width: 20rem;
}

/* An upload field has two companions - the "1 file is ready to be saved." line and a picture of
   what is stored right now - and both were written as siblings of the row: full-width blocks,
   pinned to the card's left edge, separated from their own field by the 3rem bottom margin every
   .frame-wrap carries. On a form whose rows end around 40rem that reads as two stray pictures in
   a column of their own rather than as part of the field above them.
   .clsUploadRow makes the field own them: the status line sits inline to the picker's right and
   the preview sits DIRECTLY under the picker - the BSAF Profile page's shape, which is the house
   standard for every upload field (2026-08-15). The placement is decided here, not per view, so
   all 105 views that carry these classes look the same without touching their markup. */
.clsUploadRow {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.85rem;
    row-gap: 0.35rem;
}

    /* The row's two lines are built with `order`, not with markup order, because the views
       disagree on where they write the thumbnail: most emit picker -> thumb -> status, the
       Profile page emits picker -> status -> thumb. Ordering here makes both render alike.
       ::after is a flex ITEM of the container (not of any child), so a zero-height item with
       flex-basis:100% between the status and the preview is what forces the line break - a
       preview given flex-basis:100% itself would stretch its <a> across the whole row and turn
       the empty space beside the picture into a click target. */
    .clsUploadRow > .clsUploadStatus {
        order: 1;
    }

    .clsUploadRow::after {
        content: "";
        order: 2;
        flex: 0 0 100%;
        height: 0;
    }

    .clsUploadRow > .clsUploadThumb {
        order: 3;
    }

    /* ...and it lines up UNDER the picker, not under the row.
       The picker is wrapped in a col-* whose Bootstrap gutter indents its content 15px from the
       row's edge, while the preview is a flex item of the row itself and starts at that edge - so
       without this the picture hangs 15px to the left of every field on the form. Conditional
       because ~20 of the 105 upload rows write a bare .input-group with no col-* at all: there the
       picker has no gutter to match and a blanket indent would push the preview out the other way. */
    .clsUploadRow:has(> .input-group[class*="col-"]) > .clsUploadThumb {
        margin-left: 15px;
    }

    /* Content-sized, not full width: .input-group and the col-* class on it both ask for 100%,
       which would leave the thumbnail no room and push it onto a line of its own again.
       nowrap because this row is the one place the group is not full width, and a group allowed
       to wrap inside a shrink-to-fit box breaks its own line before it runs out of page. */
    .clsUploadRow > .input-group {
        flex: 0 1 auto;
        width: auto;
        max-width: 100%;
        flex-wrap: nowrap;
        margin-bottom: 0;
    }

        /* Bootstrap sizes .custom-file as width:100%, which means nothing inside a shrink-to-fit
           row: the picker collapses to the width of the file name and the row folds under its own
           label. A definite width restores the cap every other picker on the form gets.
           Same selector shape as the cap above so it cannot be out-specified by it.
           15.4rem + the 4.6rem Upload append below = the same 20rem every .clsDControl gets, so
           the Upload button's right edge lines up with the controls above and below the row. */
        .clsUploadRow .input-group > .custom-file {
            flex: 0 1 15.4rem;
            width: 15.4rem;
        }

        /* min-width, not width: the standard label is "Upload", which this sizes to close the
           20rem total; a view with a longer label may grow past it rather than clip. */
        .clsUploadRow .input-group-append > .input-group-text {
            min-width: 4.6rem;
            justify-content: center;
        }

/* The preview card that sits under the picker. A fixed box, so the field keeps one height
   whether the image is portrait, landscape or the NoImage placeholder, and so two upload rows
   under each other line up. 100px picture + 10px padding is the size the Profile page has always
   shown; every other view now inherits it from here instead of hard-coding it inline. */
.clsUploadThumb {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 7.5rem;
    height: 7.5rem;
    padding: 10px;
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    background: #fff;
}

@media (max-width: 575px) {
    .clsUploadRow > .input-group { flex-wrap: wrap; }
    .clsUploadRow > .input-group > .input-group-prepend { flex-basis: 100%; }
    .clsUploadRow .custom-file { min-width: 0; }
}

    .clsUploadThumb:hover {
        border-color: #886ab5;
    }

    /* contain, not cover: a logo cropped to a square is worse than a small logo. */
    .clsUploadThumb > img {
        max-width: 100%;
        max-height: 100%;
        object-fit: contain;
    }

/* Stays a block-level flex item on purpose. jQuery .show() writes display:block onto this
   element, so its place on the line has to come from the flex container, never from a display
   of its own. */
.clsUploadStatus {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: 0.8rem;
    color: #6c757d;
}

/* Bootstrap Modal Fix - Ensures modals display properly above backdrop */
.modal {
    z-index: 1055 !important;
}

.modal-backdrop {
    z-index: 1050 !important;
}

.modal-dialog {
    z-index: 1056 !important;
    position: relative;
    margin: 1.75rem auto;
    pointer-events: auto;
}

.modal.fade .modal-dialog {
    transition: transform 0.3s ease-out;
    transform: translate(0, -50px);
}

.modal.show .modal-dialog {
    transform: none;
}

/* Ensure modal content is properly styled and visible */
.modal-content {
    position: relative;
    display: flex;
    flex-direction: column;
    width: 100%;
    pointer-events: auto;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid rgba(0, 0, 0, 0.2);
    border-radius: 0.3rem;
    outline: 0;
}

/* Fix for modals not appearing when fade class is present */
.modal.fade:not(.show) {
    opacity: 0;
}

.modal.fade.show {
    opacity: 1;
}

/* Ensure Bootstrap modal displays correctly */
.modal-open {
    overflow: hidden;
}

.modal-open .modal {
    overflow-x: hidden;
    overflow-y: auto;
}

/* Additional fix for modal-dialog-centered */
.modal-dialog-centered {
    display: flex;
    align-items: center;
    min-height: calc(100% - 3.5rem);
}

/* Fix for modal body scrolling */
.modal-body {
    position: relative;
    flex: 1 1 auto;
    padding: 1rem;
}

/* Fix for modal header */
.modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    padding: 1rem;
    border-bottom: 1px solid #dee2e6;
    border-top-left-radius: calc(0.3rem - 1px);
    border-top-right-radius: calc(0.3rem - 1px);
}

.modal-header .close {
    padding: 1rem;
    margin: -1rem -1rem -1rem auto;
}

/* Fix for modal footer */
.modal-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    padding: 0.75rem;
    border-top: 1px solid #dee2e6;
    border-bottom-right-radius: calc(0.3rem - 1px);
    border-bottom-left-radius: calc(0.3rem - 1px);
}

/* Always-visible thin scrollbar — overrides Windows 11 auto-hide */
html {
    scrollbar-gutter: stable;
}

* {
    scrollbar-width: thin;
    scrollbar-color: #886ab5 #f1f1f1;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: #f1f1f1;
}

::-webkit-scrollbar-thumb {
    background: #886ab5;
    border-radius: 5px;
}

::-webkit-scrollbar-thumb:hover {
    background: #6e54a0;
}

/* ═══ Report filter popups (.clsReptForm) ═══════════════════════════════════
   Every module's "Reports" page opens the same shaped dialog: Profile Name, a
   module filter or two, the shared Date Range / Ignore toggle, From + To dates
   and Format. Those rows were copy-pasted across 141 popups and drifted - label
   columns of 7 / 9 / 11 / 12rem, controls either stretching to the modal edge or
   pinned to a hand-measured width (10.6 / 11.7 / 11.9 / 18.2 / 20.3 / 27.6rem),
   and per-row negative margins tuned against whichever sibling happened to be
   there at the time. Inside one popup that reads as a ragged left AND right edge,
   most visibly against the Date Range row, which is the one row every popup gets
   from a single shared partial and so never drifted with the rest.

   The geometry now lives here instead of in 141 files: the popup form carries
   .clsReptForm, and every row gets one label column, one control width and one
   vertical rhythm regardless of the legacy margins still in its own markup.
   Overriding those with !important is deliberate - these views are maintained by
   copy-paste, so the next copy inherits the fix instead of the drift. */

.clsReptForm {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
    /* Keep report filters compact, including inside larger legacy dialogs. */
    max-width: calc(525px - 2rem - 2px);
    margin-left: 0 !important; /* cancels .ml-2, so rows start on the body's padding edge */
}

/* Legacy row scaffolding: .frame-wrap / .grid_item / .dvFieldGroup nest 2-4 deep and each
   level carries its own margin. Flatten them all; the gap above is the only spacing. */
.clsReptForm > div,
.clsReptForm [id^="dvDateRange"] > div,
.clsReptForm [id^="dvFieldGroup"],
.clsReptForm .dvFieldGroup,
.clsReptForm .grid_item,
.clsReptForm .frame-wrap {
    margin: 0 !important;
    padding: 0 !important;
    background: none;
}

/* _001_ButtonAndTab.cshtml - on every list and report page - makes div.dvFieldGroup a two-column
   grid so an AddOrUpdate form can pair two short fields on one line. In a 600px report dialog that
   pairs From Date with To Date and content-sizes both, which is where the popup's right edge starts
   fraying. Stack them like every other row here. Specificity (0,2,1) beats that rule's (0,1,1)
   WITHOUT !important on display, so the nine popups that inline display:none on this element to
   keep their dates permanently hidden stay hidden. */
.clsReptForm div.dvFieldGroup {
    display: flex;
    flex-direction: column;
    /* that rule also sets align-items:start, which in a column means "shrink each row to its own
       content" - the single biggest source of the ragged right edge. Rows stretch here. */
    align-items: stretch;
    justify-content: flex-start;
    gap: 0.75rem;
    width: auto;
}

/* From/To live in a wrapper the Ignore checkbox toggles; keep the same rhythm inside it.
   jQuery .toggle() only ever writes display:none / "", so this survives being re-shown. */
.clsReptForm [id^="dvDateRange"] {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* 9 popups (My Office, Construction, Study SERT) keep their From/To rows permanently hidden -
   the inputs still feed the download URL, only the UI is gone. That leaves a zero-height wrapper
   that would still claim its gap, opening a double gap between Date Range and Format. Cancel it;
   the selector stops matching by itself the moment one of those rows is shown again. */
.clsReptForm [id^="dvDateRange"]:not(:has(> div:not([style*="display:none"]):not([style*="display: none"]))) {
    margin-top: -0.75rem !important;
}

/* One row = prepend (icon + label) + one control that takes the rest of the width,
   so every row in the dialog starts and ends on the same pixel. */
.clsReptForm .input-group {
    flex-wrap: nowrap !important;
    width: 100% !important;
    max-width: none !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* 97 popups end with an optional "Include Only my entries" checkbox carrying .ml-auto. That was
   inert while its row was content-width; now that rows fill the form it would fling the checkbox
   to the far right, away from every other control. Keep it on the left where it has always been. */
.clsReptForm .ml-auto {
    margin-left: 0 !important;
}

/* the date rows wrap a second .input-group around the field; it has to fill the outer one */
.clsReptForm .input-group > .input-group {
    flex: 1 1 auto !important;
    max-width: none !important;
}

.clsReptForm .input-group-text {
    white-space: nowrap;
}

/* icon cell */
.clsReptForm .input-group-prepend > .input-group-text:first-child,
.clsReptForm .input-group > .input-group-text:first-child {
    min-width: 2.9rem !important;
    justify-content: center;
}

/* label cell */
.clsReptForm .input-group-prepend > .input-group-text:nth-child(2),
.clsReptForm .input-group > .input-group-text:nth-child(2) {
    min-width: 8.25rem !important;
    font-weight: bold;
}

.clsReptForm .form-control,
.clsReptForm .custom-select {
    flex: 1 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    margin-right: 0 !important;
}

/* Keep reload buttons beside the select; old negative margins let the select cover them. */
.clsReptForm .clsActionButton {
    margin-left: 0.5rem !important;
    flex: 0 0 auto;
}

/* Download / action button: same left edge as the rows, a little more air above them */
.clsReptForm > .btn,
.clsReptForm > a.btn {
    align-self: flex-start;
    margin: 0.75rem 0 0 0 !important;
}

/* ==========================================================================
   Change Log popup (Shared-GUI/_004_ChangeLog.cshtml + js/baf-change-log.js)
   --------------------------------------------------------------------------
   Here rather than in a <style> block inside the partial, for the same reason
   .clsReptForm above is: that partial renders on every grid page, so inline
   rules would be re-sent ~600 times a session instead of cached once.

   Built on the theme's own notification-feed anatomy (custom-scroll >
   ul.notification > li) so it inherits the surrounding look; only the audit
   specifics - the event badge row, the field diff - are declared.
   ========================================================================== */
.clsChangeLog {
    padding-top: 0.75rem;
}

.clsChangeLogBar {
    display: flex;
    align-items: center;
    padding: 0 0.25rem 0.5rem 0.25rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.clsChangeLogSpacer {
    flex: 1 1 auto;
}

/* Bounded rather than free-growing: the modal is fixed-height in this theme,
   and a long history must scroll inside the popup, not push its footer away. */
.clsChangeLogList {
    max-height: 60vh;
    min-height: 8rem;
    overflow-y: auto;
    overflow-x: hidden;
}

.clsChangeLog ul.notification {
    margin: 0;
    padding: 0;
    list-style: none;
}

/* The theme styles the FIRST CHILD of every .notification li as a clickable feed row:
   `.notification li > :first-child { padding:.75rem 1.5rem; border-bottom:1px solid ... }`
   plus a hover gradient (app.bundle.css). Borrowing ul.notification for its layout means
   inheriting that, and it lands in two places it does not belong - on each entry's own wrapper
   (a second horizontal rule under every row) and on the first field label of every diff (turning
   a compact one-line "Name: old -> new" into a fat underlined block). The selectors below run at
   (0,3,1) so they win, and the :hover twin is load-bearing - the theme's own hover rule is (0,3,1)
   too and would otherwise win on source order.

   :not(.clsChangeLogField) excludes the diff's own label, which is ALSO a li > :first-child but DOES
   want its right-hand gutter: a padding shorthand at (0,3,1) beats the padding-right further down at
   (0,1,0), which would butt the struck-through old value straight against the label. */
.clsChangeLog .notification li > :first-child:not(.clsChangeLogField),
.clsChangeLog .notification li > :first-child:not(.clsChangeLogField):hover {
    padding: 0;
    border-bottom: 0;
    background-image: none;
}

/* The label keeps its gutter but still loses the theme's border and hover tint. */
.clsChangeLog .notification li > .clsChangeLogField,
.clsChangeLog .notification li > .clsChangeLogField:hover {
    padding: 0 0.5rem 0 0;
    border-bottom: 0;
    background-image: none;
}

.clsChangeLogItem {
    padding: 0.65rem 0.25rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.clsChangeLogItem:last-child {
    border-bottom: none;
}

/* The record's own name and code, at the head of its block. Every entry underneath belongs to this
   record, so none of them repeats it - the entries carry a name only where it CHANGED. */
.clsChangeLogBand {
    padding: 0.4rem 0.25rem 0.45rem 0.25rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    font-size: 0.9rem;
    color: #495057;
}

/* Several rows ticked = several records in one feed. baf-change-log.js keeps each record's entries
   together and tags the head of every band after the first, so the reader sees where one record's
   history ends and the next begins instead of counting code badges down an interleaved timeline.
   The band's last entry drops its own hairline (clsChangeLogGroupEnd) so the break reads as one
   stronger rule with air around it, not two rules with a blank strip between them. */
.clsChangeLogBand.clsChangeLogGroupStart {
    margin-top: 0.9rem;
    padding-top: 0.9rem;
    border-top: 2px solid rgba(0, 0, 0, 0.12);
}

.clsChangeLogItem.clsChangeLogGroupEnd {
    border-bottom: none;
}

.clsChangeLogRow {
    display: flex;
    align-items: flex-start;
}

.clsChangeLogRow .profile-image {
    width: 32px;
    height: 32px;
}

.clsChangeLogBody {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;      /* lets long names ellipsize instead of widening the modal */
}

.clsChangeLogTitle {
    line-height: 1.6;
    word-break: break-word;
}

.clsChangeLogMeta {
    margin-top: 0.1rem;
}

.clsChangeLogMore {
    margin-top: 0.25rem;
    font-size: 0.8rem;
}

/* The diff. One line per field: label, before, arrow, after. */
.clsChangeLogFields {
    margin: 0.35rem 0 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.82rem;
}

.clsChangeLogFields > li {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    padding: 0.1rem 0;
}

.clsChangeLogField {
    min-width: 9rem;
    font-weight: 600;
    padding-right: 0.5rem;
}

.clsChangeLogOld {
    color: #a0a0a0;
    text-decoration: line-through;
    word-break: break-word;
}

.clsChangeLogArrow {
    margin: 0 0.45rem;
    color: #a0a0a0;
}

.clsChangeLogNew {
    font-weight: 600;
    word-break: break-word;
}

/* Image fields, drawn as the picture rather than read as the path it is stored at - see the argument
   in baf-change-log.js. Only the line that actually carries one is re-aligned: the rest of the diff
   is text on a baseline and should stay there. */
.clsChangeLogFields > li.clsChangeLogImageRow {
    align-items: center;
    padding: 0.3rem 0;
}

.clsChangeLogThumbLink {
    display: inline-block;
    line-height: 0;          /* no descender gap under the thumbnail */
}

.clsChangeLogThumb {
    width: 40px;
    height: 40px;
    object-fit: cover;       /* a portrait and a banner both read as one tidy row of squares */
    border-radius: 4px;
    border: 1px solid rgba(0, 0, 0, 0.12);
    background-color: #fff;
}

/* The value that WAS. A line-through cannot mark a picture the way it marks text, so the old
   thumbnail is dimmed and dashed instead - and comes back to full strength on hover, because
   comparing the two is the whole point of having them side by side. */
.clsChangeLogThumbOld {
    opacity: 0.5;
    border-style: dashed;
}

.clsChangeLogThumbLink:hover .clsChangeLogThumbOld {
    opacity: 1;
}

/* The file is gone - the CDN sweeps abandoned uploads, and the oldest entries here predate that
   migration entirely. The file NAME still answers "which picture was it", which a broken-image icon
   does not. Its own line-height because the link above sets that to 0 for the thumbnail's sake. */
.clsChangeLogThumbGone {
    line-height: 1.35;
    font-size: 0.78rem;
    word-break: break-all;
}

/* One shared class for "nothing to show" AND "it could not be read". The two
   are worded differently on purpose - looking identical is what makes a broken
   audit trail read as an empty one. */
.clsChangeLogEmpty {
    padding: 1.25rem 0.5rem;
    text-align: center;
    color: #868e96;
    border-bottom: none;
}

.clsChangeLogNoDiff {
    margin-top: 0.25rem;
    font-style: italic;
}

/* ==========================================================================
   System Data publishing popup
   (Shared-GUI/_013_SystemData.cshtml + js/baf-system-data-publish.js)
   --------------------------------------------------------------------------
   Here rather than in a <style> block inside the partial for the same reason
   as the two blocks below: the partial renders on every grid page the product
   owner opens, so inline rules would be re-sent on each one instead of cached
   once.

   Width matters most. The list is six columns wide, and at Bootstrap 4.5's
   500px .modal-dialog cap the description and owner columns collapse to one
   word per line. That cap is a (0,1,0) rule; the selector below is (0,2,0),
   so it wins without !important.
   ========================================================================== */
.modal .clsSysPubDialog {
    max-width: 1180px;
    width: 96%;
    margin-left: auto;
    margin-right: auto;
}

.clsSysPub {
    padding-top: 0.75rem;
}

/* Says what publishing does, above the list. Understated on purpose: it is
   context, not a warning - nothing here can damage a tenant's own data, and as
   the alert panel it used to be it took the height the list needs. */
.clsSysPubNote {
    font-size: 0.8rem;
    line-height: 1.35;
    color: #6c757d;
    margin: 0 0 0.6rem;
}

/* Scope switch, filters and reload on one line. The switch is hidden when the
   popup was opened with nothing ticked in the grid and the filters are hidden
   in the picked-rows scope, so this row has to survive either of its first two
   children being absent - hence margin-left:auto on the reload rather than a
   fixed three-column split. */
.clsSysPubBar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.6rem;
}

.clsSysPubScope .badge {
    margin-left: 0.25rem;
}

.clsSysPubFilter {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.clsSysPubFilter #ddlSysPubProfile {
    width: 16rem;
}

.clsSysPubFilter #txtSysPubSearch {
    width: 18rem;
}

.clsSysPubBar #btnSysPubReload {
    margin-left: auto;
}

/* The rows carried in from the grid, in the scope that shows the whole form.
   Faint on purpose - it is a reminder of where they came from, not a state. */
.clsSysPubSeeded > td {
    background-color: rgba(134, 142, 150, 0.07);
}

.clsSysPubHead {
    padding: 0.5rem 0.75rem;
}

.clsSysPubTitle {
    font-weight: bold;
}

.clsSysPubBody {
    padding: 0;
}

/* The list scrolls on its own, so a form with 300 rows cannot push the two
   action buttons off the bottom of the popup. Deliberately NOT
   class="custom-scroll" - the theme declares
   `body:not(.no-slimscroll) .custom-scroll { overflow: hidden }` at a
   specificity nothing in this file can beat, and the list would be clipped
   with no way to scroll to the rest.

   It gets the height the second list used to take: one list of 30 rows reads
   as a list, two of 15 read as a screenful of tables. */
.clsSysPubList {
    overflow: auto;
    max-height: 380px;
}

.clsSysPubTable {
    margin-bottom: 0;
    white-space: nowrap;
}

/* The two free-text columns. Everything else is an identifier or a badge and
   reads better on one line. */
.clsSysPubTable .clsSysPubWrap {
    white-space: normal;
}

.clsSysPubEmpty {
    padding: 18px;
}

/* Shown only when something was skipped or failed - a clean run says so in the
   list, which has just relabelled the rows it acted on. */
.clsSysPubResult {
    margin-top: 0.75rem;
    max-height: 160px;
    overflow: auto;
}

.clsSysPubIssues {
    padding: 0.5rem 0.75rem;
    font-size: 0.82rem;
    margin-bottom: 0;
}

.clsSysPubIssues ul {
    margin: 0.35rem 0 0;
    padding-left: 1.1rem;
}

/* The outcome message sits at the far left of the footer (mr-auto), so the
   buttons must not be pushed off a narrow popup by a long sentence. */
.clsSysPubFooter #spSysPubMessage {
    font-size: 0.82rem;
    max-width: 46%;
}

/* ==========================================================================
   Cache popup (Shared-GUI/_012_Cache.cshtml + js/baf-cache-viewer.js)
   --------------------------------------------------------------------------
   Here rather than in a <style> block inside the partial, for the same reason
   the Change Log rules above are: that partial renders on every grid page, so
   inline rules would be re-sent ~600 times a session instead of cached once.

   The one thing this popup needs that the others do not is WIDTH. Every row
   carries a full cache key ("U:1:O:1:1_BSAF_V1_01_02_ReportSettingViewData:P:12")
   and a key wrapping over three lines is unreadable next to the size it
   belongs to. Bootstrap 4.5 caps .modal-dialog at 500px in its own (0,1,0)
   rule; the selector below is (0,2,0), so it wins without !important.
   ========================================================================== */
.modal .clsCacheDialog {
    max-width: 1180px;
    width: 96%;
    margin-left: auto;
    margin-right: auto;
}

.clsCache {
    padding-top: 0.75rem;
}

.clsCacheBar {
    display: flex;
    align-items: center;
    padding: 0 0.25rem 0.5rem 0.25rem;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.clsCacheSpacer {
    flex: 1 1 auto;
}

/* The page's route, next to its name. Monospaced because it is an identifier -
   the same one that appears in the URL and in the API's own [Route]. */
.clsCacheRoute {
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.78rem;
    color: #868e96;
    margin-left: 0.5rem;
}

.clsCacheSummary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 1.25rem;
    padding: 0.6rem 0.25rem;
    font-size: 0.85rem;
    color: #495057;
}

.clsCacheStat b {
    font-size: 1rem;
}

/* Caching being OFF is the explanation for an empty list, so it reads as a
   state rather than as a statistic. */
.clsCacheStateOn {
    color: #1a9e5f;
}

.clsCacheStateOff {
    color: #c72f76;
}

/* Bounded rather than free-growing: the modal is fixed-height in this theme,
   and a long list must scroll inside the popup, not push its footer away. */
.clsCacheList {
    max-height: 55vh;
    min-height: 8rem;
    overflow-y: auto;
    overflow-x: hidden;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

.clsCacheTable {
    width: 100%;
    font-size: 0.85rem;
    table-layout: fixed;
}

    .clsCacheTable > thead > tr > th {
        position: sticky;
        top: 0;
        z-index: 1;
        background: #fff;
        padding: 0.5rem 0.5rem;
        border-bottom: 1px solid rgba(0, 0, 0, 0.12);
        font-weight: 600;
        white-space: nowrap;
    }

    .clsCacheTable > tbody > tr > td {
        padding: 0.5rem;
        border-bottom: 1px solid rgba(0, 0, 0, 0.06);
        vertical-align: top;
    }

/* Fixed layout, so the key column is the one that absorbs the slack - the same
   rule the grids follow. */
.clsCachePickCell {
    width: 2.4rem;
    text-align: center;
}

.clsCacheNum {
    width: 7.5rem;
    text-align: right;
    white-space: nowrap;
}

.clsCacheWho {
    width: 16rem;
}

.clsCacheGroup > td {
    background-color: #efe9ff;
    color: #402080;
    padding-top: 4px !important;
    padding-bottom: 4px !important;
}

/* Another user's copy is still this org's data and can still be dropped, but it
   is not what someone triaging their own stale page is usually looking for. */
.clsCacheRowOther {
    background-color: #fbfbfd;
}

.clsCacheDetail {
    display: inline-block;
    font-size: 0.8rem;
    color: #495057;
}

/* The key is an identifier: monospaced, and allowed to break anywhere, because
   it has no spaces and would otherwise force the table wider than the popup. */
.clsCacheKey {
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.74rem;
    color: #868e96;
    word-break: break-all;
    line-height: 1.35;
}

/* One shared class for "nothing is cached" AND "it could not be read". The two
   are worded differently on purpose - looking identical is what makes a broken
   lookup read as an empty one. */
.clsCacheEmpty {
    padding: 1.25rem 0.5rem;
    text-align: center;
    color: #868e96;
}

.clsCacheNote {
    margin-top: 0.6rem;
    padding: 0.55rem 0.75rem;
    border-radius: 4px;
    background-color: #eaf7f0;
    color: #12603a;
    font-size: 0.85rem;
}

.clsCacheNoteWarn {
    background-color: #fdf3e6;
    color: #8a5300;
}

/* Remove-selected sits apart from the other two: it is the only button whose
   effect depends on what is ticked, and pairing it with "Clear everything" is
   how you get the wrong one clicked. The auto margin pushes the rest right,
   which is where the theme already puts modal buttons. */
.clsCacheFooter #btnCacheRemove {
    margin-right: auto;
}

/* ==========================================================================
   Export popup (Shared-GUI/_009_Export.cshtml + js/baf-grid-export.js)
   --------------------------------------------------------------------------
   Here rather than in a <style> block inside the partial, for the same reason
   the two above are: that partial renders on every grid page, so inline rules
   would be re-sent ~600 times a session instead of cached once.

   Three rows of label + control. Deliberately NOT built on .clsReptForm, which
   the report popups share: that is a grid whose geometry is tuned for a date
   range and a format, and borrowing it would tie this popup's layout to any
   future change made for their sake.
   ========================================================================== */
.clsGridExport {
    padding: 1.1rem 1.25rem 0.75rem 1.25rem;
}

.clsGridExportRow {
    display: flex;
    align-items: center;
    margin-bottom: 0.85rem;
}

/* Fixed, so the three controls line up with each other rather than with their
   own captions - flex-basis and not width, or a long caption would push it. */
.clsGridExportRow > label {
    flex: 0 0 8.5rem;
    margin: 0;
    padding-right: 0.75rem;
    font-weight: 600;
    color: #444;
}

.clsGridExportRow > label > i {
    width: 1.1rem;
    margin-right: 0.35rem;
    color: #886ab5;
}

.clsGridExportRow > .form-control {
    flex: 1 1 auto;
    width: auto;
}

/* Says which columns are about to be written out, so nothing about the file is
   a surprise once it is open. Wraps rather than scrolls: a wide grid names a
   lot of columns and a cut-off list would defeat the point of showing it. */
.clsGridExportNote {
    margin: 0.25rem 0 0 0;
    padding-top: 0.6rem;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    color: #868e96;
    font-size: 0.8rem;
    line-height: 1.5;
    word-break: break-word;
}

/* The way through to the page's own report popup, where it has one. Pushed to
   the far left because it is not a third answer to "export this grid" - it
   leaves this popup for a different one. */
.clsGridExportReport {
    margin-right: auto;
    color: #886ab5;
    font-size: 0.85rem;
}

.clsGridExportReport:hover {
    color: #6a4f96;
    text-decoration: underline;
}

/* --- The "how caching has been helping" strip at the top of the Cache popup ---
   Deliberately the friendliest thing in the popup: it is the part written for the
   user who opened the popup out of curiosity, not the one hunting a stale entry.
   Theme purple, because the numbers are the product's own good news. */
.clsCacheBenefits {
    margin: 0.75rem 0.25rem 0.25rem 0.25rem;
    padding: 0.75rem;
    border-radius: 6px;
    background: linear-gradient(135deg, #f6f2fd 0%, #efe9ff 100%);
    border: 1px solid #e2d8f7;
}

.clsCacheBenefitHead {
    font-weight: 600;
    color: #402080;
    margin-bottom: 0.6rem;
}

/* auto-fit, not four fixed columns: the popup is width-capped and the tiles carry
   sentences, so on a narrow window they wrap to 2x2 instead of crushing the text. */
.clsCacheBenefitTiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11.5rem, 1fr));
    gap: 0.6rem;
}

.clsCacheTile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    padding: 0.6rem 0.75rem;
    background: #fff;
    border-radius: 5px;
    border: 1px solid rgba(64, 32, 128, 0.08);
}

    .clsCacheTile > i {
        color: #886ab5;
        font-size: 1rem;
    }

    /* The number is the tile; the caption exists to make it honest. */
    .clsCacheTile > b {
        font-size: 1.25rem;
        line-height: 1.2;
        color: #2a2540;
        word-break: break-word;
    }

    .clsCacheTile > span {
        font-size: 0.76rem;
        line-height: 1.35;
        color: #6b6580;
    }

/* The one-line stand-in before anything has been counted. */
.clsCacheBenefitEmpty {
    margin: 0.75rem 0.25rem 0.25rem 0.25rem;
    padding: 0.55rem 0.75rem;
    border-radius: 5px;
    background: #f6f2fd;
    color: #5b4a86;
    font-size: 0.85rem;
}

/* --- Inline help behind an icon (.clsInfoTip) --------------------------------
   The house replacement for a sentence of grey explanation parked next to a
   control: the control keeps the row, the explanation is one press away.

       <span class="clsInfoTip" tabindex="0" role="button"
             aria-label="What this does" data-tip="the explanation">
           <i class="fal fa-info-circle"></i>
       </span>

   Pure CSS on purpose. Portal pages arrive through AJAX navigation, so a
   Bootstrap tooltip added after load is never initialised (the one init call
   runs at page load) - and this portal carries two jQuery builds, which makes
   the plugin route unreliable anyway. `:focus` on a tabbable span is what turns
   a CLICK into "open" - and a click anywhere else into "close" - so a press
   works on touch exactly as hover works with a mouse. */
.clsInfoTip {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.35rem;
    height: 1.35rem;
    margin-left: 0.35rem;
    border-radius: 50%;
    color: #886ab5;
    cursor: pointer;
    outline: none;
    vertical-align: middle;
}

    .clsInfoTip:hover,
    .clsInfoTip:focus {
        color: #6a4f96;
        background: #f1edf7;
    }

    .clsInfoTip > i {
        font-size: 0.95rem;
        line-height: 1;
    }

    /* The bubble. `width:max-content` + a max-width = it hugs a short sentence
       and wraps a long one, instead of stretching the row it sits in. The font
       properties are all restated because the icon is often dropped into a bold
       or coloured line and the bubble must not inherit that. */
    .clsInfoTip::after {
        content: attr(data-tip);
        position: absolute;
        top: calc(100% + 0.55rem);
        left: 50%;
        transform: translateX(-50%);
        z-index: 1080;
        width: max-content;
        max-width: 22rem;
        padding: 0.5rem 0.65rem;
        border-radius: 5px;
        background: #2a2540;
        color: #fff;
        font-size: 0.78rem;
        font-weight: 400;
        font-style: normal;
        line-height: 1.45;
        text-align: left;
        white-space: normal;
        box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transition: opacity 0.12s ease-in-out;
    }

    /* Arrow, pinned to the icon rather than to the bubble, so it still points at
       the icon when the bubble is right-aligned (.clsInfoTipLeft). */
    .clsInfoTip::before {
        content: '';
        position: absolute;
        top: calc(100% + 0.05rem);
        left: 50%;
        transform: translateX(-50%);
        z-index: 1081;
        border: 0.32rem solid transparent;
        border-bottom-color: #2a2540;
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transition: opacity 0.12s ease-in-out;
    }

    .clsInfoTip:hover::after,
    .clsInfoTip:hover::before,
    .clsInfoTip:focus::after,
    .clsInfoTip:focus::before {
        opacity: 1;
        visibility: visible;
    }

/* For an icon that sits near the right edge of a card, where a centred bubble
   would run off it: pin the bubble's right edge to the icon instead. */
.clsInfoTipLeft::after {
    left: auto;
    right: -0.5rem;
    transform: none;
}

/* --- Searchable picker (.clsSearchPick) --------------------------------------
   A type-to-filter box for a list of people, where a plain <select> stops being
   usable somewhere past a few dozen rows. Markup the JS expects:

       <div class="clsSearchPick" id="myPick">
           <i class="fal fa-search clsSearchPickIcon"></i>
           <input type="text" class="form-control clsSearchPickInput" ... />
           <button type="button" class="clsSearchPickClear"><i class="fal fa-times"></i></button>
           <ul class="clsSearchPickList"></ul>
       </div>

   Two state classes drive it: `clsOpen` on the box shows the list, `clsHasValue`
   shows the clear button. Both are set by the page's own script - this file only
   describes what the two states look like. */
.clsSearchPick {
    position: relative;
    display: inline-block;
    width: 22rem;
    max-width: 100%;
}

    .clsSearchPick > .clsSearchPickIcon {
        position: absolute;
        left: 0.65rem;
        top: 50%;
        transform: translateY(-50%);
        color: #886ab5;
        font-size: 0.85rem;
        pointer-events: none;
    }

    /* Room for the two things sitting on top of the field. */
    .clsSearchPick > .clsSearchPickInput {
        padding-left: 2rem;
        padding-right: 2rem;
    }

    .clsSearchPick > .clsSearchPickClear {
        position: absolute;
        right: 0.4rem;
        top: 50%;
        transform: translateY(-50%);
        display: none;
        width: 1.4rem;
        height: 1.4rem;
        padding: 0;
        border: 0;
        border-radius: 50%;
        background: transparent;
        color: #9a94ad;
        font-size: 0.8rem;
        line-height: 1;
        cursor: pointer;
    }

        .clsSearchPick > .clsSearchPickClear:hover {
            background: #f1edf7;
            color: #6a4f96;
        }

    .clsSearchPick.clsHasValue > .clsSearchPickClear {
        display: block;
    }

.clsSearchPickList {
    position: absolute;
    z-index: 1070;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    display: none;
    max-height: 16rem;
    overflow-y: auto;
    margin: 0;
    padding: 0.25rem 0;
    list-style: none;
    background: #fff;
    border: 1px solid #d8d2e6;
    border-radius: 5px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
}

.clsSearchPick.clsOpen > .clsSearchPickList {
    display: block;
}

.clsSearchPickList > li {
    padding: 0.35rem 0.7rem;
    line-height: 1.3;
    cursor: pointer;
}

    /* One highlight class for both the mouse and the arrow keys, so a list being
       arrowed through never shows two "current" rows. */
    .clsSearchPickList > li.clsActive {
        background: #f1edf7;
    }

    .clsSearchPickList > li > b {
        display: block;
        font-weight: 600;
        font-size: 0.85rem;
        color: #2a2540;
    }

    .clsSearchPickList > li > span {
        display: block;
        font-size: 0.75rem;
        color: #7b7590;
    }

    /* "Loading", "nothing matched", "N more" - text, not a choice. */
    .clsSearchPickList > li.clsSearchPickNote {
        cursor: default;
        font-size: 0.78rem;
        color: #8a849c;
    }

    .clsSearchPickList mark {
        padding: 0;
        background: #efe4ff;
        color: inherit;
    }

/* ------------------------------------------------------------------------------------------------
   Left menu: an entry this portal cannot open.

   Two things land here, and both used to render as an ordinary blue link that answered 404 on click:
   a feature the operator's permission switched off (IsVisible == "No"), and a feature whose catalogue
   Href names a route no controller serves (see NavigationActive.IsReachable). The anchor is written
   WITHOUT an href in the second case, so it is already inert - this is what makes it LOOK inert.

   Kept deliberately quiet rather than struck through or badged: the entry is still the name of a real
   feature and the operator may be waiting on it, so it reads as "not now", not as "broken".

   DIM WITH COLOUR, NEVER WITH opacity, AND NEVER WITH `color: inherit`.

   The theme already dims this row. app.bundle.css carries a rule commented "mostly used for nav items":

       .disabled:not(.btn),
       .disabled:not(.btn) > *   { filter: grayscale(80%); opacity: 0.80; cursor: not-allowed; }

   which matches BOTH the <li class="disabled"> and, through `> *`, the <a> inside it. Opacity is not
   a colour - it composites the element against whatever is behind it and MULTIPLIES down the tree, so
   the 0.45 that used to sit here landed on top of the theme's 0.80 and rendered the label at 0.36 of
   full strength, with grayscale applied twice on the way. `color: inherit` finished the job: it threw
   away the theme's own label colour (.nav-menu li > ul li a is #af9fc7) and took the colour of the
   <ul> instead, which on the dark sidebar is close to the background. The entry still occupied its
   row, still had its title attribute and still held its text in the DOM - it was simply not visible,
   which reads to an operator as a menu item with NO NAME rather than one they cannot open yet.

   So: state the colour outright, as a dimmed version of the two the theme uses for a live sub-item
   (#af9fc7 label, #876fab icon), and cancel the theme's filter/opacity on this row so nothing
   compounds. Both selectors below outrank the theme's - `.nav-menu li.disabled` is (0,0,2,1) against
   `.disabled:not(.btn)` at (0,0,2,0), and `.nav-menu li.disabled > a` is (0,0,2,2) against
   `.disabled:not(.btn) > *` at (0,0,2,1).

   The theme's `.disabled:not(.btn) ul, .disabled:not(.btn) .collapse-sign { display: none }` is left
   alone on purpose: a disabled parent should still not offer a chevron to expand.
   ------------------------------------------------------------------------------------------------ */
/* Short wording is supplied by SidebarText; never clip menu names. */
.nav-menu li a > .nav-link-text {
    display: block;
    min-width: 0;
    white-space: normal;
    overflow: visible;
}
.nav-menu li a > i,
.nav-menu li a > .collapse-sign {
    flex-shrink: 0;
}

.nav-menu li.disabled,
.nav-menu li.disabled > a {
    -webkit-filter: none;
    filter: none;
    opacity: 1;
}

.nav-menu li.disabled > a {
    cursor: not-allowed;
    pointer-events: none;
}

    /* The icon and label are separate elements inside the anchor and inherit none of the above. */
    .nav-menu li.disabled > a,
    .nav-menu li.disabled > a .nav-link-text {
        color: #8a7aa6;
    }

    .nav-menu li.disabled > a > i,
    .nav-menu li.disabled > a > [class*='fa-'],
    .nav-menu li.disabled > a .nav-link-text > [class*='fa-'] {
        color: #6e5c8c;
    }

/* Hover/focus must not light up a row that cannot be opened - the theme styles those on the <li>. */
.nav-menu li.disabled:hover > a,
.nav-menu li.disabled:focus-within > a {
    background: none;
}

/* ------------------------------------------------------------------------------------------------
   Button/tab strip: a button or tab whose page this portal cannot open.

   Same rule and same reasoning as the disabled left-menu entry above, applied to the other surface
   that draws catalogue routes (_001_ButtonAndTab.cshtml). Those anchors are rendered with NO href,
   so they are already inert; this is what stops them reading as live links.

   Not hidden: the button belongs to the feature and its absence would be the more confusing of the
   two, so it stays legible and simply does not invite a click.
   ------------------------------------------------------------------------------------------------ */
a.zs-dead-link {
    cursor: not-allowed;
    opacity: 0.45;
    pointer-events: none;
    text-decoration: none;
    color: inherit;
}

/* Shared record lists: summary, keyboard-accessible paging and data freshness. */
.dt-toolbar-footer.baf-grid-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    margin-left: 0;
    padding: 0.75rem;
}

.baf-grid-summary,
.baf-grid-meta {
    flex: 1 1 12rem;
    min-width: 0;
    font-size: 0.875rem;
}

.baf-grid-meta {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 0.5rem;
}

.baf-grid-meta #spDataFreshness {
    white-space: nowrap;
    font-weight: 600;
}

.baf-grid-pagination {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.baf-grid-pagination .btn,
.baf-grid-pagination #txtPageNumber {
    height: 2.75rem;
}

.baf-grid-pagination .btn {
    width: 2.75rem;
}

.baf-grid-pagination #txtPageNumber {
    width: 5rem;
    text-align: center;
    font-weight: 600;
}

@media (max-width: 767px) {
    .dt-toolbar-footer.baf-grid-footer {
        justify-content: center;
    }
    .baf-grid-summary,
    .baf-grid-meta {
        flex-basis: 100%;
        justify-content: center;
        text-align: center;
    }
}

/* The head fixes the sidebar at 18rem; the theme's mobile offset is only 16.875rem. */
@media (max-width: 991.98px) {
    /* Balance Bootstrap's negative row margin after the layout resets page padding. */
    #js-page-content { padding-right: .75rem !important; }
    body:not(.mobile-nav-on):not(.nav-mobile-slide-out) .page-wrapper .page-sidebar {
        transform: translate3d(-100%, 0, 0);
    }
    .mobile-nav-on .page-header {
        z-index: 2001;
    }
}

/* Attendance summaries reuse the portal table and progress components. */
.baf-attendance-summary .clsDControl {
    width: 20rem;
    min-width: 0;
    max-width: 100%;
}

.attendance-table .highlight {
    background-color: #ffcccc;
}

.attendance-table .sunday-row td {
    background-color: #fff4e6;
}

/* Shared form control widths (the former per-page defaults). */
@media only screen and (max-width: 600px) { .clsDControl { width: 5rem !important; } }
@media only screen and (min-width: 600px) { .clsDControl { width: 13rem !important; } }
@media only screen and (min-width: 768px) { .clsDControl { min-width: 10rem !important; } }
@media only screen and (min-width: 992px) { .clsDControl { width: 10rem !important; } }
@media only screen and (min-width: 1200px) { .clsDControl { min-width: 20rem !important; max-width: 20rem !important; } }

/* Shared field action toolbars: the same neutral native buttons beside any field. */
.baf-workspace-tabs { width: 100%; }
.baf-workspace-button { display: flex; justify-content: flex-end; border-bottom: 4px solid #494571; }
.baf-workspace-button span { padding: 8px 40px; background: #494571; color: white; font-weight: bold; }
.baf-workspace-tab-list { display: flex; gap: 8px; border-bottom: 4px solid #494571; padding: 6px 12px 0; }
.baf-workspace-tab-list button { min-width: 150px; padding: 7px; border: 1px solid #494571; border-radius: 4px 4px 0 0; background: #a58bbe; color: white; font-weight: bold; }
.baf-workspace-tab-list button[aria-selected=true] { background: #494571; }
@media (max-width: 575px) {
    .baf-workspace-tab-list { flex-wrap: wrap; }
    .baf-workspace-tab-list button { flex: 1 1 150px; min-width: 0; }
}
 .baf-workspace-grid-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:12px; margin:12px 0; }
.baf-workspace-grid-toolbar > form { flex:1 1 32rem; margin:0 !important; display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.baf-workspace-grid-toolbar > form > input, .baf-workspace-grid-toolbar > form > select { height:40px; }
.baf-workspace-grid-toolbar > form > input[type=search], .baf-workspace-grid-toolbar > form > input:not([type]) { flex:1 1 16rem; min-width:0; }
.baf-workspace-toolbar { display:flex; justify-content:flex-end; align-items:center; flex-wrap:wrap; gap:6px; margin:0; margin-left:auto; }
.baf-workspace-toolbar .btn:disabled { background:#bcbcbc; border-color:#bcbcbc; color:white; opacity:1; }
.baf-workspace-toolbar [data-workspace-status]:empty { display:none; }
.baf-workspace-toolbar .btn { width:43px; height:42px; padding:0; line-height:40px; border-radius:4px; }
.baf-workspace-tabs [disabled] { opacity: .55; }
.baf-workspace-toolbar[hidden], [data-workspace-list][hidden], [data-workspace-editor][hidden] { display: none !important; }

.clsQuickCRUD,
.clsQuickTextOrNumber {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: 0.5rem;
    white-space: nowrap;
}

/* Ordinary record fields share one edge; reports and line-item tables keep their own layout. */
.DataItems .frame-wrap .input-group > .clsDControl {
    flex: 0 1 20rem;
    width: 20rem !important;
    min-width: 0 !important;
    max-width: 20rem !important;
    margin-right: 0 !important;
}
.DataItems .frame-wrap .input-group > .input-group-prepend > .input-group-text:last-child:not(:first-child) {
    width: 12rem;
    min-width: 12rem !important;
    white-space: normal;
}
.baf-form-section { margin-bottom: 1rem; }
.baf-form-section > summary { cursor: pointer; padding: .5rem; color: #494571; font-weight: bold; }
.baf-form-section .frame-wrap { margin-top: 0 !important; margin-bottom: .75rem; }
@media (max-width: 575px) {
    .DataItems .frame-wrap .input-group > .input-group-prepend { flex-basis: 100%; }
    .DataItems .frame-wrap .input-group > .clsDControl { flex: 1 1 20rem; }
}
.clsQuickCRUD .clsActionButton,
.clsQuickTextOrNumber .clsActionButton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2rem;
    padding: 0;
    margin: 0 !important;
}
.input-group:has(> .clsQuickCRUD, > .clsQuickTextOrNumber) > .clsDControl {
    margin-right: 0 !important;
}
@media (min-width: 992px) {
    .input-group:has(> .clsQuickCRUD),
    .input-group:has(> .clsQuickTextOrNumber) {
        flex-wrap: nowrap;
    }
}

/* Shared phone/tablet controls must fit their container without clipping content. */
@media (max-width: 991.98px) {
    #js-page-content > .breadcrumb {
        position: static !important;
        width: 100% !important;
        height: auto !important;
        overflow-wrap: anywhere;
    }
    #js-page-content #myButton,
    #js-page-content #myTab {
        float: none !important;
        height: auto;
        min-height: 40px;
    }
    #js-page-content #myButton ul,
    #js-page-content #myTab ul {
        display: flex;
        flex-wrap: wrap;
    }
    #js-page-content #myButton ul { justify-content: flex-end; }
    #js-page-content #myButton li,
    #js-page-content #myTab li { float: none; max-width: 100%; }
    #js-page-content #myButton li a,
    #js-page-content #myTab li a {
        float: none;
        display: block;
        height: auto;
        min-height: 2.75rem;
        max-width: 100%;
        overflow-wrap: anywhere;
    }
    .modal .modal-dialog { max-width: calc(100vw - 1rem) !important; }
    .modal .input-group { flex-wrap: wrap; min-width: 0; max-width: 100%; }
    .modal .form-control,
    .modal .select2-container { min-width: 0 !important; max-width: 100%; }
    body .notification-dropdown .dropdown-notification-menu {
        min-width: 0 !important;
        max-width: calc(100vw - 1rem) !important;
    }
    .dataTables_wrapper [id^="dvSerachBox"],
    .dataTables_wrapper [id^="dvSearchBox"] {
        width: auto !important;
        min-width: 0;
        max-width: 100%;
        margin: 0 !important;
        flex-wrap: wrap;
    }
    .page-header .search { min-width: 0; }
    .page-header #js_nested_list_filter { min-width: 0 !important; }
}
@media (max-width: 575.98px) {
    /* col-6 and negative form margins were sized for single-line desktop rows. */
    .DataItems .frame-wrap { margin-top: 0 !important; margin-left: 0 !important; }
    .DataItems .frame-wrap > .input-group {
        flex-basis: 100%;
        width: 100%;
        max-width: 100%;
        min-width: 0;
    }
    .baf-grid-pagination { flex-wrap: wrap; justify-content: center; }
    .page-header > .ml-auto { min-width: 0; align-items: center; }
    .page-header .baf-country-links { flex-shrink: 1; flex-wrap: wrap; min-width: 0; }
    .page-header .baf-country-links a { width: 26px; height: 28px; }
}

/* Fixed-width subscription content must size its dialog, not escape a narrower dialog. */
.modal-dialog:has(> .modal-content[style*="width: 1000px"]) {
    width: 1000px;
    max-width: calc(100vw - 1rem) !important;
}
.modal-dialog > .modal-content[style*="width: 1000px"] {
    width: 100% !important;
    margin-left: 0 !important;
}

/* Match the existing left inset: Bootstrap rows need a right gutter too. */
#js-page-content { padding-right: 19px !important; }

/* Tabs can wrap within a desktop content pane as well as a mobile viewport. */
#js-page-content #myButton, #js-page-content #myTab {
    float: none !important;
    height: auto;
    min-height: 40px;
}
#js-page-content #myButton ul, #js-page-content #myTab ul { display: flex; flex-wrap: wrap; }
#js-page-content #myButton ul { justify-content: flex-end; }
#js-page-content #myButton li, #js-page-content #myTab li { float: none; height: auto; max-width: 100%; }
#js-page-content #myButton li a, #js-page-content #myTab li a { display: block; margin-top: 0; max-width: 100%; }

@media (max-width: 991.98px) {
    .DataItems .input-group { min-width: 0; max-width: 100%; }
    .DataItems .form-control { min-width: 0; max-width: 100%; }
    .DataItems .row:has(input[id^="pattern-hours-"]) {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
        margin: 0;
    }
    .DataItems .row:has(input[id^="pattern-hours-"]) > .col { min-width: 0; }
}

/* The breadcrumb precedes wrapped tab rows at every content width. */
#js-page-content > .breadcrumb {
    position: static !important;
    width: 100% !important;
    height: auto !important;
    overflow-wrap: anywhere;
}

/* Native list tables scroll within their grid; wide columns must not widen the page. */
#datatable_tabletools_wrapper { min-width: 0; max-width: 100%; overflow-x: auto; }

/* Older native filter dialogs use the same fixed content pattern as subscription dialogs. */
.modal-dialog:has(> .modal-content[style*="900px"]) {
    width: 900px;
    max-width: calc(100vw - 1rem) !important;
}
.modal-dialog > .modal-content[style*="900px"] {
    width: 100% !important;
    margin-left: 0 !important;
}

/* Match native row-action spacing for every shared workspace, including later custom buttons. */
.baf-workspace-row-actions > .btn { margin: .125rem; }

/* Keep native title/action tabs against their lower bar, including wrapped rows. */
#js-page-content #myButton,
#js-page-content #myTab {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding-bottom: 0;
}
#js-page-content #myButton ul,
#js-page-content #myTab ul {
    align-items: flex-end;
    margin-top: 0;
}
/* A wrapping breadcrumb needs a small gutter, not the legacy empty header band. */
#js-page-content > .breadcrumb {
    padding-top: .25rem;
    padding-bottom: .25rem;
    margin-bottom: .25rem;
}
