﻿

/* ############### */
/* ### SwfForm ### */
.h70 {
    height: 70%;
}

.modal-content {
    width: max-content;
}

.control-sidebar {
    width: 50% !important;
}

h3 {
    font-family: 'Verdana', 'Geneva', 'Tahoma', 'sans-serif';
    font-size: 1.2em;
}

.estilo {
    width: 100%;
    border-width: 2px;
    border-style: solid;
    border-top: none;
    border-color: dodgerblue;
}

.fadeBorder {
    border-left: solid 2px dodgerblue;
    border-right: solid 2px dodgerblue;
    border-bottom: solid 2px dodgerblue;
    border-top: none;
}

.fadein {
    height: 0px;
    transition: height;
    transition-delay: 100ms;
    transition-duration: 800ms;
    border-bottom: none;
}

.fadeout {
    height: 68px;
    transition: height;
    transition-delay: 100ms;
    transition-duration: 1000ms;
    border-bottom: solid 2px dodgerblue;
}

.round-border {
    border: solid 1px dodgerblue;
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
}

.StartRow {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: start;
}

.CenterCol {
    display: flex;
    flex-direction: column;
    /* "safe" matters once a sidebar narrows the content area. Plain centring
       overflows an oversized child EQUALLY both ways, so a 1100px form in a
       917px column loses ~90px off its left edge - under the sidebar, where no
       scrollbar can reach it. "safe" falls back to start-alignment when the
       child overflows, keeping the overflow on the scrollable side.
       The unprefixed value first, as a fallback for engines without it. */
    align-items: center;
    align-items: safe center;
    justify-content: center;
}

.CenterRow {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-around;
}

table {
    margin-bottom: 0px !important;
}

.InvBanner {
    height: 30px;
    color: black;
    margin: 3px;
    margin-left: 10px;
}


/* ################# */
/* ### SwfReport ### */
.SwfFrmBtn {
    text-shadow: -2px 2px 4px black;
    line-height: 18px !important;
    cursor: pointer !important;
    padding: 4px !important;
    margin: 2px;
    border: solid !important;
    border-color: var(--swf-form-page-btn-color, white) !important;
    border-width: 2px !important;
    border-radius: 25px !important;
}

    .SwfFrmBtn:focus-visible {
        border-color: #fff !important;
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) rgba(255, 255, 255, 0.55) !important;
        outline: 0 !important;
    }

    .btn:focus-visible {
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) var(--swf-input-focus-ring, rgba(47,111,181,.30)) !important;
        outline: 0 !important;
    }


/* ############### */
/* ### SwfCombo / Grid Combo ### */
.allSelectCmb {
    border-bottom-style: solid;
    border-bottom-color: lightgray;
    border-block-width: 1px;
    place-items: start;
}

/* Inactive items carry cmbDiv too but can't be picked, so they don't light up. */
.cmbDiv:not([disabled]):hover {
    background-color: #cce0ff;
}

.dropdownModel {
    position: absolute;
    background-color: white;
    z-index: 999;
}

.dropbtn {
    position: relative;
    text-align: left;
    text-wrap-mode: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background-position: right .35rem center !important;
}

.dropdown-content {
    display: none;
    overflow: auto;
    border: 1px solid lightgray;
    z-index: 1;
}


/* Scoped to the dropdown it was written for. As a bare `.show` it also matched
   Bootstrap's own `.show` - used by .offcanvas, .modal and .dropdown - and
   because control.css loads after bootstrap.css it beat `.offcanvas{display:flex}`
   and forced the panel to display:block. The panel then stopped being a flex
   container, so .offcanvas-body was not a flex item, flex-grow/shrink did
   nothing, and a search panel taller than its panel grew to full content
   height and spilled over the page instead of scrolling. */
.dropdown-content.show {
    display: block;
}
/* ##################################### */
/* ### Swift Controls ###
   Several rules below write a class twice (".swf-lable.swf-lable"). control.css is loaded before
   bootstrap.css, and where a rule weighs the same as Bootstrap's (here .input-group-text) Bootstrap
   wins - the captions, grid headers and report buttons all lost the app's colours that way.
   Doubling the class makes the app's rule one step stronger without !important. */
.swf-lable.swf-lable {
    padding: .350rem !important;
    font-weight: 600 !important;
    background-color: var(--swf-label-bg, #e6ecf3);
    border: 1px solid var(--swf-input-border-color, #c3cbd6);
    color: var(--swf-label-fg, #3c4858);
}

/* The class is written twice on purpose: control.css now loads before bootstrap.css, and at equal
   weight Bootstrap's .form-control / .form-control:disabled won, turning the app's input border and
   read-only colours into Bootstrap's. Doubling the class makes these one step stronger. */
.swf-control.swf-control {
    flex: none !important;
    font-weight: var(--swf-input-font-weight, 600) !important;
    background-color: var(--swf-input-bg-color, #fff);
    border: 1px solid var(--swf-input-border-color, #c3cbd6);
    transition: border-color .12s ease-in-out, box-shadow .12s ease-in-out;
}

    .swf-control:not([type="checkbox"]):hover:not(:disabled) {
        border-color: var(--swf-input-focus-border-color, #2f6fb5);
    }

    /* The focus ring is a translucent halo, not a second solid border, so the
       focused field is obvious at a glance without shouting. */
    .swf-control:not([type="checkbox"]):focus {
        border-color: var(--swf-input-focus-border-color) !important;
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) var(--swf-input-focus-ring, rgba(47,111,181,.30)) !important;
        background-color: var(--swf-input-focus-bg) !important;
        outline: 0 !important;
    }

    .swf-control[type="checkbox"]:focus-visible {
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) var(--swf-input-focus-ring, rgba(47,111,181,.30)) !important;
        outline: 0 !important;
    }

    /* Text fields and lists only. On a checkbox this grey hid a ticked box's white tick, so a
       read-only "Active" looked unticked; checkboxes keep Bootstrap's ticked look. */
    .swf-control.swf-control:disabled:not([type="checkbox"]):not([type="radio"]) {
        background-color: #f1f3f5;
        color: #6c757d;
    }

.grid_ctrl {
    padding-left: 4px !important;
    padding-right: 4px !important;
    padding-top: 1px !important;
    padding-bottom: 1px !important;
    font-weight: 600 !important;
}

    .grid_ctrl:not([type="checkbox"]) {
        min-height: 34px;
    }

        .grid_ctrl:not([type="checkbox"]):focus {
            border-color: var(--swf-input-focus-border-color) !important;
            box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) var(--swf-input-focus-ring, rgba(47,111,181,.30)) !important;
            background-color: var(--swf-input-focus-bg) !important;
            outline: 0 !important;
        }
/* ##################################### */
/* ### DataGridComponent/SwfDataGrid ### */
.fix-table { /* Used in Form Design */
    table-layout: fixed;
    width: 100%;
    white-space: nowrap;
}

.table {
    table-layout: fixed;
}

.grid-table {
    --bs-table-bg: transparent !important;
    display: flex;
    flex-direction: column;
    height: 100%;
    width: 100%;
    overflow-x: auto; /* horizontal scroll */
    overflow-y: hidden;
    text-wrap: wrap;
}

    .grid-table tbody {
        flex: 1 1 auto;
        overflow-y: auto;
        overflow-x: hidden;
        scrollbar-gutter: stable; /* reserves scrollbar space */
        height: 100%;
    }

        .grid-table tbody.autoSize tr {
            display: table;
            width: 100%;
            table-layout: fixed;
        }

        .grid-table tbody.fixSize tr {
            width: max-content;
        }

        .grid-table tbody tr {
            width: 100%;
            table-layout: fixed;
        }

        .grid-table tbody td {
            word-break: break-word;
            font-weight: var(--swf-input-font-weight);
            /*align-content: center;*/
        }

/* ColumnAutoSize grids: lay out as one real table.
   The default grid makes the <table> a flex column and turns every <tr> into
   its own table, so the header computed one set of column widths and each body
   row computed another - which is why the two never lined up. Here the browser
   does a single width calculation for the whole table, so the header cannot
   drift from the body, and table-layout:auto lets columns size to content
   instead of being pinned to the declared widths.
   The wrapper div scrolls, and the header stays put with position:sticky. */
.grid-table.swf-grid-auto {
    display: table;
    table-layout: auto;
    /* Not width:100%. That pins the table to the container, and auto layout
       then squeezes columns to fit, wrapping the text and growing row heights.
       width:auto sizes each column to its content; min-width still fills the
       container when the content is narrower, and the wrapper scrolls when it
       is wider. */
    width: auto;
    min-width: 100%;
    overflow: visible;
    /* The wrapper is a grid container with a fixed height, so by default the
       table stretches to fill it and the rows share that height between them -
       three rows became 100px each. Starting at the top keeps rows at their
       natural height and leaves the spare space empty. */
    align-self: start;
    height: auto;
}

    /* Rows keep their normal single-line height. Without this the inherited
       word-break:break-word wraps long values and rows grow unevenly. */
    .grid-table.swf-grid-auto thead th,
    .grid-table.swf-grid-auto thead td,
    .grid-table.swf-grid-auto tbody td,
    .grid-table.swf-grid-auto tfoot td,
    .grid-table.swf-grid-auto tfoot th {
        white-space: nowrap;
        word-break: normal;
    }

    .grid-table.swf-grid-auto thead {
        display: table-header-group;
    }

    .grid-table.swf-grid-auto tfoot {
        display: table-footer-group;
    }

    .grid-table.swf-grid-auto tbody {
        display: table-row-group;
        overflow: visible;
        height: auto;
        flex: none;
        scrollbar-gutter: auto;
    }

        /* Beats .grid-table tbody.autoSize tr, which forced display:table. */
        .grid-table.swf-grid-auto tbody tr,
        .grid-table.swf-grid-auto tbody.autoSize tr,
        .grid-table.swf-grid-auto thead tr,
        .grid-table.swf-grid-auto tfoot tr {
            display: table-row;
            width: auto;
            table-layout: auto;
        }

    /* Paint the whole sticky band opaque.
       background-clip:padding-box was keeping the fill out of the border area;
       with border-collapse the cells share a border box, so that left a strip
       the width of the border on every edge where the scrolling rows below
       showed straight through the header. Default border-box clipping covers
       it. The row and the group are painted too, so the gaps between cells
       (and the cell borders themselves) never expose the body. */
    .grid-table.swf-grid-auto thead,
    .grid-table.swf-grid-auto tfoot,
    .grid-table.swf-grid-auto thead tr,
    .grid-table.swf-grid-auto tfoot tr {
        background-color: var(--swf-grid-head-bg, #eef2f7);
    }

    .grid-table.swf-grid-auto thead th,
    .grid-table.swf-grid-auto thead td {
        position: sticky;
        top: 0;
        z-index: 3;
        background-color: var(--swf-grid-head-bg, #eef2f7);
        background-clip: border-box;
    }

    .grid-table.swf-grid-auto tfoot th,
    .grid-table.swf-grid-auto tfoot td {
        position: sticky;
        bottom: 0;
        z-index: 3;
        background-color: var(--swf-grid-head-bg, #eef2f7);
        background-clip: border-box;
    }

    /* CSS backstop for the same thing, in case a caller supplies its own
       padded wrapper: sticky top:0 pins the header to the scrollport's PADDING
       edge, so any padding-top on the scroll container is a window the rows
       scroll through, above the header. Four pixels of it was enough to show a
       sliver of the row text over the header labels. */
    :is(div):has(> .grid-table.swf-grid-auto) {
        padding-top: 0 !important;
    }

/* A control with no caption still sits inside an input-group, behind an empty
   spacer div, so Bootstrap squares off its left corners as a "middle" child.
   With nothing to its left there is no seam to square against, so round it to
   match the right-hand side. */
.input-group > .swf-control.swf-rounded-start {
    border-top-left-radius: var(--bs-border-radius, 0.375rem) !important;
    border-bottom-left-radius: var(--bs-border-radius, 0.375rem) !important;
}

/* ### First-letter filter chips (SwfCharFilter) ### */
.swf-char-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 4px 2px 6px 2px;
}

.swf-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 6px 2px 9px;
    border: 1px solid #b9c6d4;
    border-radius: 999px;
    background-color: #fff;
    color: #2e4a66;
    font-size: 0.82rem;
    line-height: 1.5;
    cursor: pointer;
    transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}

    .swf-chip:hover {
        background-color: #eaf2fb;
        border-color: #7aa7d2;
    }

    .swf-chip:focus-visible {
        outline: 2px solid #2e2eb8;
        outline-offset: 1px;
    }

    .swf-chip.active {
        background-color: #2e6fbd;
        border-color: #2e6fbd;
        color: #fff;
    }

.swf-chip-key {
    font-weight: 600;
}

.swf-chip-count {
    min-width: 1.35rem;
    padding: 0 5px;
    border-radius: 999px;
    background-color: #e6edf5;
    color: #44617e;
    font-size: 0.72rem;
    font-weight: 600;
    text-align: center;
}

.swf-chip.active .swf-chip-count {
    background-color: rgba(255, 255, 255, 0.25);
    color: #fff;
}

/* Active row styles */
tr.active-row {
    /* background-color: rgba(220, 220, 255, 0.2); */
    background-color: var(--swf-grid-focus-row-bg, #e7eeff) !important;
    box-shadow: inset -1px 3px 6px 0 rgba(0, 0, 0, 0.2);
    outline: 2px solid var(--swf-grid-focus-row-border, skyblue) !important;
    outline-offset: -1px; /* Keeps outline inside the cell */
    border-radius: 5px; /* Optional: adds rounded corners to the outline */
}
    /* Hover effect for active row */
    /*tr.active-row:hover {
        outline: 3px solid var(--swf-grid-focus-row-border, skyblue) !important;
    }*/

td.active-cell {
    outline: solid 2px blue;
    border-radius: 4px;
    background-color: white !important;
    outline-offset: -2px;
}

/*
table {
    page-break-after: auto;
}

tr {
    page-break-inside: avoid;
    page-break-before: auto
}

td {
    page-break-inside: avoid;
    page-break-before: auto
}
*/
.tableHead {
    background: var(--swf-grid-head-bg, #eef2f7);
    color: var(--swf-grid-head-fg, #2b3a4a);
    font-weight: 600;
}

    /* Doubled: Bootstrap's ".table > :not(caption) > * > *" weighs the same and set the header
       cells transparent. */
    .tableHead.tableHead th,
    .tableHead.tableHead td {
        border: solid 1px var(--swf-grid-border, #cdd7e3) !important;
        /* Must be on the CELL. position:sticky is applied to th/td, so the
           thead's own background scrolls away with the table and the pinned
           cells paint transparent - body rows then show through the header. */
        background: var(--swf-grid-head-bg, #eef2f7);
    }

    .tableHead tfoot {
        min-height: 35px;
    }
/* ##################################### */
/* ### Drop Down List ### */

.dropdownTable {
    border-collapse: collapse !important;
    table-layout: fixed;
    width: 100%;
    text-wrap: wrap;
    text-align: left;
}

    .dropdownTable tbody {
        flex: 1 1 auto;
        overflow: auto;
        display: block;
    }

        .dropdownTable tbody tr {
            /*display: block;*/
            table-layout: fixed;
            cursor: pointer;
        }

    .dropdownTable td,
    .dropdownTable th {
        border: none !important;
        border-bottom: 1px solid #e9ecef !important;
        padding: 3px 5px;
    }

/* A grid combo's list (DataGridListCombo) is rendered INSIDE the grid's own
   <td>, so every ".grid-table tbody ..." descendant rule reached it as well:
   bold cell text, break-word/nowrap, the tbody turned into a flex item or a
   table-row-group (which kills its scrolling), and - once swfGridFreeze has
   run - the rows became fixed tables with the grid's column widths pinned onto
   the list's cells. Undo those so it looks exactly like SwfCombo's list, which
   lives outside any grid. The !important is needed to beat the generated
   #<grid-id>.swf-grid-frozen rules; cell widths come from inline styles. */
.grid-table .swf-grid-cmb .dropdownTable tbody {
    display: block !important;
    flex: none !important;
    min-height: 0;
    scrollbar-gutter: auto !important;
}

    .grid-table .swf-grid-cmb .dropdownTable tbody tr,
    .grid-table .swf-grid-cmb .allSelectCmb tr {
        display: table-row !important;
        width: auto !important;
        table-layout: auto !important;
    }

    .grid-table .swf-grid-cmb .dropdownTable td,
    .grid-table .swf-grid-cmb .allSelectCmb td {
        font-weight: var(--bs-body-font-weight, normal);
        white-space: normal !important;
        word-break: normal !important;
        position: static !important;
        background-color: transparent;
    }


/* Hide input number arrow */
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/* Firefox */
input[type=number] {
    -moz-appearance: textfield;
}

.number[data-value^="-"]:after {
    color: red;
}

.clickable {
    cursor: pointer;
}

.sort-th {
    cursor: pointer;
    position: relative; /* important */
    padding-right: 16px; /* room for icon */
    overflow: visible !important;
}

.sort {
    position: absolute;
    right: -1px; /* horizontal offset */
    bottom: -4px; /* vertical offset */
    font-size: 18px; /* small icon */
    font-weight: bolder;
    pointer-events: none; /* click still hits header */
    z-index: 10; /* icon always visible */
}

.sort-th:hover .sort {
    font-size: 20px; /* small icon */
}

.no-sort {
    display: none;
}
/* ##################################### */
/* --------- TAB UI ------------ */
/* Colours are the --swf-tab-* tokens in swiftTheme1.css. */
/* Container */
.swf-tab-container {
    border-top: 0;
}
/* Tab list */
.swf-tabs {
    border-bottom: 1px solid var(--swf-tab-border, #cdd7e3);
    margin-bottom: 0;
    gap: .25rem;
}
/* Tab button */
.swf-tab-link {
    color: var(--swf-tab-fg, #3c4858);
    background-color: var(--swf-tab-bg, #e6ecf3);
    font-weight: 500;
    border: 1px solid var(--swf-tab-border, #cdd7e3);
    border-bottom: none;
    border-radius: .5rem .5rem 0 0;
    padding: 5px 10px;
}
    /* Hover highlight */
    .swf-tab-link:not(.active):not(.disabled):hover {
        background-color: var(--swf-tab-hover-bg, #d9e4f0);
        color: var(--swf-tab-active-fg, #245a8d);
    }
    /* ACTIVE - panel colour with an accent bar on top; the -1px covers the
       strip's bottom line so the tab joins the panel below it. */
    .swf-tab-link.active {
        color: var(--swf-tab-active-fg, #245a8d);
        background-color: var(--swf-form-body-bg, #fff);
        font-weight: 600;
        margin-bottom: -1px;
        box-shadow: inset 0 2px 0 var(--swf-tab-accent, #2f6fb5);
    }
    /* Keyboard focus - same ring as the input fields */
    .swf-tab-link:focus-visible {
        outline: none;
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, .2rem) var(--swf-input-focus-ring, rgba(47, 111, 181, .30));
    }
    .swf-tab-link.active:focus-visible {
        box-shadow: inset 0 2px 0 var(--swf-tab-accent, #2f6fb5),
                    0 0 0 var(--swf-input-focus-ring-width, .2rem) var(--swf-input-focus-ring, rgba(47, 111, 181, .30));
    }
    /* Disabled tab */
    .swf-tab-link.disabled {
        opacity: .5;
        cursor: not-allowed;
    }
/* Tab content area */
.tab-pane-wrapper {
    border: 1px solid var(--swf-tab-border, #cdd7e3);
    border-top: none;
    border-radius: 0 0 .5rem .5rem;
    background-color: var(--swf-form-body-bg, #fff);
    padding: 0.30rem;
}
/* ##################################### */
/* --------- SwfTreeView ------------
   Scoped to .swf-tree. The old per-node <style> made every label on the page
   blue and bold on hover, and was emitted once per node. */
/* The box: optional toolbar on top, the scrolling tree below it. */
.swf-tree-box {
    display: flex;
    flex-direction: column;
    margin: .25rem;
    overflow: hidden;
    border: 1px solid var(--swf-grid-border, #cdd7e3);
    border-radius: 6px;
    background-color: var(--swf-input-bg-color, #fff);
}

.swf-tree-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
    padding: 3px 6px;
    border-bottom: 1px solid var(--swf-grid-border, #cdd7e3);
    background-color: var(--swf-grid-head-bg, #eef2f7);
}

/* Search box: takes the toolbar's free width, result count and clear inside it. */
.swf-tree-search {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    height: 28px;
    padding: 0 4px 0 8px;
    border: 1px solid var(--swf-input-border-color, #c3cbd6);
    border-radius: 5px;
    background-color: var(--swf-input-bg-color, #fff);
}

    .swf-tree-search:focus-within {
        border-color: var(--swf-input-focus-border-color, #2f6fb5);
        box-shadow: 0 0 0 .15rem var(--swf-input-focus-ring, rgba(47,111,181,.30));
    }

    .swf-tree-search > .bi-search {
        color: var(--swf-label-fg, #6b7a8d);
        font-size: .8rem;
    }

    .swf-tree-search input {
        flex: 1 1 auto;
        min-width: 0;
        border: 0;
        outline: 0;
        background: transparent;
        color: inherit;
        font-size: .85rem;
    }

.swf-tree-found {
    flex: 0 0 auto;
    font-size: .72rem;
    color: var(--swf-label-fg, #6b7a8d);
    white-space: nowrap;
}

    .swf-tree-found.none {
        color: var(--swf-rpt-neg, #b42318);
    }

.swf-tree-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--swf-label-fg, #6b7a8d);
    cursor: pointer;
}

    .swf-tree-clear:hover {
        background-color: rgba(36, 90, 141, .12);
    }

/* DataGridComponent's main search box - same look as .swf-tree-search. */
.swf-grid-search {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    height: 28px;
    padding: 0 4px 0 8px;
    border: 1px solid var(--swf-input-border-color, #c3cbd6);
    border-radius: 5px;
    background-color: var(--swf-input-bg-color, #fff);
}

    .swf-grid-search:focus-within {
        border-color: var(--swf-input-focus-border-color, #2f6fb5);
        box-shadow: 0 0 0 .15rem var(--swf-input-focus-ring, rgba(47,111,181,.30));
    }

    .swf-grid-search > .bi-search {
        color: var(--swf-label-fg, #6b7a8d);
        font-size: .8rem;
    }

    .swf-grid-search input {
        flex: 1 1 auto;
        min-width: 0;
        border: 0;
        outline: 0;
        background: transparent;
        color: inherit;
        font-size: .85rem;
    }

.swf-grid-search-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--swf-label-fg, #6b7a8d);
    cursor: pointer;
}

    .swf-grid-search-clear:hover {
        background-color: rgba(36, 90, 141, .12);
    }

/* The matched part of a node's text. */
.swf-tree-mark {
    padding: 0 1px;
    border-radius: 2px;
    background-color: #ffe58a;
    color: inherit;
}

.swf-tree-tool {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 26px;
    padding: 0 8px;
    border: 1px solid transparent;
    border-radius: 5px;
    background: transparent;
    color: var(--swf-rpt-accent, #245a8d);
    font-size: .8rem;
    font-weight: 600;
    cursor: pointer;
}

    .swf-tree-tool:hover:not(:disabled) {
        background-color: rgba(36, 90, 141, .10);
        border-color: rgba(36, 90, 141, .25);
    }

    .swf-tree-tool:focus-visible {
        outline: 0;
        box-shadow: 0 0 0 .15rem var(--swf-input-focus-ring, rgba(47,111,181,.30));
    }

    .swf-tree-tool:disabled {
        opacity: .45;
        cursor: default;
    }

.swf-tree {
    flex: 1 1 auto;
    min-height: 0;
    padding: 4px 0;
    overflow: auto;
    color: var(--swf-grid-head-fg, #2b3a4a);
    text-align: left;
    outline: 0;
}

    /* Inset: the box around it clips anything drawn outside. */
    .swf-tree:focus-visible {
        box-shadow: inset 0 0 0 2px var(--swf-input-focus-ring, rgba(47,111,181,.30));
    }

.swf-tree-group {
    list-style: none;
    margin: 0;
    padding: 0;
}

.swf-tree-row {
    display: flex;
    align-items: center;
    gap: 1px;
    min-height: 28px;
    padding-left: 10px;
    padding-right: 8px;
    cursor: pointer;
    white-space: nowrap;
    user-select: none;
}

    .swf-tree-row:hover {
        background-color: var(--swf-rpt-row-hover, rgba(36, 90, 141, .07));
    }

    .swf-tree-row.selected {
        background-color: var(--swf-grid-focus-row-bg, #e7eeff);
        box-shadow: inset 3px 0 0 var(--swf-rpt-accent, #245a8d);
        font-weight: 600;
    }

    /* Keyboard position, shown only while the tree has focus. */
    .swf-tree:focus-visible .swf-tree-row.focused,
    .swf-tree:focus .swf-tree-row.focused {
        outline: 1px dashed var(--swf-input-focus-border-color, #2f6fb5);
        outline-offset: -2px;
    }

/* Parent-to-child connector lines. A group's own left edge (its li's left
   edge) sits 15px right of its parent's - the row's 4px padding-left plus
   half of the 22px toggle - so it lands exactly under the parent's chevron;
   the vertical trunk is drawn right there, not offset further left. Each
   <li> draws the corner that reaches from the trunk into its own row: a
   vertical run down its height (stopped halfway for the last child, so the
   line doesn't run past it) and a horizontal stub into the row. The root
   group (top-level nodes) gets none. */
.swf-tree-group:not(.swf-tree-root-group) {
    margin-left: 20px;
}

    .swf-tree-group:not(.swf-tree-root-group) > li {
        position: relative;
    }

        .swf-tree-group:not(.swf-tree-root-group) > li::before,
        .swf-tree-group:not(.swf-tree-root-group) > li::after {
            content: "";
            position: absolute;
            left: 0;
        }

        .swf-tree-group:not(.swf-tree-root-group) > li::before {
            top: 14px;
            width: 15px;
            border-top: 1px solid var(--swf-grid-border, #cdd7e3);
        }

        .swf-tree-group:not(.swf-tree-root-group) > li::after {
            top: 0;
            height: 100%;
            border-left: 1px solid var(--swf-grid-border, #cdd7e3);
        }

        .swf-tree-group:not(.swf-tree-root-group) > li:last-child::after {
            height: 14px;
        }

.swf-tree-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 22px;
    height: 22px;
    border-radius: 4px;
    color: var(--swf-rpt-accent, #245a8d);
    font-size: .8rem;
}

    .swf-tree-toggle:not(.leaf):hover {
        background-color: rgba(36, 90, 141, .12);
    }

    .swf-tree-toggle.leaf {
        color: var(--swf-grid-border, #9aa5b1);
        font-size: 1rem;
        cursor: inherit;
    }

.swf-tree-text {
    padding: 2px 1px;
}

/* --------- SwfSplitContainer ------------
   Panel 1 is sized by --swf-split-size (set by the component, changed live by
   splitHandler while dragging); panel 2 is flex:1 and takes the rest, so the
   bar's thickness never has to be subtracted anywhere. */
.swf-split {
    display: flex;
    width: 100%;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--swf-grid-border, #cdd7e3);
}

    .swf-split.vertical {
        flex-direction: row;
    }

    .swf-split.horizontal {
        flex-direction: column;
    }

/* Vertical scrolling only, as before. Filter areas use Bootstrap .row, whose
   negative gutters are a few px wider than the panel - allowing overflow-x
   gave every filter a pointless horizontal scrollbar, and that bar's height
   then left "fit to content" one scrollbar short. */
.swf-split-panel {
    min-width: 0;
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
}

.swf-split-panel1 {
    flex: 0 0 var(--swf-split-size, 50px);
}

.swf-split-panel2 {
    flex: 1 1 0;
}

/* Panel 2 hidden: panel 1 has no sibling left to take "the rest", so it
   takes over the flex-grow panel 2 gave up instead of staying pinned to
   --swf-split-size. */
.swf-split.panel2-hidden > .swf-split-panel1 {
    flex: 1 1 0;
}

/* .swf-split-content (panel 1's wrapper) is deliberately auto-height by
   default: AutoSizePanel1 and the bar's double-click-to-fit both measure its
   *natural* content height to size the panel around it, and a forced 100%
   would make that measurement circular. A grid placed in panel 1 wants the
   opposite - to fill whatever height the panel actually has - so that's an
   opt-in via CssClass="swf-split-fill1" on the container, scoped to just
   that instance rather than every split's panel 1. */
.swf-split.swf-split-fill1 > .swf-split-panel1 > .swf-split-content {
    height: 100%;
}

/* The bar: a quiet band with a small grip, accent-coloured on hover, focus
   and while dragging. */
.swf-split-bar {
    flex: 0 0 var(--swf-split-bar, 6px);
    position: relative;
    background-color: var(--swf-grid-head-bg, #eef2f7);
    touch-action: none;
    z-index: 5;
    transition: background-color .12s ease;
}

    .swf-split.horizontal > .swf-split-bar {
        cursor: row-resize;
        border-block: 1px solid var(--swf-grid-border, #cdd7e3);
    }

    .swf-split.vertical > .swf-split-bar {
        cursor: col-resize;
        border-inline: 1px solid var(--swf-grid-border, #cdd7e3);
    }

    /* Grip: three dots in the middle of the bar. */
    .swf-split-bar::after {
        content: "";
        position: absolute;
        inset: 50% auto auto 50%;
        width: 18px;
        height: 2px;
        transform: translate(-50%, -50%);
        background-image: radial-gradient(circle, var(--swf-label-fg, #3c4858) 1px, transparent 1.2px);
        background-size: 6px 2px;
        opacity: .55;
    }

    .swf-split.vertical > .swf-split-bar::after {
        width: 2px;
        height: 18px;
        background-size: 2px 6px;
    }

    .swf-split-bar:hover,
    .swf-split-bar:focus-visible,
    .swf-split.dragging > .swf-split-bar {
        background-color: var(--swf-input-focus-border-color, #2f6fb5);
        outline: 0;
    }

        .swf-split-bar:hover::after,
        .swf-split-bar:focus-visible::after,
        .swf-split.dragging > .swf-split-bar::after {
            background-image: radial-gradient(circle, #fff 1px, transparent 1.2px);
            opacity: .9;
        }

    /* While dragging, nothing else on the page reacts or gets selected. */
    .swf-split.dragging {
        user-select: none;
        cursor: row-resize;
    }

        .swf-split.dragging.vertical {
            cursor: col-resize;
        }

        .swf-split.dragging > .swf-split-panel {
            pointer-events: none;
        }

/* ##################################### */
/* ### SwfForm chrome ###
   These were inline styles on SwfForm.razor, which meant the page header
   colour could only be changed by editing the control. They are variables in
   swiftTheme1.css now, so one edit re-themes every page. */
.swf-form-header {
    background: var(--swf-form-header-bg, linear-gradient(180deg, #3573b5, #245a8d));
    border: 1px solid var(--swf-form-header-border, #1d3f66);
    border-bottom: none;
    border-top-left-radius: 6px;
    border-top-right-radius: 6px;
    align-items: center;
    position: relative;
    min-height: 40px;
}

.swf-form-title {
    position: absolute;
    text-align: center;
    color: var(--swf-form-header-fg, #fff);
    font-size: 1.15rem;
    font-weight: 600;
    letter-spacing: .01em;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    padding-bottom: 2px;
}

.swf-form-body {
    text-align: center;
    border: 1px solid var(--swf-form-body-border, #d3dce7);
    border-top: none;
    border-bottom-left-radius: 6px;
    border-bottom-right-radius: 6px;
    box-shadow: var(--swf-form-body-shadow, 0 1px 3px rgba(16, 24, 40, 0.10));
    background-color: var(--swf-form-body-bg, #fff);
    color: var(--swf-form-body-fg, inherit);
}

.swf-offcanvas-header {
    height: 20px;
    background-color: var(--swf-grid-head-bg, #eef2f7);
    border-bottom: 1px solid var(--swf-grid-border, #cdd7e3);
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
}

/* ### SwfModelDialog / Bootstrap modal chrome ###
   Was an inline `to bottom #66a3ff -> #e6f0ff` gradient repeated in 23 pages. */
.swf-modal-header {
    height: 40px;
    background: var(--swf-modal-head-bg, #f4f7fb);
    border-bottom: 1px solid var(--swf-grid-border, #cdd7e3);
    color: var(--swf-grid-head-fg, #2b3a4a);
    font-weight: 600;
}

.swf-modal-footer {
    margin: 0;
    height: 45px;
    background: var(--swf-modal-head-bg, #f4f7fb);
    border-top: 1px solid var(--swf-grid-border, #cdd7e3);
}

/* SwfForm search panel.
   .offcanvas is a flex column, so .offcanvas-body is a flex item - and a flex
   item's default min-height:auto means it cannot shrink below its content. A
   search panel whose content is taller than the panel therefore overflowed the
   bottom edge and painted its rows over the page behind, rather than scrolling.
   min-height:0 restores shrinking so the body's own overflow-y:auto applies. */
.swf-offcanvas-body {
    min-height: 0;
    display: flex;
    flex-direction: column;
}

    /* Everything in the panel keeps its natural height so the chip filter is
       not squashed; a section that should absorb the remaining space opts in
       with its own flex:1 1 auto. */
    .swf-offcanvas-body > * {
        flex: 0 0 auto;
        min-height: 0;
    }

/* ColumnAutoSize grids, after swfGridFreeze() has pinned the measured column
   widths (see control.js). The table stops being one table box and becomes a
   fixed header block over a scrolling body, so the body's scrollbar begins
   BELOW the header and rows can no longer pass beneath it. Column widths come
   from the generated #<id> stylesheet, so header and body still agree. */
.grid-table.swf-grid-auto.swf-grid-frozen {
    display: flex;
    flex-direction: column;
    align-self: stretch;
    height: 100%;
    min-width: 0;
    overflow: hidden;
}

    .grid-table.swf-grid-auto.swf-grid-frozen thead,
    .grid-table.swf-grid-auto.swf-grid-frozen tfoot {
        display: block;
        flex: 0 0 auto;
    }

    .grid-table.swf-grid-auto.swf-grid-frozen tbody {
        display: block;
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overflow-x: hidden;
    }

        .grid-table.swf-grid-auto.swf-grid-frozen thead tr,
        .grid-table.swf-grid-auto.swf-grid-frozen tbody tr,
        .grid-table.swf-grid-auto.swf-grid-frozen tfoot tr {
            display: table;
            table-layout: fixed;
        }

    /* The header is its own block now, so it no longer needs to stick - and a
       sticky cell here would re-introduce the overlap we just removed. */
    .grid-table.swf-grid-auto.swf-grid-frozen thead th,
    .grid-table.swf-grid-auto.swf-grid-frozen thead td,
    .grid-table.swf-grid-auto.swf-grid-frozen tfoot th,
    .grid-table.swf-grid-auto.swf-grid-frozen tfoot td {
        position: static;
    }

/* ### DataGridComponent: header and grouping, every grid ###
   The report look, applied to all grids for the column header and group
   rows only (rows, zeros and the grand total stay report-specific, below).
   Colours are the --swf-rpt-* tokens in swiftTheme1.css. */

/* Accent rule under the column headings - only on cells that reach the
   bottom of the header. With two header rows, a spanning parent heading
   (colspan) sits above its sub-columns and must not get the line too; every
   other cell either is in the last row or spans down to it. */
.grid-table thead.tableHead tr:last-child > th,
.grid-table thead.tableHead tr > th:not([colspan]) {
    border-bottom: 2px solid var(--swf-rpt-accent, #245a8d) !important;
}

.grid-table thead.tableHead th {
    color: var(--swf-grid-head-fg);
}

/* Same accent rule, mirrored above the footer. Lower specificity than the
   .swf-report double-border override below, which must still win there. */
.grid-table tfoot.tableHead td {
    border-top: 2px solid var(--swf-rpt-accent, #245a8d) !important;
}

/* Column move: headings can be dragged onto another heading. A 3px accent bar
   (inset shadow, so nothing shifts) marks the side the column will land on. */
.grid-table thead th.swf-col-movable {
    cursor: grab;
}

    .grid-table thead th.swf-col-movable:active {
        cursor: grabbing;
    }

.grid-table thead th.swf-col-drop-before {
    box-shadow: inset 3px 0 0 var(--swf-input-focus-border-color, #2f6fb5) !important;
}

.grid-table thead th.swf-col-drop-after {
    box-shadow: inset -3px 0 0 var(--swf-input-focus-border-color, #2f6fb5) !important;
}

/* Column resize: a thin drag handle on a resizable heading's right edge (see
   swfColResize in control.js). Positioned relative to the th so the handle
   sits over the border regardless of that column's own width. */
.grid-table thead th.swf-col-resizable {
    position: relative;
}

    .grid-table thead th.swf-col-resizable .swf-col-resizer {
        position: absolute;
        top: 0;
        right: -3px;
        width: 6px;
        height: 100%;
        cursor: col-resize;
        z-index: 2;
        touch-action: none;
    }

        .grid-table thead th.swf-col-resizable .swf-col-resizer:hover,
        .grid-table thead th.swf-col-resizable .swf-col-resizer:active {
            background-color: var(--swf-input-focus-border-color, #2f6fb5);
            opacity: .5;
        }

/* Print / export progress card (DataGridComponent). A light backdrop so the
   page stays visible, and so a second click cannot start another export. */
.swf-export-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1060;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(16, 24, 40, .18);
    cursor: progress;
}

.swf-export-card {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 260px;
    padding: 16px 22px;
    border: 1px solid var(--swf-grid-border, #cdd7e3);
    border-left: 4px solid var(--swf-rpt-accent, #245a8d);
    border-radius: 0 8px 8px 0;
    background-color: var(--swf-input-bg-color, #fff);
    box-shadow: 0 8px 24px rgba(16, 24, 40, .18);
}

.swf-export-spinner {
    width: 1.6rem;
    height: 1.6rem;
    border-width: .2em;
    color: var(--swf-rpt-accent, #245a8d);
}

.swf-export-title {
    font-weight: 600;
    color: var(--swf-grid-head-fg, #2b3a4a);
}

.swf-export-step {
    font-size: .85rem;
    color: var(--swf-label-fg, #5a6b7d);
}

/* PDF print preview (swfShowPdf in control.js). A full-page panel rather
   than a new tab/window: the PDF is built after an async round trip to the
   server, and a window opened at that point gets silently eaten by the
   browser's popup blocker (same reason swfPrintPdf uses an iframe, above). */
.swf-pdf-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background-color: rgba(16, 24, 40, .6);
}

.swf-pdf-bar {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: space-between;
    padding: 8px 8px 8px 16px;
    background: var(--swf-form-header-bg, #245a8d);
    color: var(--swf-form-header-fg, #fff);
}

.swf-pdf-title {
    font-weight: 600;
}

.swf-pdf-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #fff;
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
}

    .swf-pdf-close:hover {
        background-color: rgba(255, 255, 255, .15);
    }

.swf-pdf-frame {
    flex: 1 1 auto;
    width: 100%;
    border: 0;
    background-color: #525659;
}

/* SwfModelDialog: a dimmed backdrop plus an absolutely-positioned panel sized
   to its content. Own classes rather than Bootstrap's .modal - that assumes
   a JS-driven fade/show lifecycle this component doesn't use, and a bare
   .show class has collided with it before (see the dropdown-content note
   above). The header reuses the SwfForm/report header tokens so every
   header bar in the app - form, report, dialog - matches. */
.swf-modeldlg {
    position: fixed;
    inset: 0;
    z-index: 1055;
    background-color: rgba(15, 23, 42, .5);
    outline: 0;
}

.swf-modeldlg-content {
    position: absolute;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 16px);
    border-radius: 8px;
    overflow: auto;
    background-color: var(--swf-input-bg-color, #fff);
    box-shadow: 0 10px 30px rgba(16, 24, 40, .35);
}

.swf-modeldlg-header {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: .5rem;
    height: 34px;
    padding: 0 6px 0 12px;
    background: var(--swf-form-header-bg, #245a8d);
    color: var(--swf-form-header-fg, #fff);
}

.swf-modeldlg-title {
    flex: 1 1 auto;
    margin: 0;
    overflow: hidden;
    font-size: .92rem;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.swf-modeldlg-actions {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 2px;
}

/* OK and Close share one look: same box, same icon size and weight. */
.swf-modeldlg-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

    .swf-modeldlg-btn:hover {
        background-color: rgba(255, 255, 255, .15);
    }

    .swf-modeldlg-btn:focus-visible {
        outline: 2px solid rgba(255, 255, 255, .6);
        outline-offset: 1px;
    }

.swf-modeldlg-body {
    flex: 1 1 auto;
    padding: 0.5rem;
}

/* Group bar above a grid: drop a heading here to group by it. Height is set
   inline from GroupBarHeight (the grid gives up the same amount). */
.swf-grp-bar {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 4px 4px 0;
    padding: 0 8px;
    border: 1px dashed var(--swf-grid-border, #cdd7e3);
    border-radius: 6px;
    background-color: var(--swf-grid-head-bg, #eef2f7);
    color: var(--swf-grid-head-fg, #2b3a4a);
    font-size: .85rem;
    white-space: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    transition: background-color .12s ease, border-color .12s ease;
}

    /* A heading is being dragged: show where it can go. */
    .swf-grp-bar.drop-ready {
        border-color: var(--swf-input-focus-border-color, #2f6fb5);
        background-color: var(--swf-input-focus-bg, #f4f9ff);
    }

.swf-grp-bar-icon {
    color: var(--swf-rpt-accent, #245a8d);
    font-size: 1rem;
}

.swf-grp-hint {
    color: var(--swf-label-fg, #6b7a8d);
    opacity: .8;
}

.swf-grp-sep {
    color: var(--swf-grid-border, #9aa5b1);
    font-size: .75rem;
}

.swf-grp-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    height: 22px;
    padding: 0 2px 0 9px;
    border: 1px solid var(--swf-rpt-accent, #245a8d);
    border-radius: 999px;
    background-color: var(--swf-input-bg-color, #fff);
    color: var(--swf-rpt-accent, #245a8d);
    font-weight: 600;
}

    /* Set by the page (e.g. a report's Group checkbox): shown, not removable here. */
    .swf-grp-chip.locked {
        padding-right: 9px;
        border-style: dashed;
    }

    .swf-grp-chip button,
    .swf-grp-clear {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 0;
        background: transparent;
        color: inherit;
        padding: 0;
        width: 18px;
        height: 18px;
        border-radius: 50%;
        line-height: 1;
        cursor: pointer;
    }

        .swf-grp-chip button:hover,
        .swf-grp-clear:hover {
            background-color: rgba(36, 90, 141, .12);
        }

.swf-grp-clear {
    color: var(--swf-label-fg, #6b7a8d);
}

/* Collapse all / expand all / clear, pushed to the right end of the bar. */
.swf-grp-actions {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-left: auto;
}

/* Group heading: tinted band with an accent edge. The edge is an inset
   shadow, so it doesn't push the text the way a border-left would; the grid
   pads the text 8px clear of it. !important beats Bootstrap's striped-row
   shadow. */
.grid-table tr.swf-grp-head > td {
    background-color: var(--swf-rpt-group-bg, #f3f7fc);
    color: var(--swf-rpt-accent, #245a8d);
    box-shadow: inset 3px 0 0 var(--swf-rpt-accent, #245a8d) !important;
    padding-top: 5px;
    padding-bottom: 5px;
}

/* No underline any more - the band marks the group. It used to be an inline
   border on the baseline, cutting through Gujarati vowel signs. */
.swf-grp-title {
    display: inline-block;
    line-height: 1.5;
    font-weight: 600;
}

/* Row count after a group heading: "(6)". Quieter than the title. */
.swf-grp-count {
    margin-left: 6px;
    font-size: .85em;
    font-weight: 400;
    color: var(--swf-label-fg, #5a6b7d);
}

/* Collapsible group heading: the whole row is the toggle. */
.grid-table tr.swf-grp-toggle {
    cursor: pointer;
    user-select: none;
}

    .grid-table tr.swf-grp-toggle:hover > td {
        filter: brightness(.97);
    }

.swf-grp-chevron {
    display: inline-block;
    width: 1em;
    margin-right: 4px;
    font-size: .8rem;
    color: var(--swf-rpt-accent, #245a8d);
    -webkit-text-stroke: .4px;
}

/* Group subtotal row: the grid writes a grey fill and black borders inline,
   hence !important. */
.grid-table tr.swf-grp-total > td {
    background-color: var(--swf-rpt-group-bg, #f3f7fc) !important;
    color: var(--swf-rpt-accent, #245a8d);
    border: none !important;
    border-top: 1px solid var(--swf-grid-border, #cdd7e3) !important;
    padding: 3px !important;
    box-shadow: none !important;
}

/* ### Reports (SwfReport) - "ledger navy" ###
   The bar reuses the SwfForm header tokens so reports and entry forms share
   one look. Everything below is scoped to .swf-report, so data-entry grids are
   not affected. Colours come from the --swf-rpt-* tokens in swiftTheme1.css. */
.swf-report-bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    /* 34px tall: SwfReport sizes the area below as 100vh - 96px to match. */
    height: 34px;
    padding: 0 6px;
    background: var(--swf-form-header-bg, #245a8d);
    color: var(--swf-form-header-fg, #fff);
    border: 1px solid var(--swf-form-header-border, #1d3f66);
    border-radius: 6px 6px 0 0;
}

.swf-report-tools {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 1 1 0;
    min-width: 0;
}

    .swf-report-tools:last-child {
        justify-content: flex-end;
    }

.swf-report-title {
    flex: 0 1 auto;
    min-width: 0;
    text-align: center;
    line-height: 1.2;
}

    .swf-report-title strong {
        display: block;
        font-size: 1.05rem;
        font-weight: 600;
        letter-spacing: .01em;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

/* Flat icon buttons: one colour, a soft wash on hover, a ring on keyboard focus. */
.swf-rpt-btn {
    color: var(--swf-form-header-fg, #fff) !important;
    background-color: transparent;
    border: 1px solid transparent !important;
    border-radius: 6px !important;
    padding: 4px 7px !important;
    line-height: 1 !important;
}

    .swf-rpt-btn i {
        display: block;
        font-size: 1.05rem;
    }

    .swf-rpt-btn:hover:not(:disabled) {
        background-color: rgba(255, 255, 255, .14);
    }

    /* Doubled: Bootstrap's .btn.active weighs the same and took the highlight away. */
    .swf-rpt-btn.swf-rpt-btn.active {
        background-color: rgba(255, 255, 255, .18);
        border-color: rgba(255, 255, 255, .35) !important;
    }

    .swf-rpt-btn:focus-visible {
        box-shadow: 0 0 0 .2rem rgba(255, 255, 255, .55) !important;
        outline: 0 !important;
    }

.swf-report .grid-table {
    --bs-table-striped-bg: var(--swf-rpt-stripe);
    font-family: 'Segoe UI', 'Nirmala UI', system-ui, sans-serif;
    font-variant-numeric: tabular-nums;
}

    /* Rows at normal weight - bold Gujarati in long lists is heavy to read.
       Headers, group rows and totals keep their weight. */
    .swf-report .grid-table tbody td {
        font-weight: 400;
        border-bottom: 1px solid var(--swf-rpt-rule);
    }

    .swf-report .grid-table tbody tr:hover > td {
        background-color: var(--swf-rpt-row-hover);
    }

    /* The column the report is about (e.g. Closing): set Class="swf-col-key". */
    .swf-report .grid-table tbody td.swf-col-key {
        background-color: var(--swf-rpt-key-bg);
        font-weight: 600;
    }

    /* Zeros dimmed, negatives red: always in reports, and in any grid with
       NumberColors="true" (class swf-num-colors). */
    .swf-report .grid-table td.swf-num-zero,
    .grid-table.swf-num-colors td.swf-num-zero {
        color: var(--swf-rpt-zero) !important;
    }

    .swf-report .grid-table td.swf-num-neg,
    .grid-table.swf-num-colors td.swf-num-neg {
        color: var(--swf-rpt-neg) !important;
    }

    /* Grand total: the double rule of a printed ledger. */
    .swf-report .grid-table tfoot.tableHead td {
        border-top: 3px double var(--swf-rpt-accent) !important;
        color: var(--swf-grid-head-fg);
    }

/* Grid settings menu.

   Deliberately NOT Bootstrap's .dropdown-menu / .dropdown-item: SwfCommon's
   menu.css repaints those selectors in the dark navbar colour with !important
   for the top nav, and SwfBlazor sits below SwfCommon - the grid must not
   inherit a theme from a layer above it. Own classes, own light palette. */
.swf-dg-menu {
    background-color: #ffffff;
    border: 1px solid #dfe3e8;
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(16, 24, 40, 0.18);
    padding: 4px;
    font-family: 'Segoe UI', sans-serif;
}

.swf-dg-item {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 6px 8px;
    border: none;
    border-radius: 4px;
    background-color: transparent;
    color: #212529;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}

    .swf-dg-item:hover,
    .swf-dg-item:focus {
        background-color: #eef3fb;
        color: #0d3b8f;
        outline: none;
    }

    /* The Export row while its flyout is open. */
    .swf-dg-item.swf-dg-open {
        background-color: #e2ebfa;
        color: #0d3b8f;
    }

.swf-dg-sep {
    height: 1px;
    margin: 4px 2px;
    background-color: #e6e8eb;
}

.swf-dg-menu:focus {
    outline: none;
}

/* Column filter dropdown (Excel style). Shares .swf-dg-menu for the shell, so
   the panel, the settings menu and their flyouts stay one visual family. */
.swf-fltr-cell {
    position: relative;
    display: block;
}

    .swf-fltr-cell input {
        padding-right: 20px !important;
    }

.swf-fltr-btn {
    position: absolute;
    top: 50%;
    right: 1px;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    padding: 0 3px;
    border: none;
    border-radius: 3px;
    background-color: transparent;
    color: #5a6472;
    font-size: 0.78rem;
    line-height: 1;
    cursor: pointer;
}

    .swf-fltr-btn:hover {
        background-color: #dfe7f3;
        color: #0d3b8f;
    }

    /* A column that is actually filtering says so, the way Excel does. */
    .swf-fltr-btn.swf-fltr-on {
        color: #0d3b8f;
    }

.swf-fltr-panel {
    max-width: 92vw;
}

.swf-fltr-args {
    padding: 2px 4px 4px;
}

.swf-fltr-search {
    margin: 2px 0;
}

/* The value list is the only part that scrolls; the panel itself must not,
   or the operator flyout would be clipped by the scroll container. */
.swf-fltr-list {
    max-height: 190px;
    overflow-y: auto;
    border: 1px solid #e6e8eb;
    border-radius: 4px;
    padding: 2px;
}

.swf-fltr-opt {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 3px 4px;
    border-radius: 3px;
    color: #212529;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

    .swf-fltr-opt:hover {
        background-color: #eef3fb;
    }

    .swf-fltr-opt input {
        flex: 0 0 auto;
        margin: 0;
    }

    .swf-fltr-opt span,
    .swf-fltr-opt b {
        overflow: hidden;
        text-overflow: ellipsis;
    }

/* "(Select All)" with the invert-selection button at its right. */
.swf-fltr-allrow {
    display: flex;
    align-items: center;
    gap: 2px;
}

    .swf-fltr-allrow .swf-fltr-opt {
        flex: 1 1 auto;
        min-width: 0;
    }

.swf-fltr-invert {
    flex: 0 0 auto;
    width: 26px;
    height: 24px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 4px;
    background: none;
    color: #2f6fb5;
}

    .swf-fltr-invert:hover:not(:disabled) {
        background-color: #eef3fb;
        border-color: #c9d8ee;
    }

    .swf-fltr-invert:disabled {
        color: #b8c0ca;
    }

.swf-fltr-empty {
    padding: 8px 4px;
    color: #6b7a8d;
    text-align: center;
}

.swf-fltr-actions {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    padding-top: 6px;
}

    /* Square icon buttons for apply / cancel. */
    .swf-fltr-actions .swf-fltr-act {
        width: 34px;
        height: 30px;
        padding: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: 1rem;
    }

/* A menu entry that is present but has nothing to act on. */
.swf-dg-item.swf-dg-off {
    color: #9aa3ad;
    pointer-events: none;
}

/* ##################################### */
/* ### Validation bubble (SwfErrorTip) ###
   Overlays the form instead of adding a line under the control, so the layout
   stays put. The anchor has no height; the bubble hangs from it with an arrow
   pointing up at the input. Click to dismiss. */
.swf-err-anchor {
    position: relative;
    height: 0;
    margin: 0;
    padding: 0;
}

.swf-err-tip {
    position: absolute;
    top: -3px;
    left: 0;
    z-index: 1060;
    display: flex;
    align-items: center;
    gap: 6px;
    /* As wide as the message, wrapping only when it would not fit the window. */
    width: max-content;
    max-width: calc(100vw - 16px);
    padding: 4px 10px;
    border-radius: 6px;
    background: #d63939;
    color: #fff;
    font-size: .8rem;
    font-weight: 600;
    line-height: 1.25;
    text-align: left;
    white-space: normal;
    box-shadow: 0 4px 12px rgba(16, 24, 40, 0.22);
    cursor: pointer;
    animation: swf-err-in .14s ease-out;
}

    .swf-err-tip::before {
        content: "";
        position: absolute;
        top: -6px;
        left: var(--swf-err-arrow, 14px);
        border-width: 0 6px 6px 6px;
        border-style: solid;
        border-color: transparent transparent #d63939 transparent;
    }

    .swf-err-tip i {
        flex: 0 0 auto;
    }

    /* Above the anchor, arrow pointing down (SwfErrorTip Above="true"). */
    .swf-err-tip.above {
        top: auto;
        bottom: 3px;
    }

        .swf-err-tip.above::before {
            top: auto;
            bottom: -6px;
            border-width: 6px 6px 0 6px;
            border-color: #d63939 transparent transparent transparent;
        }

@keyframes swf-err-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ##################################### */
/* ### SwfDialogHost - alert/confirm ###
   The backdrop is a full-viewport flex box, so the dialog is always in the
   centre of the screen whatever the scroll position or window size.
   Colours are the --swf-dlg-* and form tokens in swiftTheme1.css. */
.swf-dlg-backdrop {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background-color: rgba(15, 23, 42, .45);
    animation: swf-err-in .12s ease-out;
}

.swf-dlg {
    display: flex;
    flex-direction: column;
    width: min(440px, 100%);
    max-height: calc(100vh - 32px);
    overflow: hidden;
    text-align: left;
    background-color: var(--swf-form-body-bg, #fff);
    color: var(--swf-form-body-fg, #212529);
    border: 1px solid var(--swf-form-header-border, #1d3f66);
    border-radius: 8px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, .28);
    animation: swf-dlg-in .14s ease-out;
}

.swf-dlg-header {
    padding: 8px 14px;
    background: var(--swf-form-header-bg, linear-gradient(180deg, #3573b5, #245a8d));
    color: var(--swf-form-header-fg, #fff);
}

.swf-dlg-title {
    font-size: 1rem;
    font-weight: 600;
}

/* Long messages (an SQL error, say) scroll inside the dialog rather than
   pushing the buttons off screen. */
.swf-dlg-body {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 16px 16px 12px;
    overflow: auto;
}

.swf-dlg-icon {
    flex: 0 0 auto;
    font-size: 1.75rem;
    line-height: 1;
    color: var(--swf-dlg-info, #2f6fb5);
}

.swf-dlg-success .swf-dlg-icon { color: var(--swf-dlg-success, #1f8a4c); }
.swf-dlg-warning .swf-dlg-icon { color: var(--swf-dlg-warning, #b7791f); }
.swf-dlg-error .swf-dlg-icon { color: var(--swf-dlg-error, #c0392b); }
.swf-dlg-question .swf-dlg-icon { color: var(--swf-dlg-question, #2f6fb5); }

.swf-dlg-message {
    padding-top: 4px;
    white-space: pre-line;
    overflow-wrap: anywhere;
}

.swf-dlg-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 10px 14px;
    border-top: 1px solid var(--swf-form-body-border, #d3dce7);
}

.swf-dlg-btn {
    min-width: 84px;
    padding: 5px 16px;
    font-weight: 500;
    border-radius: 6px;
    border: 1px solid transparent;
}

    .swf-dlg-btn:focus-visible {
        outline: none;
        box-shadow: 0 0 0 var(--swf-input-focus-ring-width, .2rem) var(--swf-input-focus-ring, rgba(47, 111, 181, .30));
    }

.swf-dlg-ok {
    background-color: var(--swf-nav-accent, #3573b5);
    color: #fff;
}

    .swf-dlg-ok:hover {
        background-color: var(--swf-rpt-accent, #245a8d);
    }

.swf-dlg-cancel {
    background-color: transparent;
    color: var(--swf-tab-fg, #3c4858);
    border-color: var(--swf-tab-border, #cdd7e3);
}

    .swf-dlg-cancel:hover {
        background-color: var(--swf-tab-bg, #e6ecf3);
    }

/* Zero-size focus sentinels that keep Tab inside the dialog. */
.swf-dlg-trap {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

@keyframes swf-dlg-in {
    from { opacity: 0; transform: scale(.96); }
    to { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .swf-dlg-backdrop, .swf-dlg { animation: none; }
}

/* ##################################### */
/* ### SwfCombo list: rows as wide as the list ###
   The tbody is display:block so the list can scroll, which made every row its own shrink-to-fit
   table - the highlight, hover and click area stopped at the end of the text. Each row is laid
   out as a full-width fixed table instead; every row has the same cells, so a multi-select's
   checkbox column still lines up. Used by SwfCombo and the grid's combo cells (DataGridListCombo). */
.swf-combo-list tbody tr {
    display: table;
    width: 100%;
    table-layout: fixed;
}

    /* The text cell takes whatever the row has left. Its width:100% (inline in the grid combo, an
       attribute in SwfCombo) plus a multi-select's 23px checkbox cell would overflow the row. */
    .swf-combo-list tbody td:last-child {
        width: auto !important;
    }

/* The grid combo's list sits inside the grid, whose rules force display:table-row / width:auto on
   its rows (see ".grid-table .swf-grid-cmb" above). One class more specific, so the list's rows
   span the list there too. */
.grid-table .swf-grid-cmb .dropdownTable.swf-combo-list tbody tr {
    display: table !important;
    width: 100% !important;
    table-layout: fixed !important;
}

/* ##################################### */
/* ### SwfCombo list in dark mode ###
   The list carries Bootstrap's .text-dark (dark text, !important) for light mode; on the
   dark list background that would be unreadable. */
[data-theme="dark"] .dropdown-content.text-dark {
    color: var(--swf-form-body-fg, #dbe5ef) !important;
}

/* ##################################### */
/* ### Pagination: rows per page ###
   Two classes, so it still wins over Bootstrap's .form-select-sm now that control.css is loaded
   before bootstrap.css. */
.form-select.swf-page-size {
    display: inline-block;
    width: auto;
    height: 26px;
    padding: 0 1.8rem 0 .5rem;
    font-size: .8rem;
    vertical-align: middle;
}

/* ### A grid's own pager (DataGridComponent with GVStatus AllowPaging) ###
   A light bar in the grid header's colours - the pager's buttons are made for the form's dark
   title bar (white icons, white ring), which looked like a second title under the grid. */
.swf-grid-pager {
    background: var(--swf-grid-head-bg, #eef2f7);
    color: var(--swf-grid-head-fg, #2b3a4a);
    border: 1px solid var(--swf-grid-border, #cdd7e3);
    border-top: 0;
    border-radius: 0 0 6px 6px;
    padding: 3px 6px !important;
    align-items: center;
}

    .swf-grid-pager > .col-auto {
        display: flex;
        align-items: center;
        gap: 3px;
    }

    .swf-grid-pager .SwfFrmBtn {
        width: 28px;
        height: 26px;
        padding: 0 !important;
        margin: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        text-shadow: none;
        background: var(--bs-body-bg, #fff);
        border: 1px solid var(--swf-grid-border, #cdd7e3) !important;
        border-radius: 6px !important;
    }

        .swf-grid-pager .SwfFrmBtn:hover {
            border-color: var(--swf-grid-focus-row-border, #2f6fb5) !important;
            background: var(--swf-grid-focus-row-bg, #e7eeff);
        }

        .swf-grid-pager .SwfFrmBtn:focus-visible {
            border-color: var(--swf-grid-focus-row-border, #2f6fb5) !important;
            box-shadow: 0 0 0 var(--swf-input-focus-ring-width, 0.2rem) var(--swf-input-focus-ring, rgba(47,111,181,.30)) !important;
        }

        /* The icons carry an inline white colour for the title bar. */
        .swf-grid-pager .SwfFrmBtn i {
            color: var(--swf-grid-focus-row-border, #2f6fb5) !important;
            font-size: .95rem !important;
        }

    /* One small box to type a page into; "Page 1 of 2" is in the info text beside it. */
    .swf-grid-pager #ptxtCurrPgNo {
        width: 44px !important;
        height: 26px !important;
        font-size: .85rem;
        text-align: center !important;
        background: var(--bs-body-bg, #fff);
        color: inherit;
        border: 1px solid var(--swf-grid-border, #cdd7e3) !important;
        border-radius: 6px !important;
        -moz-appearance: textfield;
    }

        .swf-grid-pager #ptxtCurrPgNo::-webkit-inner-spin-button,
        .swf-grid-pager #ptxtCurrPgNo::-webkit-outer-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }

    .swf-grid-pager #ptxtTotPg {
        display: none;
    }

.swf-pager-info {
    font-size: .8rem;
    white-space: nowrap;
    opacity: .85;
}

.swf-pager-sep {
    margin: 0 .35rem;
    opacity: .6;
}

/* ##################################### */
/* ### SwfRadioB options ###
   One even gap between options (was a lone 20px spacer after the first). */
.swf-radio-opt {
    cursor: pointer;
    margin-right: 12px; /* on the right, so an option that wraps starts flush left */
}

    .swf-radio-opt:last-child {
        margin-right: 0;
    }

/* ##################################### */
/* Dashboard widgets (SwfWidgetCard + the SwfCommon dashboard shell). */
.swf-widget {
    border-radius: 10px;
    background: var(--swf-widget-bg, #fff);
}

.swf-widget-head {
    padding: 6px 10px;
    border-bottom: 1px solid rgba(0, 0, 0, .06);
    font-size: .86rem;
}

.swf-widget-title {
    font-weight: 600;
}

.swf-widget-sub {
    color: #6c757d;
    font-size: .74rem;
}

.swf-widget-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: #6c757d;
    cursor: pointer;
}

    .swf-widget-btn:hover {
        background-color: rgba(0, 0, 0, .07);
        color: #212529;
    }

.swf-widget-body {
    padding: 10px;
}

.swf-widget-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 60px;
}

/* The one big figure on a tile, with its comparison line underneath. */
.swf-widget-figure {
    font-size: 1.45rem;
    font-weight: 700;
    line-height: 1.2;
}

.swf-widget-note {
    color: #6c757d;
    font-size: .76rem;
}

/* Two or three figures side by side (this month / this year). */
.swf-widget-split {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
}

    .swf-widget-split > div {
        min-width: 90px;
    }

/* Compact list inside a widget (top parties, low stock, pending orders). */
.swf-widget-list {
    width: 100%;
    font-size: .8rem;
}

    .swf-widget-list td {
        padding: 2px 4px;
        border-bottom: 1px solid rgba(0, 0, 0, .05);
    }

        .swf-widget-list td.num {
            text-align: right;
            white-space: nowrap;
        }

.swf-dash-empty {
    padding: 24px;
    text-align: center;
    color: #6c757d;
}

/* Dashboard bar: company, financial year and the two buttons on one slim line. */
.swf-dash-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 2px 2px 8px;
}

.swf-dash-title {
    margin: 0;
    font-size: 1.5rem; /* h4 */
    font-weight: 700;
    color: var(--swf-nav-bg, darkslateblue);
    letter-spacing: .2px;
    text-shadow: 1px 1px 3px rgba(0, 0, 0, .18);
}

.swf-dash-year {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 9px;
    border: 1px solid rgba(0, 0, 0, .12);
    border-radius: 999px;
    background: rgba(0, 0, 0, .04);
    color: #495057;
    font-size: .76rem;
    font-weight: 600;
    white-space: nowrap;
}

/* Small buttons, so the line stays a bar and not a toolbar. */
.swf-dash-btn {
    padding: 0 6px !important;
    font-size: .72rem !important;
    line-height: 1.6;
    border: 1px solid rgba(0, 0, 0, .12) !important;
}

    .swf-dash-btn i {
        font-size: .78rem;
    }

/* Refresh and Customize stay at the right end of the bar, even when it wraps. */
.swf-dash-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

/* ##################################### */
/* SwfSidePanel: details sliding in from the right over the page. */
.swf-sidepanel-wrap {
    position: fixed;
    inset: 0;
    z-index: 1055;
    outline: none;
}

.swf-sidepanel-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(16, 24, 40, .35);
    animation: swf-sidepanel-fade .18s ease-out;
}

.swf-sidepanel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(460px, 94vw);
    display: flex;
    flex-direction: column;
    background: #fff;
    border-left: 1px solid rgba(0, 0, 0, .08);
    box-shadow: -10px 0 28px rgba(16, 24, 40, .18);
    animation: swf-sidepanel-in .22s cubic-bezier(.22, .61, .36, 1);
}

    .swf-sidepanel.wide {
        width: min(880px, 96vw);
    }

.swf-sidepanel-head {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    padding: 8px 10px;
    background: var(--swf-form-header-bg, #245a8d);
    color: var(--swf-form-header-fg, #fff);
}

.swf-sidepanel-title {
    font-size: .95rem;
    font-weight: 600;
}

.swf-sidepanel-sub {
    font-size: .76rem;
    opacity: .85;
}

.swf-sidepanel-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: #fff;
    font-size: 1rem;
    cursor: pointer;
}

    .swf-sidepanel-close:hover {
        background-color: rgba(255, 255, 255, .15);
    }

.swf-sidepanel-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 6px;
}

/* A panel with no title keeps only a slim strip for the close button, so its
   content (a grid, say) decides the width and gets the height. */
.swf-sidepanel-head.bare {
    padding: 2px 4px;
    background: #f1f3f5;
    color: #495057;
    border-bottom: 1px solid rgba(0, 0, 0, .08);
}

    .swf-sidepanel-head.bare .swf-sidepanel-close {
        width: 22px;
        height: 22px;
        color: #495057;
        font-size: .85rem;
    }

        .swf-sidepanel-head.bare .swf-sidepanel-close:hover {
            background-color: rgba(0, 0, 0, .08);
        }

/* A widget whose card opens a panel says so by the cursor. */
.swf-widget-clickable {
    cursor: pointer;
}

@keyframes swf-sidepanel-in {
    from {
        transform: translateX(100%);
    }

    to {
        transform: translateX(0);
    }
}

@keyframes swf-sidepanel-fade {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}
