/* REMOVED: .arrow_carrot-down:before { content: "\35" !important; }

   That rule forced the submenu caret to glyph \35 in the ElegantIcons font,
   to stop every caret flipping when one submenu opened. But that font is no
   longer loading, so \35 rendered as the plain character it is — the digit
   "5" — which is what was showing beside Reference Data / Setup / Doc Manager.

   The sidebar now uses Font Awesome chevrons (see the menu builder in
   ignite.js), which are already loaded everywhere else on the page. The
   click handler flips fa-chevron-right / fa-chevron-down on the clicked link
   only, so the original all-carets-change bug does not come back. */

.length-label {
    font-weight: normal;
}

.length-select {
    width: 80px !important;
}


@media (min-width: 992px) {
    .modal-massive {
        max-width: 98% !important;
        width: 98% !important;
    }
}

.yellow-star {
    color: #d4af37;
    font-size: 24px;
}

/* Data tables */
.dt-left {
    text-align: left;
}

.dt-center {
    text-align: center;
}

.dt-right {
    text-align: right;
}

.dataTables_scrollBody table tbody tr.selected {
    background-color: #99acbd;
}

.dataTables_scrollBody table tbody tr.selected:hover {
    background-color: #b0c0cf;
}

.table thead>tr>th {
    background: #f4f7fb !important;
    color: #4a6fa5;
    font-weight: 700;
    font-size: 10px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    border-bottom: 2px solid #d0e0f0 !important;
    border-top: none !important;
    padding: 8px 14px;
    white-space: normal;
    overflow-wrap: break-word;
    hyphens: auto;
    line-height: 1.3;
    vertical-align: bottom;
}

.light-yellow {
    background-color: #FFFF99 !important;
}

.__igdev {
    display: inline-block;
    background-color: #ffff99 !important;
    color: Black
}

table.dataTable tbody tr.selected,
table.dataTable tbody th.selected,
table.dataTable tbody td.selected {
    background-color: #99acbd !important;
    color: inherit !important;
    vertical-align: top;
}

.table-striped>tbody>tr.selected:nth-child(odd)>td,
.table-striped>tbody>tr.selected:nth-child(odd)>th,
.table-striped>tbody>tr.selected:nth-child(even)>td,
.table-striped>tbody>tr.selected:nth-child(even)>th {
    background-color: #9ab0be !important;
    color: inherit !important;
    vertical-align: top;
}


/* ── Row stripes: subtle blue tint instead of pure grey ── */
.table-striped>tbody>tr:nth-child(odd)>td,
.table-striped>tbody>tr:nth-child(odd)>th {
    background-color: #ffffff;
    border-bottom: 0.5px solid #f2f2f4;
    vertical-align: middle;
}


.table-striped>tbody>tr:nth-child(even)>td,
.table-striped>tbody>tr:nth-child(even)>th {
    background-color: #f8f8fa;
    border-bottom: 0.5px solid #f2f2f4;
    vertical-align: middle;
}

/* ── Hover: clear blue wash ── */
.table-striped>tbody>tr:hover>td,
.table-striped>tbody>tr:hover>th {
    background-color: #f0f0f8 !important;
}

/* ── Selected: stronger blue + left accent bar ── */
.table-striped>tbody>tr.selected>td,
.table-striped>tbody>tr.selected>th {
    background-color: #eeeeff !important;
    color: inherit !important;
    vertical-align: middle;
}

.table-striped>tbody>tr.selected>td:first-child {
    box-shadow: inset 2px 0 0 #6366f1;
}

/* ── First/last cell padding ── */
.table td:first-child,
.table th:first-child {
    padding-left: 16px;
}

.table td:last-child,
.table th:last-child {
    padding-right: 16px;
}

.dataTables_length {
    display: inline !important;
}

/* .dt-buttons display is owned by igniteButtons.css §8
   (inline-flex containment fix). The old
   `display: inline !important;` here let 34px atomic-inline buttons
   hang below their line box, so the table's scrollHead painted over
   their bottom edge at fractional zoom — the cut-off-buttons bug. */

.dataTables_info {
    display: inline !important;
}

.dataTables_paginate {
    float: right;
    width: 50%;
}

.dataTables_length,
.dataTables_filter {
    padding: 0px !important;
}

.dataTables_filter {
    display: inline-flex !important;
    align-items: center;
    flex: 1;
    /* stretch within the toolbar row */
}

.dataTables_filter label {
    display: flex;
    align-items: center;
    width: 100%;
    margin-bottom: 0;
    /* label must also stretch */
    font-size: 0;
    /* The wrapper stretches (flex:1 above) but the input is capped at 350px,
       so without this the field sits at the START of that stretched box and
       hugs whatever is to its left -- which on a dialog with no toolbar
       buttons is the card's left edge. Aligning to the end puts its right
       edge flush with the toolbar, and therefore with the table below it.
       Exactly what .ig-tree-search label already does in igniteTrees.css;
       the DataTables filter never got the matching line. */
    justify-content: flex-end;
}

/* Matches the Gantt search box (ganntChart.css .gantt-label-search-input). */
.dataTables_filter input.search-input {
    height: 24px !important;
    width: 160px;
    /* default compact size */
    max-width: 350px !important;
    padding: 0 30px 0 26px !important;
    font-size: 11px !important;
    color: #495057;
    background-color: #fff;
    background-image: url("/ignite/icons/search-b99f3208c452b7a44db6314fede2588f.svg");
    background-repeat: no-repeat;
    background-position: 8px center;
    background-size: 10px 10px;
    border: 1px solid #ced4da !important;
    border-radius: 4px !important;
    outline: none;
    transition: width .15s ease, border-color .15s, box-shadow .15s;
}

.dataTables_filter input.search-input:focus {
    border-color: #4b8fef !important;
    box-shadow: 0 0 0 2px rgba(75, 143, 239, 0.15) !important;
}

.dataTables_filter input.search-input::placeholder {
    color: #adb5bd;
    font-size: 11px;
}

/* ── Search tools: squares beside a table's search box (igAddTableSearchTools).
   Same square as the Gantt's (deliverableAdvancedFilter.css .advf-trigger-btn). */
.ig-search-tool {
    position: relative;
    flex: 0 0 auto;
    height: 24px;
    width: 22px;
    padding: 0;
    border: 1px solid #ced4da;
    border-radius: 4px;
    background: #fff;
    color: #6c757d;
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, border-color 0.12s, color 0.12s, box-shadow 0.15s;
}

.dataTables_filter .ig-search-tool {
    margin-left: 6px;
}

.ig-search-tool:hover,
.ig-search-tool.ig-search-tool-open {
    background: #f1f3f5;
    border-color: #4b8fef;
    color: #3574c4;
}

.ig-search-tool-badge {
    position: absolute;
    top: -5px;
    right: -5px;
    min-width: 13px;
    height: 13px;
    padding: 0 3px;
    border-radius: 7px;
    background: #4b8fef;
    color: #fff;
    font-size: 8px;
    font-weight: 700;
    line-height: 13px;
    text-align: center;
    display: none;
    pointer-events: none;
}

/* Blue = active and saved. Same as the filter's on state (.advf-trigger-btn.advf-on, deliverableAdvancedFilter.css). */
.ig-search-tool.ig-search-tool-on {
    border-color: #4b8fef;
    background: #eaf2fe;
    color: #2f6fc4;
    box-shadow: 0 0 0 2px rgba(75, 143, 239, 0.15);
}

.ig-search-tool.ig-search-tool-on .ig-search-tool-badge {
    display: block;
}

/* Amber = active but temporary, gone on reload. Same as the eye's on state (.advf-trigger-btn.advf-bypass-on). */
.ig-search-tool.ig-search-tool-warn {
    border-color: #d99a2b;
    background: #fdf4e3;
    color: #a4700f;
    box-shadow: 0 0 0 2px rgba(217, 154, 43, 0.15);
}

.ig-search-tool.ig-search-tool-warn .ig-search-tool-badge {
    display: block;
    background: #d99a2b;
}

/* Show all eye (icon library "filterShowAll"). Blinks on hover. */
.advf-sa-svg {
    width: 16px;
    height: 16px;
    display: block;
    overflow: visible;
}

.dt-button.btn-glass .advf-sa-svg {
    width: 16px !important;
    height: 16px !important;
}

.advf-sa-eye {
    transform-box: fill-box;
    transform-origin: center;
}

.advf-sa-lid {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linejoin: round;
}

.advf-sa-pupil {
    fill: currentColor;
}

@keyframes advfSaBlink {
    0%   { transform: scaleY(1); }
    22%  { transform: scaleY(0.08); }
    30%  { transform: scaleY(0.08); }
    55%  { transform: scaleY(1); }
    100% { transform: scaleY(1); }
}

@keyframes advfSaBlinkPupil {
    0%   { opacity: 1; }
    18%  { opacity: 0; }
    34%  { opacity: 0; }
    58%  { opacity: 1; }
    100% { opacity: 1; }
}

.advf-bypass:hover .advf-sa-eye,
.ig-search-tool:hover .advf-sa-eye {
    animation: advfSaBlink 1.6s cubic-bezier(0.22, 0.68, 0.36, 1) infinite;
}

.advf-bypass:hover .advf-sa-pupil,
.ig-search-tool:hover .advf-sa-pupil {
    animation: advfSaBlinkPupil 1.6s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .advf-bypass:hover .advf-sa-eye,
    .advf-bypass:hover .advf-sa-pupil,
    .ig-search-tool:hover .advf-sa-eye,
    .ig-search-tool:hover .advf-sa-pupil {
        animation: none;
    }
}

/* File filter icon (icon library "fileFilter"). On hover the sheet lifts out of the folder,
   with the same timing as the filter slider (deliverableAdvancedFilter.css). */
.dmf-file-filter-svg {
    width: 16px;
    height: 16px;
    display: block;
    overflow: visible;
}

.dmf-file-filter-folder,
.dmf-file-filter-sheet {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

@keyframes dmfFileFilterSheetLift {

    0%,
    100% {
        transform: translateY(0);
    }

    50% {
        transform: translateY(-1.6px);
    }
}

.ig-search-tool:hover .dmf-file-filter-sheet {
    animation: dmfFileFilterSheetLift 1.6s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

@media (prefers-reduced-motion: reduce) {
    .ig-search-tool:hover .dmf-file-filter-sheet {
        animation: none;
    }
}
/* Tree expand icon (icon library "treeExpand"). At rest it shows the tree as it is now; hover plays the click.
   ig-tree-expand-will-collapse on the button (dmMyFilesTab.js) means the next click collapses. */
.ig-tree-expand-svg {
    width: 16px;
    height: 16px;
    display: block;
    overflow: visible;
}

.ig-tree-expand-svg path {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.45;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.ig-tree-expand-spine {
    transform-box: view-box;
    transform-origin: 4.5px 3px;
    transform: scaleY(0.667);
}

.ig-tree-expand-row-first {
    transform: translateY(-1.5px);
}

.ig-tree-expand-row-second {
    transform: translateY(-3px);
}

.ig-tree-expand-will-collapse .ig-tree-expand-spine,
.ig-tree-expand-will-collapse .ig-tree-expand-row-first,
.ig-tree-expand-will-collapse .ig-tree-expand-row-second {
    transform: none;
}

@keyframes igTreeExpandUnfold {
    50% {
        transform: none;
    }
}

@keyframes igTreeExpandFoldSpine {
    50% {
        transform: scaleY(0.667);
    }
}

@keyframes igTreeExpandFoldRowFirst {
    50% {
        transform: translateY(-1.5px);
    }
}

@keyframes igTreeExpandFoldRowSecond {
    50% {
        transform: translateY(-3px);
    }
}

.ig-search-tool:hover .ig-tree-expand-row-first {
    animation: igTreeExpandUnfold 1.6s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

.ig-search-tool:hover .ig-tree-expand-row-second,
.ig-search-tool:hover .ig-tree-expand-spine {
    animation: igTreeExpandUnfold 1.6s cubic-bezier(0.65, 0, 0.35, 1) 0.12s infinite;
}

.ig-search-tool.ig-tree-expand-will-collapse:hover .ig-tree-expand-row-first {
    animation-name: igTreeExpandFoldRowFirst;
}

.ig-search-tool.ig-tree-expand-will-collapse:hover .ig-tree-expand-row-second {
    animation-name: igTreeExpandFoldRowSecond;
}

.ig-search-tool.ig-tree-expand-will-collapse:hover .ig-tree-expand-spine {
    animation-name: igTreeExpandFoldSpine;
}

@media (prefers-reduced-motion: reduce) {

    .ig-search-tool:hover .ig-tree-expand-spine,
    .ig-search-tool:hover .ig-tree-expand-row-first,
    .ig-search-tool:hover .ig-tree-expand-row-second {
        animation: none;
    }
}

/* One pill under a table tool, opened on right-click (openIgTableToolFlyout). The pill is .ig-mode-btn; no panel around it. */
.ig-tool-menu.ig-tool-flyout {
    padding-bottom: 0;
    background: transparent;
    background-image: none;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    overflow: visible;
}

/* Compact: the flyout is the tool button's height, so the pill drops .ig-mode-btn's uppercase toolbar type. */
.ig-tool-flyout .ig-tool-flyout-option {
    height: 100%;
    padding: 0 10px;
    font-size: 12px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    line-height: 1;
}

/* Amber = the eye's warn state. Hover selectors outrank .ig-mode-btn:hover:not(.ig-mode-on). */
.ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-warn,
.ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-warn:hover {
    border-color: #d99a2b;
    background: #fdf4e3;
    color: #a4700f;
}

body.dark-mode .ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-warn,
body.dark-mode .ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-warn:hover {
    border-color: #e0a83c;
    background: rgba(224, 168, 60, 0.14);
    color: #f0c473;
}

.ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-locked,
.ig-tool-flyout .ig-tool-flyout-option.ig-tool-flyout-locked:hover {
    opacity: 0.45;
    cursor: default;
    transform: none;
    background: transparent;
}
/* ── Table tool popups (Sort, Columns). Same glass as the Panels menu and Gantt popovers. */
@keyframes igToolMenuIn {
    from {
        opacity: 0;
        transform: scale(0.92) translateY(-4px);
    }

    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

.ig-tool-menu {
    position: fixed;
    z-index: 5900;
    padding-bottom: 10px;
    background-color: transparent;
    background-image: var(--igd-pop-bg);
    backdrop-filter: blur(14px) saturate(1.8);
    -webkit-backdrop-filter: blur(14px) saturate(1.8);
    border: 1px solid var(--igd-pop-border);
    border-radius: var(--igd-radius);
    box-shadow: var(--igd-shadow);
    overflow: clip;
    isolation: isolate;
    transform-origin: top left;
    animation: igToolMenuIn 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
}

body:not(.dark-mode) .ig-tool-menu {
    border-color: rgba(31, 45, 61, 0.18);
}

.ig-tool-menu-head {
    display: flex;
    align-items: center;
    padding: 10px 12px 9px 14px;
    background: var(--igd-header-bg);
    border-bottom: 1px solid var(--igd-hairline);
    color: var(--igd-ink-strong);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.ig-tool-menu-close,
.ig-sort-level-delete {
    margin-left: auto;
    border: none;
    background: transparent;
    color: var(--igd-ink);
    opacity: 0.6;
    font-size: 12px;
    line-height: 1;
    padding: 3px 5px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.12s, opacity 0.12s;
}

.ig-tool-menu-close:hover,
.ig-sort-level-delete:hover {
    background: rgba(96, 125, 149, 0.12);
    opacity: 1;
}

.ig-tool-menu-foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 10px;
    padding: 10px 12px 0;
    border-top: 1px solid var(--igd-hairline);
}

/* Same values as .gantt-search-field-cb (ganntChart.css), which is not loaded on every page. */
.ig-tool-check {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    margin: 0;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background: #ffffff;
    border: 1px solid rgba(96, 125, 149, 0.45);
    border-radius: 4px;
    position: relative;
    transition: background 0.12s ease, border-color 0.12s ease;
}

.ig-tool-check:hover {
    border-color: #4b8fef;
}

.ig-tool-check:checked {
    background: #4b8fef;
    border-color: #4b8fef;
}

.ig-tool-check:checked::after {
    content: "";
    position: absolute;
    left: 4px;
    top: 1px;
    width: 4px;
    height: 8px;
    border: solid #ffffff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}

.ig-tool-check:disabled {
    opacity: 0.5;
    cursor: default;
}

.ig-tool-check:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(75, 143, 239, 0.25);
}

/* ── Sort popup (openIgTableSortMenu) */
.ig-sort-menu {
    width: 360px;
}

.ig-sort-menu-levels {
    padding: 10px 12px 4px;
}

.ig-sort-level {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 6px;
}

.ig-sort-level-number {
    flex: 0 0 12px;
    color: var(--igd-ink);
    opacity: 0.6;
    font-size: 12px;
    font-weight: 600;
    text-align: center;
}

.ig-sort-level-column {
    flex: 1 1 auto;
    min-width: 0;
    height: 24px;
    padding: 0 6px;
    border: 1px solid var(--igd-input-border);
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.92);
    color: var(--igd-ink-strong);
    font-size: 12px;
}

.ig-sort-level-column:focus {
    outline: none;
    border-color: #4b8fef;
    box-shadow: 0 0 0 2px rgba(75, 143, 239, 0.15);
}

.ig-sort-level .ig-sort-level-direction {
    margin-bottom: 0;
    gap: 4px;
}

.ig-sort-level-direction .ig-mode-btn {
    height: 24px;
    padding: 0 8px;
    font-size: 10px;
}

.ig-sort-level-delete {
    margin-left: 0;
}

.ig-sort-menu-add {
    display: block;
    width: calc(100% - 24px);
    margin: 2px 12px 0;
    padding: 4px 10px;
    border: 1px dashed var(--igd-input-border);
    border-radius: 6px;
    background: transparent;
    color: var(--igd-ink);
    font-size: 12px;
    cursor: pointer;
    transition: border-color 0.12s, color 0.12s;
}

.ig-sort-menu-add:hover {
    border-color: #4b8fef;
    color: #3574c4;
}

.ig-sort-menu-add:disabled {
    opacity: 0.45;
    cursor: default;
}

/* ── Columns popup (openIgTableColumnsMenu) */
.ig-columns-menu {
    width: 300px;
}

.ig-columns-menu-hint {
    padding: 8px 14px 2px;
    font-size: 11px;
    color: #868e96;
}

.ig-columns-menu-list {
    max-height: 360px;
    overflow-y: auto;
    padding-top: 4px;
}

.ig-columns-menu-row {
    display: grid;
    grid-template-columns: 1fr 48px 52px;
    align-items: center;
    padding: 6px 14px;
    font-size: 13px;
    color: var(--igd-ink-strong);
    transition: background 0.12s;
}

.ig-columns-menu-row:hover {
    background: rgba(96, 125, 149, 0.10);
}

.ig-columns-menu-row .ig-tool-check,
.ig-columns-menu-row-head span + span {
    justify-self: center;
}

.ig-columns-menu-row-head {
    padding-top: 4px;
    padding-bottom: 4px;
    color: var(--igd-ink);
    opacity: 0.7;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.ig-columns-menu-row-head:hover {
    background: transparent;
}

.ig-columns-menu-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ig-columns-menu.ig-columns-menu-can-move {
    width: 340px;
}

.ig-columns-menu.ig-columns-menu-can-move .ig-columns-menu-row {
    grid-template-columns: 44px 1fr 48px 52px;
}

.ig-columns-menu.ig-columns-menu-can-move .ig-columns-menu-row-head span:nth-child(2) {
    justify-self: start;
}

.ig-columns-menu-move {
    display: inline-flex;
    gap: 2px;
}

.ig-columns-menu-move-button {
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 4px;
    background: transparent;
    color: var(--igd-ink);
    opacity: 0.6;
    font-size: 10px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, opacity 0.12s;
}

.ig-columns-menu-move-button:hover:not(:disabled) {
    background: rgba(96, 125, 149, 0.12);
    opacity: 1;
}

.ig-columns-menu-move-button:disabled {
    opacity: 0.2;
    cursor: default;
}

/* Widths row: only on tables with columnResize. The button shows once a width has been set. */
.ig-columns-menu-widths {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 36px;
    margin-top: 6px;
    padding: 6px 10px 6px 14px;
    border-top: 1px solid var(--igd-hairline);
}

.ig-columns-menu-widths-label {
    color: var(--igd-ink);
    opacity: 0.7;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

.ig-columns-menu-widths-state {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 11px;
    color: #868e96;
}

.ig-columns-menu-widths-adjusted .ig-columns-menu-widths-state {
    font-size: 12px;
    font-weight: 400;
    color: var(--ig-btn-success-ink);
}

.ig-columns-menu-widths.ig-columns-menu-widths-off {
    display: none;
}

/* Same compact pill as .ig-tool-flyout-option (the eye's right-click). Two classes so it outranks .ig-mode-btn's display. */
.ig-columns-menu-widths .ig-columns-menu-widths-reset {
    display: none;
    flex: 0 0 auto;
    height: 26px;
    padding: 0 10px;
    font-size: 12px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
    line-height: 1;
}

.ig-columns-menu-widths-adjusted .ig-columns-menu-widths-reset {
    display: inline-flex;
}

.ig-tool-menu-unsaved {
    margin-right: auto;
    align-self: center;
    font-size: 11px;
    color: #868e96;
}

/* ── Date range row: label, two date pickers and Refresh, sized to their content rather than
   a share of the page, so they stay compact on wide screens. Anything in -end sits on the right. */
.ig-date-range-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    margin-bottom: 10px;
}

.ig-date-range-label {
    margin: 0;
    white-space: nowrap;
}

.ig-date-range-bar .ig-date-range-input {
    flex: 0 0 170px;
    width: 170px;
}

.ig-date-range-bar-end {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
    white-space: nowrap;
}

/* .btn {
    min-height: 34px !important;
    min-width: 34px !important;
    margin-right: 1px !important;
}

.btn:hover {
    background-color: #cdcdcd !important;
}

.btn-toolbar {
    background-color: #fafafa;
    border: 1px solid #cccccc;
    border-radius: 4px 4px 4px 4px !important;
    margin-left: 2px;
    padding: 6px
}

.btn-toolbar-slim {
    padding: 0px
}

.btn-spacer {
    all: unset;
    display: inline-block;
    width: 15px;
    pointer-events: none;
} */

.btn-toolbar {
    width: fit-content !important;
    flex-shrink: 0 !important;
}


/* .btn-toolbar background is owned by igniteButtons.css §1 (transparent
   in both modes) — do not set a fill here. */

.document-manager {
    font-size: 15pt;
    color: #777777;
}

.modal-lg {
    max-width: 90% !important;
    width: 60% !important;
}

.modal-lg90 {
    max-width: 90% !important;
    width: 90% !important;
}

.modal-lg80 {
    max-width: 90% !important;
    width: 80% !important;
}

.modal-lg70 {
    max-width: 90% !important;
    width: 70% !important;
}

.modal-med60 {
    max-width: 60% !important;
    width: 60% !important;
}

.modal-med50 {
    max-width: 60% !important;
    width: 50% !important;
}

.modal-med40 {
    max-width: 60% !important;
    width: 40% !important;
}

.modal-med30 {
    max-width: 60% !important;
    width: 30% !important;
}


.modal-dialog {
    position: static;
    background-size: cover;

}

.modal-header {
    padding: 6px 14px;
    min-height: unset;
}

.modal-header .modal-title {
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    line-height: 1.4;
}

.modal-header .close {
    color: #8fa3b1;
    opacity: 0.8;
    font-size: 18px;
    padding: 4px 8px;
    margin: -4px -8px -4px auto;
}

.modal-header .close {
    padding: 1rem;
    margin: -1rem -1rem -1rem auto;
}

.fixed-dialog-body {
    height: calc(90vh - 160px) !important;
    max-height: calc(90vh - 160px) !important;
    overflow-y: auto;
}

.fixed-dialog-footer {
    /*position: absolute;*/
    bottom: 0px;
    right: 0px;
    width: 100%;
}

html,
body {
    height: 98%;
    background-color: #f0f4f8;
}

/* .page-layout {
    height: calc(100vh - 100px);
    display: flex;
  
    flex-direction: row;
  
    align-items: flex-start;
  
    gap: 0;
  
}

.tab-pane {
    height: 100%;
} */

.page-layout {
    min-height: calc(100vh - 100px);
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 0;
}

.tab-pane {
    min-height: 100%;
}



.h4-titlebar {
    margin-bottom: 0px;
    padding: 0px;

}

/* ─── Nav Tabs — Rounded Square Outline Style ───────────────────────────── */

/* Nav container — wrap with position:relative so JS scroll buttons can anchor to it */
.panel-heading {
    padding: 0;
    background: #ffffff !important;
    border-bottom: none !important;
    /* 12px to MATCH the .panel card radius (Module 5). The old 8px
       left a darker wedge peeking out at the panel's rounded top
       corners — the "weird shadow" at the corner of the tab strip. */
    border-radius: 12px 12px 0 0;
    box-shadow: none !important;
    position: relative !important;
}

/* Tab scroll wrapper injected by JS — flex row: [<] [nav] [>] */
.tab-nav-scroll-wrap {
    display: flex;
    align-items: stretch;
    width: 100%;
    position: relative;
    overflow: visible;
}

.tab-nav-scroll-wrap>ul.nav.nav-tabs {
    flex: 1 1 auto;
    min-width: 0;
    /* lets the nav shrink & scroll instead of blowing out at zoom */
}

ul.nav.nav-tabs {
    border-bottom: none !important;
    background: transparent !important;
    padding: 8px 4px 0 4px !important;
    margin-bottom: 0 !important;
    /* .tab-bg-info.panel-heading .nav (style.css:4524) still ships a 15px
       left margin that indents the strip past the panel gutter and past
       .ig-segwrap. Declared here so ignite.css owns the whole gutter and
       the legacy rule can't reintroduce it. */
    margin-left: 0 !important;
    display: flex !important;
    flex-wrap: nowrap !important;
    align-items: flex-end !important;
    overflow-x: auto !important;
    overflow-y: hidden !important;
    /* hide scrollbar cross-browser */
    scrollbar-width: none !important;
    -ms-overflow-style: none !important;
    gap: 4px !important;
    /* NOTE: no scroll-behavior:smooth here — CSS smooth on a container that
       JS drives via scrollLeft makes Chrome restart the animation on every
       wheel tick (feels sluggish). Smooth is applied per-call in JS
       (arrow buttons) via scrollTo({behavior:'smooth'}) instead. */
}

ul.nav.nav-tabs::-webkit-scrollbar {
    display: none !important;
    width: 0 !important;
    height: 0 !important;
}

ul.nav.nav-tabs li {
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
    background: transparent !important;
    margin-bottom: 0 !important;
    float: none !important;
    flex-shrink: 0 !important;
    list-style: none !important;
    position: relative !important;
}

ul.nav.nav-tabs li a,
ul.nav.nav-tabs a {
    outline: none !important;
    /* Literally the same surface as .ig-segmented, and flat so it can
       actually animate to the white active fill. */
    background: var(--igd-wash) !important;
    color: var(--igd-ink) !important;
    border: 1.5px solid var(--igd-hairline) !important;
    border-bottom: 1.5px solid var(--igd-hairline) !important;
    /* TWO layers, always, in this order: hairline crown, then recess blur.
       A state with a different layer count cannot interpolate — CSS snaps
       the whole shadow instead. The second layer is transparent here. */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9),
        inset 0 3px 4px -2px rgba(31, 45, 61, 0) !important;
    border-radius: 7px 7px 0 0 !important;
    /* Only padding-top varies between states. The ul is align-items:
       flex-end (ignite.css:448), so a shorter tab keeps its bottom edge
       on the seam and drops its top edge instead. padding-bottom is
       fixed, so the label never moves.
       7px is the row's ceiling and never changes, so ul.nav.nav-tabs
       keeps a constant height and nothing below it shifts on select. */
    padding: 7px 15px 8px 15px !important;
    font-size: 11.5px !important;
    font-weight: 500 !important;
    letter-spacing: 0.02em !important;
    white-space: nowrap !important;
    line-height: 1.4 !important;
    margin: 0 !important;
    display: block !important;
    position: relative !important;
    margin-bottom: -1px !important;
    /* One duration, one curve, all five properties — a split duration
       reads as lag, not motion. Not the .ig-seg-cap curve: that long
       decelerate is built for the cap's ~60px slide and creeps over 5px.
       background, not background-color: the wash is set via background. */
    transition:
        padding-top 200ms cubic-bezier(.4, 0, .2, 1),
        background 200ms cubic-bezier(.4, 0, .2, 1),
        color 200ms cubic-bezier(.4, 0, .2, 1),
        border-color 200ms cubic-bezier(.4, 0, .2, 1),
        box-shadow 200ms cubic-bezier(.4, 0, .2, 1) !important;
}

ul.nav.nav-tabs li a:focus,
ul.nav.nav-tabs a:focus {
    outline: none !important;
    box-shadow: var(--igd-input-focus) !important;
}

/* Hover — lift, crisp outline, NO glow (the upward blur read as a
   smudgy "shadow behind the tabs" against the heading strip) */
ul.nav.nav-tabs li:not(.active) a:hover {
    padding-top: 5px !important;
    background: #ffffff !important;
    color: var(--igd-accent) !important;
    border-color: var(--igd-hairline) !important;
    border-bottom-color: var(--igd-hairline) !important;
    border-radius: 7px 7px 0 0 !important;
    /* Half the active inset — the tab is on its way down. Layer 1 is
       already a faint accent so the bar grows in rather than snapping. */
    box-shadow: inset 0 1px 0 rgba(59, 130, 196, 0.35),
        inset 0 4px 5px -2px rgba(31, 45, 61, 0.06) !important;
}

/* Active — filled card with blue top stripe, merges into content.
   The old 0 -3px 12px upward blur is gone: it painted a permanent
   grey-blue smudge above/behind the tab row. */
ul.nav.nav-tabs li.active a,
ul.nav.nav-tabs li.active>a,
ul.nav.nav-tabs li.active a:hover,
ul.nav.nav-tabs li.active a:focus {
    /* Same fill as .ig-seg-cap. border-bottom-color must stay equal to
       the strip background — that is what erases the seam into the
       content pane, so it cannot be a token. */
    /* Selected recedes rather than rises: it drops below the idle
       ceiling, so the row height is fixed by the unselected tabs. */
    padding-top: 5px !important;
    background: #ffffff !important;
    color: var(--igd-accent) !important;
    border-color: var(--igd-hairline) !important;
    border-bottom-color: #ffffff !important;
    border-radius: 7px 7px 0 0 !important;
    font-weight: 500 !important;
    /* Layer 1 is the accent bar, layer 2 the recess. Inset, so the bar
       follows the rounded top corners instead of running square across
       them like the old box-shadow stripe did, and no upward outer blur
       (ignite.css:524). The crown layer runs white -> faint accent ->
       accent across the three states and must stay in position 1 or the
       shadow stops interpolating. */
    box-shadow: inset 0 2px 0 var(--igd-accent),
        inset 0 4px 5px -2px rgba(31, 45, 61, 0.13) !important;
    z-index: 2 !important;
}

/* Holds the row at an idle tab's height. Active and hovered tabs are 2px
   shorter, so on a two-tab strip (one active, one hovered) the row would
   otherwise shrink and everything below it would jump. */
ul.nav.nav-tabs::after {
    content: "\00a0" !important;
    display: block !important;
    flex: 0 0 auto !important;
    width: 0 !important;
    overflow: hidden !important;
    white-space: nowrap !important;
    margin-left: -4px !important;
    padding: 7px 0 8px 0 !important;
    border-top: 1.5px solid transparent !important;
    border-bottom: 1.5px solid transparent !important;
    font-size: 11.5px !important;
    line-height: 1.4 !important;
    visibility: hidden !important;
}
/* ─── Project / Setup segmented control (project-edit.html) ─────────────
   Own classes, not .ig-modebar: that strip is shared by the Services, HR
   Requirements, Deliverable Roles and SD dialog toggles, which still want
   the raised pill. Gutter is 4px to match ul.nav.nav-tabs padding-left
   above, so the track lines up with the first tab. */
/* No top margin: .panel-body already contributes Bootstrap's 15px above
   this, which is all the breathing room the control needs.
   The 12px below is not 12px of gap. .tab-bg-info.panel-heading .nav
   (style.css:4524) pulls the tab row up by 10px, so 12 + the nav's own
   8px padding-top - that 10px leaves the 10px the eye actually sees. */
.ig-segwrap {
    display: flex;
    align-items: center;
    margin: 0 0 8px 0;
    /* 8px = the ul's own 4px padding-left (ignite.css:444) plus the 4px
       flex gap (ignite.css:454) it pays to the zero-width
       .tab-scroll-btn that always sits before the first tab. Measured:
       the wrapper and the ul both start at 15, the first tab at 23. */
    padding-left: 8px;
}

/* Page title on the tab-group row (project-edit.html) instead of its own line
   above the panel. Bottom margin matches .ig-segwrap so both centre on one line.
   Colour comes from the global h4 rules, so dark mode is unchanged. */
.ig-header-title {
    flex: 1 1 auto;
    min-width: 0;
    align-self: center;
    margin: 0 0 12px 16px;
    font-size: 14px;
    font-weight: 400;
    color: #797979;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ig-segmented {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--igd-hairline);
    border-radius: 9px;
    background: var(--igd-wash);
    box-shadow: none;
}

/* Absolutely positioned, so it sits outside the flex run and the gap
   between the two labels is unaffected by it. */
.ig-seg-cap {
    position: absolute;
    top: 2px;
    bottom: 2px;
    left: 0;
    width: 0;
    border-radius: 6px;
    background: #ffffff;
    box-shadow: 0 1px 2px rgba(31, 45, 61, 0.12),
        0 1px 1px rgba(31, 45, 61, 0.04);
    transform: translateX(0);
    transition: transform 420ms cubic-bezier(.32, .72, 0, 1),
        width 420ms cubic-bezier(.32, .72, 0, 1);
    pointer-events: none;
}

.ig-seg-btn {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    padding: 5px 14px;
    border: none;
    border-radius: 6px;
    background: transparent;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.02em;
    line-height: 1.4;
    white-space: nowrap;
    color: var(--igd-ink);
    opacity: 0.65;
    cursor: pointer;
    outline: none;
    transition: color 260ms cubic-bezier(.32, .72, 0, 1),
        opacity 260ms cubic-bezier(.32, .72, 0, 1);
}

.ig-seg-btn:hover {
    opacity: 1;
}

.ig-seg-btn.ig-seg-on {
    color: var(--igd-ink-strong);
    opacity: 1;
    font-weight: 600;
}

.ig-seg-btn:focus-visible {
    box-shadow: var(--igd-input-focus);
}

@media (prefers-reduced-motion: reduce) {

    .ig-seg-cap,
    .ig-seg-btn {
        transition: none;
    }
}

/* ─── Selected / hover marker ───────────────────────────────────────────
   Direct children only: ul.nav.nav-tabs a also matches dropdown menu
   items (ignite.css:2341), which must not get a bar.
   Inset 12px each side so it reads as a mark placed on the tab rather
   than as a thicker top border. Same curve as .ig-seg-cap. */
@media (prefers-reduced-motion: reduce) {

    ul.nav.nav-tabs li a,
    ul.nav.nav-tabs a {
        transition: none !important;
    }
}

/* No hairline after the nav row */
.panel-heading::after {
    display: none !important;
}

/* Tab scroll arrow buttons (injected by JS) — inline flex siblings, never overlay */
.tab-scroll-btn {
    flex: 0 0 auto;
    width: 0 !important;
    align-self: stretch;
    display: none !important;
    align-items: flex-end;
    justify-content: center;
    padding: 0 0 8px 0 !important;
    margin: 0 !important;
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
    /* These are real <button> elements. Without neutralizing
       `appearance`, the browser paints its own native button
       face/accent-color UNDER our background — that's the stray
       lavender/purple wash behind the arrow. `appearance: none`
       removes that native chrome so only our flat #f8fafc paints. */
    appearance: none !important;
    -webkit-appearance: none !important;
    -moz-appearance: none !important;
    background: #ffffff !important;
    color: #94a3b8 !important;
    font-size: 13px;
    cursor: pointer;
    overflow: hidden;
    transition: color 0.15s ease;
}

.tab-scroll-btn.visible {
    display: flex !important;
    width: 26px !important;
}

.tab-scroll-btn:hover {
    color: #2563eb !important;
}

/* Tab bar bottom accent */
.panel-heading+.tab-content,
.panel-heading+.panel-body {
    box-shadow: none;
}

/* ─────────────────────────────────────────────────────────────────────────── */

.panel {
    background-color: transparent !important;
}

/* .panel-heading visual appearance handled by nav tab block above */
.panel-heading {
    padding: 0 0 0 0;
}

.panel-body {
    background-color: #ffffff;
    border: 1px solid #e4e4e8;
    border-radius: 8px;
    width: 100%;
}

.dataTables_wrapper {
    /* background: linear-gradient(160deg,
        rgba(240, 247, 255, 0.95) 0%,
        rgba(255, 255, 255, 0.88) 40%,
        rgba(245, 250, 255, 0.92) 100%); */
    border-radius: 8px;
    border: 1px solid var(--ig-border-light, #dee2e6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
    /* backdrop-filter REMOVED — it created a containing block + stacking
       context for the whole wrapper, which trapped any position:fixed
       descendant (dialogs, datepickers, select dropdowns) inside the
       tab instead of letting them sit at z-index 1050 over the page.
       The background it was meant to frost is commented out anyway. */
    /* overflow: hidden; */
    overflow: visible;
    padding: 16px 18px 12px 18px;
}

/* ════════════════════════════════════════════════════════════════
   View card (.ig-view-card)
   A stable frame for a panel whose CONTENTS swap. Material lifted from
   .dataTables_wrapper above so a table/tree switch keeps the look the
   user already knows. No padding here: the DataTables wrapper carries
   its own, and the toolbar wash is sized to that element.
   ════════════════════════════════════════════════════════════════ */
.ig-view-card {
    border-radius: 8px;
    border: 1px solid var(--ig-border-light, #dee2e6);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
    background-color: transparent;
    overflow: visible;
}

/* The card is the box; the wrapper inside it is layout only. Same move
   .scd-panel makes further down. !important and the dark-mode and
   modal-body selectors are all required, because darkMode.css declares
   the wrapper border, radius and padding with !important in both
   contexts. Padding is deliberately not reset: it is what insets the
   table from the card edge. */
.ig-view-card .dataTables_wrapper,
.ig-view-card .table-scrollable .dataTables_wrapper,
body.dark-mode .ig-view-card .dataTables_wrapper,
body.dark-mode .ig-view-card .table-scrollable .dataTables_wrapper,
body.dark-mode .modal-body .ig-view-card .dataTables_wrapper,
body.dark-mode .modal-body .ig-view-card .table-scrollable .dataTables_wrapper {
    border: none !important;
    border-top: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}

/* Non-table content in the card. Matches the wrapper's inset exactly. */
.ig-view-pane {
    padding: 16px 18px 12px 18px;
}

/* Layout wrappers around the document viewer. The card is .file-viewer-container (image-viewer.css). */
.right-panel {
    height: calc(100vh - 100px);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Sticky inside the General tab's ig-viewport-scroll box, and exactly its height. */
.right-panel-general {
    flex-shrink: 0;
    width: 460px;
    min-width: 460px;
    position: sticky;
    top: 0;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

.left-panel-general {
    flex: 0 1 auto;
    box-sizing: border-box;
    padding: 10px;
    overflow: visible;
    height: auto;
}
/* ── Hide empty DataTables scrollHead spacer row ── */
.dataTables_scrollHead table thead tr td:empty,
.dataTables_scrollHead table thead tr th:empty {
    display: none !important;
    height: 0 !important;
    padding: 0 !important;
    border: none !important;
}

.open-panel-body {
    background-color: #ffffff;
    width: 100%;
}

.form-horizontal .form-group {
    margin-bottom: 0px;
    padding-bottom: 6px;
}

.notificationBar {
    display: inline;
}

.pointer {
    cursor: pointer;
}

.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
    cursor: default !important;
    background-color: #e3e3e3 !important;
    border: 0px;
}

div.dataTables_wrapper div.dataTables_processing {
    background-color: var(--ig-panel, #fdfefe);
    color: #333;
    border: 1px solid var(--ig-panel-border, rgba(148, 173, 199, 0.28));
    border-radius: 8px;
    box-shadow: 0 4px 14px rgba(20, 40, 80, 0.10);
    z-index: 5;
}

div.dataTables_wrapper .dataTables_scroll {
    transition: filter 0.15s ease, opacity 0.15s ease;
}

div.dataTables_wrapper.ig-dt-processing .dataTables_scroll {
    filter: blur(2px);
    opacity: 0.55;
    pointer-events: none;
}

hr.divider {
    width: 100%;
    border-top: 1px solid #ced4da !important;
    border-bottom: 0px;
}

.info-success {
    color: #4cd964;
}

.info-danger {
    color: #ff2d55;
}

/* .lite-fade {
    text-transform: none !important;
    opacity: 0.4 !important;
    font-size: 12px;
} */

.lite-fade {
    text-transform: none !important;
    opacity: 0 !important;
    font-size: 12px;
    transform: translateX(-6px);
    transition: opacity 0.4s ease, transform 0.4s ease;
}

#headerLogo a img {
    transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
        opacity 0.4s ease,
        filter 0.4s ease;
    opacity: 0.85;
    filter: brightness(0.9);
}

#headerLogo a:hover img {
    transform: scale(1.08);
    opacity: 1;
    filter: brightness(1.15) drop-shadow(0 0 8px rgba(231, 76, 36, 0.6));
}

#headerLogo a:hover .lite-fade {
    opacity: 0.7 !important;
    transform: translateX(0);
}

.badge {
    margin-left: 4px !important;
}

.badge-right {
    position: absolute;
    right: 6px;
    top: 3px;
    font-size: 75%;
}

.badge-empty {
    height: 10px !important;
    width: 10px !important;
    min-height: 10px !important;
    min-width: 10px !important;

    margin-left: 4px !important;
}

.badge-right {
    float: right !important;
    position: absolute;
    right: 10px;
}

/* *************************************************************************************** */
/* Toast                                                                                   */
/* *************************************************************************************** */

.toast {
    position: fixed;
    top: 70px;
    right: 26px;

    font-size: 9pt;

    min-height: 80px;
    min-width: 250px;

    background-color: #fefefe;
    border: 1px solid #ddd;
    border-radius: 4px;

    -moz-box-shadow: 4px 4px 10px 1px #cccccc;
    -webkit-box-shadow: 4px 4px 10px 1px #cccccc;
    box-shadow: 4px 4px 10px 1px #cccccc;
}

.toast-header {
    padding: 0px 14px 0px 14px;
    font-weight: bold;
    color: #6c757d;
    border-bottom: 1px solid #e9ecef;
}

.toast-header .close {
    padding: 1rem;
    margin: -1rem -1rem -1rem auto;
}

.toast-title {
    margin-bottom: 0;
    line-height: 2.5;
}

.toast-body {
    color: #6c757d;
    padding: 14px;
}


/* *************************************************************************************** */
/* Calendar                                                                                   */
/* *************************************************************************************** */

div.publicHolidayBackground {
    position: relative;
    width: 100%;
    height: 100%;
}

div.publicHolidayBackground:before {
    position: absolute;
    font-family: 'Font Awesome 5 Free';
    font-size: 26px;
    font-weight: 100;
    color: rgb(104, 138, 126, 0.4);
    bottom: 44px;
    left: 24px;
    content: "\f133";
}


.fixed-height-panel-245 {
    height: 245px !important;
    overflow-y: auto;
    /* Optional: Add scroll if content overflows */
}


/* Put this in your stylesheet or inside a <style> tag */
.large-icon {
    font-size: 1.6rem;
    line-height: 1;
}

.med-icon {
    font-size: 1.3rem;
    line-height: 1;
}

.icon-red {
    color: red;
}

.icon-green {
    color: green;
}


/******************************************************************************/
/*  Johannes uses this in Bank Transaction to display shortened text 	      */
/******************************************************************************/
/* table.dataTable {
    table-layout: fixed;
    width: 100% !important;
} */

.dt-nowrap {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.dt-cell {
    display: inline-block;
    width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}


/******************************************************************************/
/*  Johannes uses this to make sure tables don't have a horizontal scrollbar  */
/******************************************************************************/
.dataTables_scrollBody {
    overflow-x: hidden !important;
    overflow-y: auto !important;
}


#map {
    position: relative;
    display: block;
    width: 100%;
    height: 500px;
    /* or whatever you prefer */
    overflow: hidden;
    /* prevent tile spill */
    z-index: 1;
    /* keep it beneath floating elements if needed */
}

.gray-row,
.gray-row td,
.gray-row td * {
    color: #777 !important;
}

.estimator-name {
    color: darkblue;
}

.gray-row .estimator-name {
    color: #ccc !important;
}

.selected-footer {
    background-color: #f0f8ff !important;
    /* Light blue */
    font-weight: bold;
}


#sectionDetailsWrapper {
    overflow-x: auto;
    max-width: 100%;
}


#sectionDetailsByTendererTable {
    width: max-content !important;
    table-layout: fixed !important;
}

table#sectionDetailsByTendererTable {
    width: max-content !important;
    display: block;
    /* Optional: forces table to respect width */
}

#sectionDetailsByTendererTable td,
#sectionDetailsByTendererTable th {
    white-space: nowrap;
}


#theSoqDetailsByTendererTable {
    width: max-content !important;
    table-layout: fixed !important;
}

table#theSoqDetailsByTendererTable {
    width: max-content !important;
    display: block;
    /* Optional: forces table to respect width */
}

#theSoqDetailsByTendererTable td,
#theSoqDetailsByTendererTable th {
    white-space: nowrap;
}


#soqItemEvaluationTable td,
#soqItemEvaluationTable th {
    white-space: nowrap;
}

#soqItemEvalPanel {
    overflow-x: auto;
    max-width: 100%;
}


#soqItemEvaluationTable.dataTable {
    table-layout: auto !important;
    width: auto !important;
    min-width: max-content;
    /* forces growth beyond parent */
}


/* Applies to every cell in the row */
.highlight-border td {
    border-top: 4px solid #000 !important;
    border-bottom: 4px solid #000 !important;
}


/* *************************************************************************************** */
/*  Ingrid het hierdie bygesit vir d3                                                      */
/* *************************************************************************************** */

path {
    fill: none;
    stroke: black;
}

.node circle {
    fill: #999;
}

.node text {
    font: 10px sans-serif;
}

.node--internal circle {
    fill: #555;
}

.node--internal text {
    text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}

.link {
    fill: black;
    stroke: black;
    stroke-opacity: 0.4;
    stroke-width: 1.5px;
}

.disabled {
    pointer-events: none;
}

.input-group-append {
    display: table-cell !important;
}

.input-group-text {
    border-top-right-radius: 4px !important;
    border-bottom-right-radius: 4px !important;
    border-left-width: 0px !important;
}

.thin-row {
    padding-top: 0px;
    padding-bottom: 0px;
}

.menuLink {
    height: 100%;
}

.menuSpan {
    height: 100%;
    vertical-align: top;
}

.menuIcon {
    padding-top: 3px;
}

.disable-panel {
    pointer-events: none;
    opacity: 0.6;
    /* Optional: to give a disabled look */
}

/* Ingrid added this - mandatory label  */
.form-label-mandatory {
    color: rgb(71, 71, 71) !important;
    font-weight: 700 !important;
}

/* Ingrid added this: mandatory text field */
.form-control-mandatory {
    display: block;
    width: 100%;
    height: calc(1.5em + 0.75rem + 2px);
    padding: 0.375rem 0.75rem;
    font-size: 14px;
    font-weight: 400;
    line-height: 1.5;
    color: inherit;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid rgb(71, 71, 71) !important;
    border-radius: 0.25rem;
    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

.form-control-mandatory:disabled,
.form-control-mandatory[readonly] {
    background-color: #e9ecef;
    opacity: 1;
}

.help-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 38px;
    /* Match input height for perfect alignment */
    margin-left: 8px;
    color: #6c757d;
    cursor: help;
    font-size: 20px;
    transition: color 0.2s ease;
    vertical-align: top;
    /* Align with input */
}

.help-icon:hover {
    color: #007bff;
}

.help-icon i {
    pointer-events: none;
}

.help-icon::after {
    content: attr(tooltip);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background-color: #333;
    color: #ffffff;
    padding: 10px 12px;
    border-radius: 6px;
    font-size: 13px;
    line-height: 1.4;
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
    z-index: 10000;
    pointer-events: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
    max-width: 300px;
    white-space: normal;
    width: max-content;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.help-icon::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    /* border-top-color: #333; */
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s ease;
    z-index: 10000;
    pointer-events: none;
}

.help-icon:hover {
    color: #007bff;
}


.help-icon:hover::after,
.help-icon:hover::before {
    opacity: 1;
    visibility: visible;
}

.readonly-checkbox {
    pointer-events: none;
}


.disabled-table {
    /* Ingrid added this: For all the ellipsis table: Disable the table until reload has finished*/
    pointer-events: none;
    opacity: 0.5;
    /* Optional: adds a greyed-out visual effect */
    cursor: not-allowed;
    /* Optional: changes the cursor to indicate no interaction */
}

.readonly-checkbox {
    pointer-events: none;
}


/* ================================================= */
/* ==================SideScroll / Panning=========== */
/* ================================================= */

/* Cayden added this for nav side scrolling — flex/scroll now handled in main nav-tabs block above */
.panel-heading.tab-bg-info .nav-tabs {
    overflow-x: auto;
    overflow-y: hidden;
}

.panel-heading.tab-bg-info .nav-tabs>li {
    flex-shrink: 0;
}

.panel-heading.tab-bg-info .nav-tabs::-webkit-scrollbar {
    height: 2px;
}

.panel-heading.tab-bg-info .nav-tabs::-webkit-scrollbar-thumb {
    background: #d1d5db;
    border-radius: 2px;
}

.panel-heading.tab-bg-info .nav-tabs::-webkit-scrollbar-track {
    background: transparent;
}

.tab-content {
    overflow: visible;
    overflow-x: auto;
    overflow-y: visible;
    scrollbar-width: thin;
    /* was: none — show the thin side-scroll bar */
}

.tab-content::-webkit-scrollbar {
    display: none;
}

.tab-content::-webkit-scrollbar {
    display: none;
    /* Chrome/Safari */
}

/* .tab-content {
    overflow: visible;
} */

.tab-content::-webkit-scrollbar {
    display: none;
}


.tab-content.is-grabbing {
    cursor: grabbing;
    user-select: none;
}

/* .tab-content > .tab-pane {
    min-width: unset;
} */


/* Pannable: the element itself is the horizontal scroll container.
   NOTE: no min-width:max-content — that made scrollWidth == clientWidth
   (drag could never scroll it) and caused a compounding width feedback
   loop on browser zoom. Content width comes from the col-min-* /
   .pannable > .row > .col-md-* min-widths inside it. */
.pannable {
    overflow-x: auto;
    max-width: 100%;
    min-width: 0;
    /* lets it shrink inside flex rows and scroll instead of blowing out */
    cursor: grab;
}

.pannable.is-grabbing {
    cursor: grabbing;
    user-select: none;
}


.panel-body>.row {
    display: flex;
    flex-wrap: nowrap;
}

.panel-body>.row>[class*="col-"] .pannable>[class*="col-"] {
    min-width: 1200px;
    flex-shrink: 0;
}

/* ================================================= */
/* ============ SideScroll / Panning Dialog ========= */
/* ================================================= */

/* Cayden added this for panning/ side scroll in a dialog */
.modal-lg90 {
    width: 90%;
    max-width: 90%;
}

.modal-lg90 .modal-content {
    /* overflow-x: auto; */
    overflow: hidden;
}

.modal-lg90 .modal-body {
    max-height: 80vh;
}

/* salariesDialog is modal-lg70, so it misses the .modal-lg90 height cap above.
   Same recipe, id-scoped: body scrolls internally, header + footer stay fixed. */
#salariesDialog .modal-content {
    overflow: hidden;
}

#salariesDialog .modal-body {
    max-height: 80vh;
}

/*Adds scroll to left side and "universal scroll"*/

/* #productModelDialog #pmDialogLayoutRow,
#productModelVariantDialog #pmvDialogLayoutRow {
    height: calc(90vh - 260px);
    margin-bottom: 0;
    padding-bottom: 0;
}

#productModelDialog #pmDialogLeftColumn,
#productModelVariantDialog #pmvDialogLeftColumn {
    height: 100%;
    overflow-y: auto;
}

#productModelDialog #pmDialogLeftColumn .row,
#productModelVariantDialog #pmvDialogLeftColumn .row {
    height: auto;
}

#productModelDialog .right-panel,
#productModelVariantDialog .right-panel {
    height: 100%;
} */

#productModelDialog .modal-body,
#productModelVariantDialog .modal-body {
    display: flex;
    flex-direction: column;
}

#productModelDialog .modal-body > section.panel,
#productModelVariantDialog .modal-body > section.panel {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
    margin-bottom: 0 !important;
}

#productModelDialog .modal-body > section.panel > form,
#productModelVariantDialog .modal-body > section.panel > form,
#productModelDialog .modal-body > section.panel > form > fieldset,
#productModelVariantDialog .modal-body > section.panel > form > fieldset {
    flex: 1 0 auto;
    display: flex;
    flex-direction: column;
}

#productModelDialog #pmDialogLayoutRow,
#productModelVariantDialog #pmvDialogLayoutRow {
    flex: 1 0 auto;
    height: auto;
    margin-bottom: 0;
    padding-bottom: 0;
}

#productModelDialog #pmDialogLeftColumn .row,
#productModelVariantDialog #pmvDialogLeftColumn .row {
    height: auto;
}

#productModelDialog .right-panel,
#productModelVariantDialog .right-panel {
    height: auto;
    overflow: hidden;
}

/* All modal bodies — pannable with scroll */
.modal-body {
    overflow: auto;

    overscroll-behavior: contain;
}

.modal-body.is-grabbing {
    cursor: grabbing;
    user-select: none;
}

/* Inner content keeps natural width */
.modal-body>section,
.modal-body>form,
.modal-body>section>form,
.modal-body>div {
    min-width: unset;
}

.pannable>.row>.col-md-4 {
    min-width: 600px;
}

.pannable>.row>.col-md-5 {
    min-width: 600px;
}

.pannable>.row>.col-md-6 {
    min-width: 800px;
}

.pannable>.row>.col-md-7 {
    min-width: 1000px;
}

.pannable>.row>.col-md-8 {
    min-width: 1200px;
}

.pannable>.row>.col-md-9 {
    min-width: 1400px;
}

.pannable>.row>.col-md-10 {
    min-width: 1600px;
}


/* ================================================= */
/* ================================================= */
/* ============ SideScroll / Panning Dialog ========= */
/* ================================================= */
/* ================================================= */
/* ============ Column Min-Width Utilities ========== */
/* ================================================= */
.col-min-sm {
    min-width: 200px;
    flex-shrink: 0;
}

.col-min-smd {
    min-width: 250px;
    flex-shrink: 0;
}

.col-min-smed {
    min-width: 300px;
    flex-shrink: 0;
}

.col-min-md {
    min-width: 350px;
    flex-shrink: 0;
}

.col-min-lg {
    min-width: 500px;
    flex-shrink: 0;
}

.col-min-xl {
    min-width: 700px;
    flex-shrink: 0;
}

.col-min-xxl {
    min-width: 900px;
    flex-shrink: 0;
}

.col-min-xxxl {
    min-width: 1400px;
    flex-shrink: 0;
}

.col-min-xxxxl {
    min-width: 1800px;
    flex-shrink: 0;
}

.col-min-5xl {
    min-width: 2400px;
    flex-shrink: 0;
}

.col-min-6xl {
    min-width: 4000px;
    flex-shrink: 0;
}

/* ================================================= */
/* ============ Form Row Layout ==================== */
/* ================================================= */


/* ================================================= */
/* ============ Stacked Form Labels ================ */
/* ================================================= */
form .col-form-label,
fieldset .col-form-label,
.form-horizontal .col-form-label {
    font-size: 11px;
    font-weight: 400;
    color: #474b4e;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 4px;
    display: block;
}

.form-label-mandatory::after {
    content: " *";
    color: #e74c3c;
}

label.col-form-label.h4 {
    font-size: 13px;
    font-weight: 500;
    text-transform: uppercase;
    color: #5a6c7c;
    letter-spacing: 0;
}


/* checkBox styling */
/* ================================================= */
/* ============ Toggle Switch Checkboxes =========== */
/* ================================================= */

.ignite-toggle {
    position: relative;
    display: inline-block;
    width: 38px;
    height: 22px;
    flex-shrink: 0;
    margin-bottom: 0;
    cursor: pointer;
    align-self: center;
}

.ignite-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
    position: absolute;
}

.ignite-toggle__slider {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    border-radius: 22px;
    transition: background-color 0.25s ease;
}

.ignite-toggle__slider::before {
    content: "";
    position: absolute;
    height: 16px;
    width: 16px;
    left: 3px;
    bottom: 3px;
    background-color: #ffffff;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    transition: transform 0.25s ease;
}

.ignite-toggle input:checked+.ignite-toggle__slider {
    background-color: #2196F3;
}

.ignite-toggle input:checked+.ignite-toggle__slider::before {
    transform: translateX(16px);
}

.ignite-toggle input:focus-visible+.ignite-toggle__slider {
    outline: 2px solid #2196F3;
    outline-offset: 2px;
}

.ignite-toggle input:disabled+.ignite-toggle__slider {
    background-color: #e9ecef;
    cursor: not-allowed;
    opacity: 0.65;
}

.ignite-toggle input:disabled+.ignite-toggle__slider::before {
    background-color: #f8f9fa;
}

/* Checked + disabled: keep blue so the state is still visible */
.ignite-toggle input:checked:disabled+.ignite-toggle__slider {
    background-color: #2196F3;
    opacity: 0.55;
}

.ignite-toggle input:checked:disabled+.ignite-toggle__slider::before {
    background-color: #ffffff;
}

/* ================================================= */
/* ================ Scrollable Table =============== */
/* ================================================= */

.table-scrollable table.dataTable thead>tr>th {
    white-space: nowrap;
    overflow-wrap: unset;
    hyphens: unset;
}

.table-scrollable table.dataTable {
    table-layout: auto !important;
    width: max-content !important;
    min-width: 100%;
}

.table-scrollable .dataTables_scrollBody {
    overflow-x: auto !important;
    overflow-y: auto !important;
}

/* Applied by igSyncHorizontalScrollbar() when the table fits its container.
   overflow-x:auto cannot be trusted on its own here - max-content sizing plus
   DataTables' fractional inline widths overshoot by a pixel on a table that
   fits, and a pixel of overflow still draws a full scrollbar.
   Three classes deep so it outranks the rule above rather than tying with it. */
.table-scrollable .dataTables_scrollBody.ig-no-hscroll {
    overflow-x: hidden !important;
}

.table-scrollable .dataTables_wrapper {
    overflow-x: auto;
    width: 100%;
}
/* ═══════════════════════════════════════════════════════════════════
   Reference Data — list + mounted document

   ONE sizing system for the row. The earlier version mixed a pixel
   floor on the list column with Bootstrap's rigid flex: 0 0 83.333%
   on the frame column, so the row demanded 250px + 83.333% and went
   wider than the panel on any window under ~1500px. That overflow is
   what produced the page scrollbars and what made the block fit shrink
   the frame trying to recover.
   ═══════════════════════════════════════════════════════════════════ */
.ig-refdata-row {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 12px;
}

/* Fixed, and it must not shrink: below ~190px the DataTables filter
   (float:right at style.css:4396, 160px input at ignite.css:187) is
   wider than its container and a float:right box that overflows spills
   out the LEFT side, over the sidebar. */
.ig-refdata-row > .ig-refdata-list {
    flex: 0 0 250px;
    min-width: 250px;
}

/* Takes the remainder. min-width:0 is load-bearing: a flex item's
   default min-width is auto, which refuses to shrink below its content
   and would reintroduce the overflow this block exists to remove. */
.ig-refdata-row > .ig-refdata-stage-col {
    flex: 1 1 auto;
    min-width: 0;
}

/* The frame sits DIRECTLY in its column, with no card of its own. The
   .panel-body it is already inside is the surface, exactly as a project
   tab's content sits directly in the panel body -- a second bordered box
   around the frame was a card inside a card, and its overflow:hidden was
   one more box in the chain that could reserve a scrollbar gutter.

   display:block kills the inline baseline gap under a replaced element:
   a few px of descender space that reads as a misaligned bottom edge. */
.ig-refdata-stage-col > iframe {
    display: block;
    width: 100%;
    border: none;
    background: transparent;
}

/* ═══════════════════════════════════════════════════════════════════
   DM Reference Data — mounted, not framed

   #dmReferenceDataPage mounts its seven setup screens as tab-panes in
   THIS document. #referenceDataPage still uses the iframe, so every
   rule below is scoped to the DM page id and the block above is left
   alone.

   ONE fitted element, the ROW. igFitBlockToViewport (ignite.js:1768)
   measures belowSpan from the block's own bottom edge to the footer's,
   so two blocks side by side deadlock: the taller one sets the row's
   bottom, which is precisely what the shorter one is told to measure
   against, and growing it changes nothing it can see. Fitting their
   shared parent removes the question, and puts the fitted bottom edge
   exactly where the iframe's was.
   ═══════════════════════════════════════════════════════════════════ */
/* NOTHING on this page scrolls except the grids themselves.

   The stage has no height of its own: every grid on every pane is in a
   table fit set, and _igFitTableSet's shrink and grow loops
   (ignite.js:1594, :1625) size them together so the document ends at the
   fold. A height plus overflow-y here would put a second scroll surface
   around grids that already scroll, which is the one thing this layout
   must not have.

   overflow-x is hidden and deliberately NOT auto: dm-service-discipline
   carries .pannable, which is already the horizontal scroll container
   (ignite.css:1255) and must keep owning that axis -- an auto here would
   make the stage a horizontal scroller too, and ignite.js:4925 routes
   the tilt-wheel to the nearest ancestor that can move sideways. */
#dmReferenceDataPage #dmRefDataStage {
    overflow-x: hidden;
}

/* ───────────────────────────────────────────────────────────────────
   The list FOLLOWS the stage column. It has no height of its own and
   no JS authority over its height.

   It used to be LIST_SPEC in every pane's fit set. That put two writers
   on one max-height: _igFitTableSet on every pane-grid draw
   (ignite.js:2261 -> :1935 -> :1544), and a local align pass on its own
   timers. Whichever ran last won, so the list stepped up, then down,
   then grew a scrollbar. There is no run order that settles two
   disagreeing authorities; the fix is to have one.

   Flex is that one. The stage column's height is whatever the pane
   grids settle at AFTER the shrink and grow loops, and a flex sibling
   tracks it at every intermediate state rather than at the end of a
   timeout. The list can also never be the cause of page overflow now:
   it cannot exceed the column that sets the row height.

   min-height:0 on every link is load-bearing. A flex child defaults to
   min-height:auto and refuses to shrink below its content, which
   silently breaks the chain -- same note as the pane fill chain module
   further down this file.
   ─────────────────────────────────────────────────────────────────── */
#dmReferenceDataPage .ig-refdata-row {
    align-items: stretch;
}

#dmReferenceDataPage .ig-refdata-row > .ig-refdata-list {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The wrapper holds the button row, the filter and .dataTables_scroll.
   The first two keep their natural height; the scroll box takes the
   rest. */
#dmReferenceDataPage #dmReferenceDataTable_wrapper,
#dmReferenceDataPage #dmReferenceDataTable_wrapper > .dataTables_scroll {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/* DataTables writes an inline max-height on this box, from scrollY
   ("60dvh", ignite.js:1263) and again from scrollCollapse on every
   draw. Flex owns the box now, so that inline value has to lose, and an
   inline style can only be beaten by !important. This is the ONE place
   in this module that needs it. */
#dmReferenceDataPage #dmReferenceDataTable_wrapper .dataTables_scrollBody {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none !important;
    height: auto !important;
}

/* ───────────────────────────────────────────────────────────────────
   Document Type pane — the one pane that is not a plain Bootstrap row

   dm-service-discipline wraps its four columns in .pannable with
   flex-wrap:nowrap, and the columns carry col-min-lg + col-min-lg +
   col-min-xl + col-min-xxl: 2600px of floor with flex-shrink:0
   (:1384-1397), against a stage that is the window minus the sidebar
   minus the 250px list. So the pannable ALWAYS overflows sideways.

   Two things follow, and both are what makes this pane feel stuck.

   1. .pannable (:1255) declares only overflow-x:auto. Per the CSS
      overflow spec an axis left at `visible` beside an axis that is
      not computes to `auto`, so .pannable is silently a VERTICAL
      scroll container too. Its horizontal scrollbar eats ~15px off
      the bottom of its own box, the content then overflows vertically
      by that same ~15px, and a second scrollbar is drawn for it. A
      nested bar with 15px of travel.

   2. That same 15px makes scrollHeight - clientHeight > 1 at
      ignite.js:4881, so grab-to-pan picks the pannable as the
      vertical scroll target and a drag jitters the panel.

   Same fix, same reason, as #projectDeliverableOpenInteractionTab at
   :1735 below: with a floor that wide the grid always fits its
   container, so the container never scrolls and something above it
   does instead. Every grid here comes from initializeGenericTable,
   which sets scrollX:"98%" (ignite.js:2100), so each already owns a
   horizontal scroll body ready to take the overflow back.

   Scoped to #dmReferenceDataPage: dm-service-discipline keeps its pan
   on its own standalone route, where it has the full window width.
   ─────────────────────────────────────────────────────────────────── */

/* Not a scroll container on either axis. overflow:visible sets BOTH,
   which is what breaks the auto-computation above -- setting
   overflow-y alone would leave overflow-x:auto and change nothing. */
#dmReferenceDataPage #dmDocumentTypePane .pannable {
    overflow: visible;
    cursor: default;
}

/* The floors are what stopped the columns shrinking. flex-shrink is
   restored alongside min-width because col-min-* sets both. */
#dmReferenceDataPage #dmDocumentTypePane [class*="col-min-"] {
    min-width: 0;
    flex-shrink: 1;
}

/* A flex child defaults to min-width:auto and refuses to shrink below
   its content, which would put the floor straight back. */
#dmReferenceDataPage #dmDocumentTypePane .row,
#dmReferenceDataPage #dmDocumentTypePane .pannable > .row > [class*="col-lg-"] {
    min-width: 0;
}

/* Explicit proportions, because col-lg-3/3/2/4 only carry a width at
   >=992px and the stage column drops below that on a laptop. Under
   the breakpoint those classes contribute nothing, and the columns
   would size to their content inside a row that is flex-wrap:nowrap.
   Naming the split here makes it 3:3:2:4 at every width. flex-basis 0
   so the grow factors are the whole story. */
#dmReferenceDataPage #dmDocumentTypePane .pannable > .row > .col-lg-3 {
    flex: 3 1 0;
}
#dmReferenceDataPage #dmDocumentTypePane .pannable > .row > .col-lg-2 {
    flex: 2 1 0;
}
#dmReferenceDataPage #dmDocumentTypePane .pannable > .row > .col-lg-4 {
    flex: 4 1 0;
}

/* The selected reference table's name, appended to the page header
   rather than given a line of its own: a heading strip above the stage
   would cost ~40px of the fold on every screen. Colour lives here, not
   in JS, so dark mode picks it up. */
#dmRefDataStageName {
    color: #888888;
    font-weight: normal;
}

/* Same job as the #johannes-main-content .col-lg-12 rule at the end of this
   file: kill the 30px .panel margin (style.css:1373) on the OUTERMOST page
   card. That rule's selector does not reach these two pages, and the margin
   sits between the card and #ig_footer -- inside the span
   igFitBlockToViewport reserves -- so it is subtracted from the frame's
   height on every screen. Scoped by page id, so no other panel is touched. */
#referenceDataPage > section.panel,
#dmReferenceDataPage > section.panel {
    margin-bottom: 1px;
}

/* Nothing on these two pages scrolls: the frame is fitted to the fold and
   refDataLockPageScroll locks the page box. Measured on both documents,
   scrollHeight equals clientHeight on each. What remains is a scrollbar
   GUTTER with no thumb in it -- a track the engine reserves for a scroll
   container that turns out to have nothing to scroll.

   Removing it is lossless BY CONSTRUCTION here, which is the only reason
   this is acceptable: with no overflow there is nothing a scrollbar could
   have reached. Scoped to the two page ids, so no other screen loses a
   scrollbar it actually needs. Both engines named -- Firefox reads
   scrollbar-width, Chromium and Safari need the pseudo-element. */
/* #dmReferenceDataPage removed: that page no longer has a frame, so it has
   no phantom gutter to suppress, and its grids are in a table fit set that
   ends the document at the fold. Suppressing the bar there now would only
   hide a scrollbar the user genuinely needs. #referenceDataPage still uses
   the iframe and still needs this. */
body:has(> #johannes-main-content #referenceDataPage),
html:has(#referenceDataPage) {
    scrollbar-width: none;
}

html:has(#referenceDataPage)::-webkit-scrollbar,
body:has(#referenceDataPage)::-webkit-scrollbar {
    width: 0;
    height: 0;
}

/* ═══════════════════════════════════════════════════════════════════
   projectDeliverableOpenInteractionTab — layout + scroll ownership

   Lives here, next to the .table-scrollable module it interacts with,
   rather than in a <style> block in the tab's HTML: an ID-scoped rule
   that touches .dataTables_scrollBody outranks the .ig-no-hscroll rule
   above no matter how many classes that one carries, and a rule that
   can silently defeat a global mechanism has to be visible to anyone
   reading the stylesheet.
   ═══════════════════════════════════════════════════════════════════ */

/* The tab is never a horizontal scroller. Anything too wide is handled
   by the grid that owns it, not by moving the page. */
/* clip, not hidden: hidden would turn the pane into a vertical scroller too. */
#projectDeliverableOpenInteractionTab {
    overflow-x: clip;
    max-width: 100%;
}

/* A flex child defaults to min-width:auto and so refuses to shrink below
   its content width - that is what pushes the row past the viewport.
   min-width:0 lets each column collapse to whatever room is actually
   available and hand the overflow down to its table. */
#projectDeliverableOpenInteractionTab .row,
#projectDeliverableOpenInteractionTab .oi-panel-col {
    min-width: 0;
}

/* col-min-xxxl and friends set a 1400px floor with flex-shrink:0. With a
   floor that wide the table always fits its container, so the container
   never scrolls and the page does instead. Neutralised in case any
   col-min-* is still on the markup. */
#projectDeliverableOpenInteractionTab [class*="col-min-"] {
    min-width: 0;
    flex-shrink: 1;
}

/* One scroller per grid, and it is DataTables' own scroll body. The
   wrapper above it is switched off so the two cannot stack. */
#projectDeliverableOpenInteractionTab .table-scrollable .dataTables_wrapper {
    overflow-x: hidden;
}

/* :not() is load-bearing - see the module note above. Without it this
   rule wins on ID specificity and re-enables the scroller on every grid
   in this tab, defeating igSyncHorizontalScrollbar entirely. */
#projectDeliverableOpenInteractionTab .table-scrollable .dataTables_scrollBody:not(.ig-no-hscroll) {
    overflow-x: auto !important;
}

/* timesheetEntryTab: same scroll ownership as the Open Interaction tab above.
   A fieldset defaults to min-width:min-content, so it grew to the full
   max-content table width and pushed the page sideways. */
#timesheetEntryTab {
    overflow-x: clip;
    max-width: 100%;
}

#timesheetEntryTab fieldset,
#timesheetEntryTab .row,
#timesheetEntryTab [class*="col-"] {
    min-width: 0;
}

#timesheetEntryTab .table-scrollable .dataTables_wrapper {
    overflow-x: hidden;
}
/* ================ Scrollable Table =============== */
/* ================================================= */
/* ================================================= */

.badge-status {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

.badge-status-blue {
    background-color: var(--ig-status-default);
    color: var(--ig-status-default-text);
}

.badge-status-red {
    background-color: var(--ig-status-overdue);
    color: var(--ig-status-overdue-text);
}

.badge-status-orange {
    background-color: var(--ig-status-at-risk);
    color: var(--ig-status-at-risk-text);
}

.badge-status-grey {
    background-color: var(--ig-status-not-started);
    color: var(--ig-status-not-started-text);
}

.badge-status-default {
    background-color: var(--ig-status-default);
    color: var(--ig-status-default-text);
}

.badge-status-green {
    background-color: var(--ig-status-on-track);
    color: var(--ig-status-on-track-text);
}

.badge-status-green-dark {
    background-color: var(--ig-status-completed);
    color: var(--ig-status-completed-text);
}

.badge-status-unassigned {
    background-color: var(--ig-status-not-started);
    color: var(--ig-status-not-started-text);
    opacity: 0.8;
}

.badge-status-lg {
    padding: 5px 16px;
    margin-top: 6px;
    display: inline-block;
    vertical-align: middle;
}

/* ================================================= */
/* =========  Notification Response Pills  ========= */
/* ========= (actionsAndNotificationsTab)  ========= */
/* ================================================= */
.ant-resp-btn,
.ant-resp-done {
    display: inline-block;
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.3px;
    white-space: nowrap;
    line-height: 1.5;
    padding: 3px 12px;
    margin: 2px 4px 2px 0;
    border-radius: 12px;
    border: 1px solid rgba(0, 0, 0, 0.20);
    background-color: var(--ig-status-default);
    color: var(--ig-status-default-text);
}

.ant-resp-btn {
    cursor: pointer;
    /* Two-layer shadow, deliberately QUIET at rest. The first is a HARD 1px
       edge directly under the pill — no blur — which reads as the moulded lip
       of a physical key. That edge is what separates a button from the flat
       .ant-resp-done label beside it, before the user has hovered anything.
       The second is a faint lift.
       Both were previously twice these values (0.18 / 0.10) and stacked into a
       visible dark band along the bottom curve on the pale panel — the pill
       looked pre-lifted, so hover had nowhere to go. Keep the rest state
       under-stated; the :hover rule below owns the lift. */
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.09),
        0 1px 2px rgba(0, 0, 0, 0.05);
    transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}

.ant-resp-btn:hover {
    transform: translateY(-1px);
    /* Saturate, do NOT darken. The old brightness(0.94) read as "pressed", so
       hover and active were saying the same thing. Raising saturation makes the
       status colour assert itself while keeping the dark label text legible. */
    filter: saturate(1.18) brightness(1.03);
    box-shadow: 0 2px 0 rgba(0, 0, 0, 0.20),
        0 4px 10px rgba(0, 0, 0, 0.16);
}

.ant-resp-btn:active {
    /* Travels 2px from the hover position and the hard edge collapses to zero —
       the pill sits down onto the surface. */
    transform: translateY(1px);
    filter: saturate(1.18) brightness(0.96);
    box-shadow: 0 0 0 rgba(0, 0, 0, 0.20),
        0 1px 2px rgba(0, 0, 0, 0.12);
}

@media (prefers-reduced-motion: reduce) {
    .ant-resp-btn {
        transition: none;
    }
}

.ant-resp-btn:focus-visible {
    outline: 2px solid var(--ig-accent-steel);
    outline-offset: 1px;
}

/* Same treatment as .doc-file-action:disabled below: a pill that acts on a
   selection is inert until there is one. pointer-events also suppresses the
   :hover lift and :active travel above. */
.ant-resp-btn:disabled {
    opacity: 0.4;
    cursor: default;
    pointer-events: none;
}

.ant-resp-done {
    opacity: 0.92;
}

.ant-resp-accept {
    background-color: var(--ig-status-on-track);
    color: var(--ig-status-on-track-text);
}

.ant-resp-decline {
    background-color: var(--ig-status-at-risk);
    color: var(--ig-status-at-risk-text);
}

.ant-resp-cond {
    background-color: var(--ig-status-completed);
    color: var(--ig-status-completed-text);
}

.ant-resp-cede {
    background-color: var(--ig-status-delayed);
    color: var(--ig-status-delayed-text);
}

.ant-resp-delegate {
    background-color: var(--ig-status-approval);
    color: var(--ig-status-approval-text);
}

.ant-resp-noted {
    background-color: var(--ig-status-not-started);
    color: var(--ig-status-not-started-text);
}

.ant-resp-withdraw {
    background-color: var(--ig-status-withdraw);
    color: var(--ig-status-withdraw-text);
}

/* ================================================= */
/* =========  Notification Response Pills  ========= */
/* ================================================= */

/* side bar toggle */
/* ═══════════════════════════════════════════════════════ */
/* ============ Sidebar Collapse (icon-only) =========== */
/* ═══════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════ */
/* ============ Sidebar Collapse (icon-only) =========== */
/* ═══════════════════════════════════════════════════════ */

#sidebar,
aside {
    transition: width 0.2s ease;

    /* Was `overflow: hidden`, which meant that once an expanded submenu made
       the menu taller than the viewport the extra items were simply clipped
       and unreachable. Cap the height and scroll instead.

       overflow-y:auto engages ONLY when the menu is genuinely taller than the
       space available — when everything fits, nothing about the layout
       changes. The scrollbar itself is hidden on all engines so the menu
       stays a solid block with no track running down it; wheel, trackpad,
       touch and keyboard scrolling all still work.

       --ig-sidebar-offset: set this if a fixed header is ever placed above
       the sidebar, so the cap becomes 100vh minus that header's height. */
    max-height: calc(100vh - var(--ig-sidebar-offset, 0px));
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* stop scroll chaining to the page */
    scrollbar-width: none;
    /* Firefox */
    -ms-overflow-style: none;
    /* legacy Edge / IE */
}

#sidebar::-webkit-scrollbar,
aside::-webkit-scrollbar {
    /* Chrome / Safari / Edge */
    width: 0;
    height: 0;
}

.sidebar-collapsed #sidebar,
.sidebar-collapsed aside {
    overflow-x: visible !important;
    overflow-y: auto !important;
}

.sidebar-collapsed aside,
.sidebar-collapsed #sidebar {
    width: 44px !important;
    min-width: 44px !important;
    max-width: 44px !important;
    position: fixed !important;
    top: 0;
    bottom: 0;
    z-index: 100;
    overflow-y: auto;
    overflow-x: visible !important;
}

/* .sidebar-collapsed #sidebar li a {
    font-size: 0 !important;
    padding: 12px 0 !important;
    text-align: center;
    white-space: nowrap;
} */

.sidebar-collapsed #sidebar li a {
    font-size: 0 !important;
    padding: 12px 0 !important;
    text-align: center;
    white-space: nowrap;
    overflow: visible;
}

/* Collapsed nav: tooltip label on hover */
/* .sidebar-collapsed #sidebar li {
    position: relative;
}
.sidebar-collapsed #sidebar li a::after {
    content: attr(data-menu-title);
    position: absolute;
    left: 54px;
    top: 50%;
    transform: translateY(-50%);
    background: #1e2d3d;
    color: #9EDFEA;
    font-size: 12px !important;
    font-weight: 500;
    padding: 5px 12px;
    border-radius: 4px;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    border: 1px solid rgba(134, 173, 220, 0.25);
    box-shadow: 2px 2px 8px rgba(0,0,0,0.3);
    transition: opacity 0.18s ease;
    z-index: 9999;
}
.sidebar-collapsed #sidebar li a:hover::after {
    opacity: 1;
} */

/* Collapsed nav: icon-only with smooth tooltip + icon highlight on hover */
.sidebar-collapsed #sidebar li {
    position: relative;
}

.sidebar-collapsed #sidebar li a::after {
    content: attr(data-menu-title);
    position: absolute;
    left: 52px;
    top: 50%;
    transform: translateY(-50%) translateX(-6px);
    background: linear-gradient(135deg, #1e2d3d 0%, #253445 100%);
    color: #9EDFEA;
    font-size: 12px !important;
    font-weight: 500;
    padding: 6px 14px;
    border-radius: 6px;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    border: 1px solid rgba(134, 173, 220, 0.3);
    box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.35),
        0 0 0 1px rgba(158, 223, 234, 0.08);
    transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
    z-index: 9999;
}

.sidebar-collapsed #sidebar li a:hover::after {
    opacity: 1;
    transform: translateY(-50%) translateX(0) scale(1.04);
}

/* Collapsed nav item — smooth grow + glow on hover */
.sidebar-collapsed #sidebar li a {
    transition: background 0.2s ease, border-color 0.2s ease,
        transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),
        box-shadow 0.22s ease !important;
    transform-origin: left center;
}

.sidebar-collapsed #sidebar li a:hover {
    background: rgba(134, 173, 220, 0.15) !important;
    border-left: 3px solid #9EDFEA !important;
    transform: scaleX(1.06) !important;
    box-shadow: inset 0 0 12px rgba(158, 223, 234, 0.08),
        2px 0 12px rgba(158, 223, 234, 0.12) !important;
}

/* Icon highlight + scale on hover when collapsed */
.sidebar-collapsed #sidebar li a i,
.sidebar-collapsed #sidebar li a .fa,
.sidebar-collapsed #sidebar li a [class*="fa-"] {
    transition: color 0.22s ease,
        transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
    display: inline-block;
}

.sidebar-collapsed #sidebar li a:hover i,
.sidebar-collapsed #sidebar li a:hover .fa,
.sidebar-collapsed #sidebar li a:hover [class*="fa-"] {
    color: #9EDFEA !important;
    transform: scale(1.25) !important;
    filter: drop-shadow(0 0 4px rgba(158, 223, 234, 0.5));
}


.sidebar-collapsed #sidebar li a i,
.sidebar-collapsed #sidebar li a .fa,
.sidebar-collapsed #sidebar li a [class*="icon"],
.sidebar-collapsed #sidebar li a [class*="fa-"] {
    font-size: 16px !important;
    margin: 0 !important;
    display: inline-block;
    width: 100%;
}

.sidebar-collapsed #sidebar li a .menu-arrow,
.sidebar-collapsed #sidebar .sub-menu,
.sidebar-collapsed #sidebar .sidebar-menu>li>ul {
    display: none !important;
}

/* .sidebar-collapsed #johannes-main-content,
.sidebar-collapsed section#johannes-main-content,
.sidebar-collapsed #main-content,
.sidebar-collapsed section#main-content {
    margin-left: 50px !important;
    padding-left: 0 !important;
} */

.sidebar-collapsed #johannes-main-content,
.sidebar-collapsed section#johannes-main-content,
.sidebar-collapsed #main-content,
.sidebar-collapsed section#main-content,
.sidebar-autohide #johannes-main-content,
.sidebar-autohide section#johannes-main-content,
.sidebar-autohide #main-content,
.sidebar-autohide section#main-content {
    margin-left: 44px !important;
    padding-left: 10px !important;
}

.sidebar-collapsed #johannes-main-content>.wrapper,
.sidebar-collapsed #johannes-main-content>section.wrapper,
.sidebar-collapsed #main-content>.wrapper,
.sidebar-collapsed #main-content>section.wrapper,
.sidebar-autohide #johannes-main-content>.wrapper,
.sidebar-autohide #johannes-main-content>section.wrapper,
.sidebar-autohide #main-content>.wrapper,
.sidebar-autohide #main-content>section.wrapper {
    margin-left: 0 !important;
    padding-left: 0 !important;
}

/* Expand on hover: a 50px icon rail that slides open over the page. */
.sidebar-autohide #sidebar {
    z-index: 100;
    transition: width 0.22s ease 0.15s, box-shadow 0.22s ease 0.15s;
}

.sidebar-autohide #sidebar:not(:hover) {
    width: 44px;
}

.sidebar-autohide #sidebar:hover {
    box-shadow: 4px 0 18px rgba(15, 25, 40, 0.28);
    transition-delay: 0.12s;
}

.sidebar-autohide #sidebar ul.sidebar-menu li a {
    white-space: nowrap;
    transition: font-size 0.22s ease 0.15s, background 0.2s ease, color 0.2s ease,
        border-color 0.2s ease, padding 0.2s ease, box-shadow 0.2s ease;
}

.sidebar-autohide #sidebar:hover ul.sidebar-menu li a {
    transition-delay: 0.12s, 0s, 0s, 0s, 0s, 0s;
}

.sidebar-autohide #sidebar:not(:hover) ul.sidebar-menu li a {
    font-size: 0;
}

.sidebar-autohide #sidebar:not(:hover) ul.sidebar-menu ul.sub {
    display: none !important;
}

.nav-item.dropdown.open .dropdown-menu,
.nav-item.dropdown .dropdown-menu {
    z-index: 1050 !important;
}

/* Dropdown inside scrollable tabs — escape overflow clipping */
.panel-heading.tab-bg-info .nav-tabs>li.dropdown {
    position: static;
}

/* ── Nav tab dropdown menu — polished styling ─────────────────────────── */
.panel-heading.tab-bg-info .nav-tabs .dropdown-menu {
    z-index: 1050 !important;
    border: none !important;
    border-radius: 8px !important;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.08) !important;
    padding: 6px 0 !important;
    min-width: 160px !important;
    overflow: hidden !important;
    background: #ffffff !important;
    margin-top: 2px !important;
}

.panel-heading.tab-bg-info .nav-tabs .dropdown-menu>li>a,
.panel-heading.tab-bg-info .nav-tabs .dropdown-menu .dropdown-item {
    padding: 8px 18px !important;
    font-size: 13px !important;
    font-weight: 500 !important;
    color: #3a4a5c !important;
    line-height: 1.4 !important;
    display: block !important;
    white-space: nowrap !important;
    text-decoration: none !important;
    border-radius: 0 !important;
    background: transparent !important;
    border: none !important;
    transition: background 0.12s ease, color 0.12s ease !important;
}

.panel-heading.tab-bg-info .nav-tabs .dropdown-menu>li>a:hover,
.panel-heading.tab-bg-info .nav-tabs .dropdown-menu .dropdown-item:hover {
    background: #f0f5fb !important;
    color: #2563eb !important;
}

/* Caret on dropdown toggle — cleaner */
.panel-heading.tab-bg-info .nav-tabs li.dropdown>a .caret {
    margin-left: 4px;
    opacity: 0.6;
}

body:not(.dark-mode) .modal-footer,
body:not(.dark-mode) .fixed-dialog-footer {
    border-top: 1px solid #dee2e6 !important;
}

/* ================================================= */
/* ============ File Upload Drop Zone ============== */
/* ================================================= */


#fileDropZone,
#docUploadDropZone,
#dmfUploadDropZone {
    border: 2px dashed #4a90d9;
    border-radius: 10px;
    padding: 2.5rem 1rem;
    text-align: center;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
    background: #eaf3fb;
    margin-bottom: 0.25rem;
}

#fileDropZone:hover,
#docUploadDropZone:hover,
#dmfUploadDropZone:hover {
    background: #d6eaf8;
    border-color: #1a6fbf;
}

/* Active drag-over state */
#fileDropZone.drag-over,
#docUploadDropZone.drag-over,
#dmfUploadDropZone.drag-over {
    background: #d6eaf8;
    border-color: #1a6fbf;
}

/* File chosen state — green accent */
#fileDropZone.file-chosen,
#docUploadDropZone.file-chosen,
#dmfUploadDropZone.file-chosen {
    border-color: #198754;
    border-style: solid;
}

#fileDropZonePrompt p,
#docUploadDropZonePrompt p,
#dmfUploadDropZonePrompt p {
    margin: 0;
    font-size: 0.9rem;
    color: #333;
}

#fileDropZonePrompt svg,
#docUploadDropZonePrompt svg,
#dmfUploadDropZonePrompt svg {
    display: block;
    stroke: #4a90d9;
    margin: 0 auto 0.75rem;
}

#fileDropZonePrompt span,
#docUploadDropZonePrompt span,
#dmfUploadDropZonePrompt span {
    color: #4a90d9;
    text-decoration: underline;
    cursor: pointer;
}

#fileDropZoneChosen,
#docUploadDropZoneChosen,
#dmfUploadDropZoneChosen {
    display: none;
    align-items: center;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
}

#fileDropZoneChosen .file-icon,
#docUploadDropZoneChosen .file-icon,
#dmfUploadDropZoneChosen .file-icon {
    font-size: 1.4rem;
    color: #198754;
    flex-shrink: 0;
}

#fileDropZoneFileName,
#docUploadDropZoneFileName,
#dmfUploadDropZoneFileName {
    font-weight: 500;
    color: #198754;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 260px;
    font-size: 0.9rem;
}

#fileDropZoneClearBtn,
#docUploadDropZoneClearButton,
#dmfUploadDropZoneClearButton {
    background: none;
    border: none;
    cursor: pointer;
    color: #198754;
    font-size: 1.3rem;
    line-height: 1;
    padding: 0 2px;
    flex-shrink: 0;
}

.file-drop-size-limit {
    text-align: right;
    font-size: 0.95rem;
    color: #6c757d;
    margin-top: 4px;
    margin-bottom: 0.25rem;
}

/* ================================================= */
/* ============ File Upload Drop Zone ============== */
/* ================================================= */


/* ── .ig-liquid-glass — reusable liquid-glass SURFACE (panels, popovers) ── */
.ig-liquid-glass {
    /* v5: the pour now lives in ONE theme-aware token pair
       (--igd-pop-bg / --igd-pop-border, ignite.css :root) so this
       surface, .modal-content and .gantt-tooltip can never drift
       apart again. Thinner pour + lighter blur = clearer glass;
       saturate 1.8 keeps the show-through vivid, not washed out. */
    background: var(--igd-pop-bg);
    backdrop-filter: blur(14px) saturate(1.8);
    -webkit-backdrop-filter: blur(14px) saturate(1.8);
    border: 1px solid var(--igd-pop-border);
    border-radius: 14px;
    box-shadow: var(--igd-shadow);
}

body.dark-mode .ig-liquid-glass {
    /* v5: fully token-driven — --igd-pop-bg / --igd-pop-border /
       --igd-shadow all flip with the theme in the :root block. */
    background: var(--igd-pop-bg);
    border-color: var(--igd-pop-border);
    box-shadow: var(--igd-shadow);
}

/* ═══════════════════════════════════════════════════════════════════════
   Shared tool-button icon animations
   -----------------------------------------------------------------------
   ONE consistent behaviour everywhere: a subtle, smooth "rise + glow" on
   hover — the exact feel of the Documents-tab folder delete button.

     ADD    (ig-ic-add)  : gentle lift + soft blue glow   (fa-plus, fa-folder-plus, cog-plus)
     DELETE (ig-ic-del)  : del-lid-open rise + soft red glow  (fa-trash everywhere)
     EDIT   (ig-ic-edit) : pencil "writing" strokes + soft blue glow  (fa-edit)

   Applied to the <i> (or composite <span>) inside any tool button, so it
   works on .btn / .btn-glass / .dt-button and the Gantt context menu.

       <button class="btn btn-sm btn-glass">
           <i class="fas fa-folder-plus ig-ic-add"></i>
       </button>
       <button class="btn btn-sm btn-glass">
           <i class="fa fa-trash ig-ic-del"></i>
       </button>
   ═══════════════════════════════════════════════════════════════════════ */


/* ── Documents tab: folder-sync overlay ──────────────────────────────
   Scrim recipe copied from .modal-backdrop.in/.show so the two dim the
   page identically. The panel carries .ig-liquid-glass, so its surface,
   border and shadow come from --igd-pop-bg / --igd-pop-border /
   --igd-shadow and flip with the theme on their own — only the scrim
   needs a dark override.                                              */
.ig-doc-sync-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 9998;
    background: rgba(21, 34, 49, 0.16);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: center;
}

.ig-doc-sync-panel {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 20px 30px;
    font-size: 14px;
    color: var(--igd-ink);
}

.ig-doc-sync-spinner {
    color: var(--igd-accent);
}

.ig-ic-add,
.ig-ic-del {
    display: inline-block;
    transition: color 0.2s ease, text-shadow 0.2s ease, transform 0.25s ease;
    transform-origin: center 60%;
}

/* ── ADD: subtle smooth rise + blue glow (loops gently while hovered) ── */
@keyframes ig-ic-rise {

    0%,
    100% {
        transform: translateY(0) rotate(0deg);
    }

    30% {
        transform: translateY(-3px) rotate(0deg);
    }

    60% {
        transform: translateY(-1px) rotate(0deg);
    }
}

.btn:hover .ig-ic-add,
.btn-glass:hover .ig-ic-add,
.dt-button:hover .ig-ic-add {
    animation: ig-ic-rise 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #4b8fef !important;
    text-shadow:
        0 0 6px rgba(75, 143, 239, 0.6),
        0 0 12px rgba(75, 143, 239, 0.4);
}

/* ── DELETE: identical to the Documents tab — del-lid-open rise + red glow ── */
.btn:hover .ig-ic-del,
.btn-glass:hover .ig-ic-del,
.dt-button:hover .ig-ic-del,
.gantt-ctx-menu-delete:hover .ig-ic-del {
    animation: del-lid-open 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #ef6b6b !important;
    text-shadow:
        0 0 6px rgba(239, 107, 107, 0.7),
        0 0 12px rgba(239, 107, 107, 0.4);
}

/* ── "Add works component" composite icon ──
   A compact tool glyph (toolbox = "works/tools") with a small, clearly
   visible plus tucked into its clean top-right corner. The toolbox has an
   empty top-right, so unlike the wrench the plus never collides with it.
   Both glyphs inherit the button colour at rest and share the same smooth
   blue rise + glow on hover (the wrapper carries ig-ic-add; the absolute
   plus rides along because the animation transforms the wrapper).
   The plus is a plain fa-plus — the same glyph (and weight) baked into
   the participants fa-user-plus — so it stays consistent across the app.

   Swap the main glyph freely — all keep the clean corner + plus:
     fa-toolbox (default) · fa-cubes / fa-cube (most "component"-like)
     · fa-hard-hat · fa-hammer · fa-screwdriver · fa-wrench

   <span class="ig-tool-add ig-ic-add">
       <i class="fas fa-toolbox ig-tool-add__main"></i>
       <i class="fas fa-plus ig-tool-add__plus"></i>
   </span>

   (.ig-cog-plus / __badge kept as aliases so older markup still styles.) */
.ig-tool-add,
.ig-cog-plus {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

.ig-tool-add__main,
.ig-cog-plus>.fa-cog {
    font-size: 1.2em;
    /* bigger toolbox glyph */
    font-weight: 900;
    /* fa-solid weight — thicker strokes */
    line-height: 1;
    position: relative;
    left: -1px;
    top: -1px;
    /* raised so bottom aligns with trash icon */
}

.ig-tool-add__plus,
.ig-tool-add__badge,
.ig-cog-plus__badge {
    position: absolute;
    top: -5px;
    right: -3px;
    font-size: 0.7em;
    /* slightly smaller plus */
    font-weight: 900;
    /* fa-solid weight — thicker strokes */
    line-height: 1;
    pointer-events: none;
}


/* Respect reduced-motion: keep the colour/glow, drop the movement */
@media (prefers-reduced-motion: reduce) {

    .btn:hover .ig-ic-add,
    .btn:hover .ig-ic-del,
    .btn-glass:hover .ig-ic-add,
    .btn-glass:hover .ig-ic-del,
    .dt-button:hover .ig-ic-add,
    .dt-button:hover .ig-ic-del,
    .gantt-ctx-menu-delete:hover .ig-ic-del {
        animation: none !important;
        transform: none !important;
    }
}

/* Dark-mode glow tints (slightly brighter so they read on dark chrome) */
body.dark-mode .btn:hover .ig-ic-del,
body.dark-mode .btn-glass:hover .ig-ic-del,
body.dark-mode .dt-button:hover .ig-ic-del,
body.dark-mode .gantt-ctx-menu-delete:hover .ig-ic-del {
    color: #f08080 !important;
    text-shadow:
        0 0 6px rgba(240, 128, 128, 0.7),
        0 0 12px rgba(240, 128, 128, 0.4);
}

body.dark-mode .btn:hover .ig-ic-add,
body.dark-mode .btn-glass:hover .ig-ic-add,
body.dark-mode .dt-button:hover .ig-ic-add {
    color: #7db0f5 !important;
    text-shadow:
        0 0 6px rgba(125, 176, 245, 0.6),
        0 0 12px rgba(125, 176, 245, 0.4);
}

/* ── Expand / collapse utility icons (tree toolbars) ──
   A gentle outward pulse for "expand all", inward pulse for "collapse
   all" — the motion mirrors what the button does. */
@keyframes ig-ic-expand {

    0%,
    100% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.18);
    }
}

@keyframes ig-ic-collapse {

    0%,
    100% {
        transform: scale(1);
    }

    50% {
        transform: scale(0.82);
    }
}

.ig-ic-expand,
.ig-ic-collapse {
    display: inline-block;
    transition: color 0.2s ease, transform 0.25s ease;
}

.btn:hover .ig-ic-expand,
.btn-glass:hover .ig-ic-expand,
.dt-button:hover .ig-ic-expand {
    animation: ig-ic-expand 0.9s ease-in-out infinite;
    color: #4b8fef !important;
}

.btn:hover .ig-ic-collapse,
.btn-glass:hover .ig-ic-collapse,
.dt-button:hover .ig-ic-collapse {
    animation: ig-ic-collapse 0.9s ease-in-out infinite;
    color: #4b8fef !important;
}

@media (prefers-reduced-motion: reduce) {

    .btn:hover .ig-ic-expand,
    .btn:hover .ig-ic-collapse,
    .btn-glass:hover .ig-ic-expand,
    .btn-glass:hover .ig-ic-collapse,
    .dt-button:hover .ig-ic-expand,
    .dt-button:hover .ig-ic-collapse {
        animation: none !important;
        transform: none !important;
    }
}

body.dark-mode .btn:hover .ig-ic-expand,
body.dark-mode .btn-glass:hover .ig-ic-expand,
body.dark-mode .dt-button:hover .ig-ic-expand,
body.dark-mode .btn:hover .ig-ic-collapse,
body.dark-mode .btn-glass:hover .ig-ic-collapse,
body.dark-mode .dt-button:hover .ig-ic-collapse {
    color: #7db0f5 !important;
}

/* ── EDIT (ig-ic-edit) ──
   A pencil that "writes": short strokes back and forth along its own
   diagonal axis, with a tiny tilt on each pass — the motion mirrors what
   the button does. Same house blue + glow as ADD / expand / collapse, so
   it sits naturally beside them in a tree toolbar.

       <button class="btn btn-sm btn-glass">
           <i class="fas fa-edit ig-ic-edit"></i>
       </button>                                                        */
@keyframes ig-ic-edit {

    0%,
    100% {
        transform: translate(0, 0) rotate(0deg);
    }

    25% {
        transform: translate(1.5px, -1.5px) rotate(-6deg);
    }

    50% {
        transform: translate(0, 0) rotate(0deg);
    }

    75% {
        transform: translate(-1.5px, 1.5px) rotate(6deg);
    }
}

.ig-ic-edit {
    display: inline-block;
    transition: color 0.2s ease, text-shadow 0.2s ease, transform 0.25s ease;
    transform-origin: 30% 70%;
    /* pivot near the pencil tip so the nib stays put while it writes */
}

.btn:hover .ig-ic-edit,
.btn-glass:hover .ig-ic-edit,
.dt-button:hover .ig-ic-edit {
    animation: ig-ic-edit 1.1s ease-in-out infinite;
    color: #4b8fef !important;
    text-shadow:
        0 0 6px rgba(75, 143, 239, 0.6),
        0 0 12px rgba(75, 143, 239, 0.4);
}

@media (prefers-reduced-motion: reduce) {

    .btn:hover .ig-ic-edit,
    .btn-glass:hover .ig-ic-edit,
    .dt-button:hover .ig-ic-edit {
        animation: none !important;
        transform: none !important;
    }
}

body.dark-mode .btn:hover .ig-ic-edit,
body.dark-mode .btn-glass:hover .ig-ic-edit,
body.dark-mode .dt-button:hover .ig-ic-edit {
    color: #7db0f5 !important;
    text-shadow:
        0 0 6px rgba(125, 176, 245, 0.6),
        0 0 12px rgba(125, 176, 245, 0.4);
}

/* Fallback for the Font Awesome glyph, so any fa-ellipsis-h button not
   yet swapped to the SVG still responds. One glyph, so it can only lift
   and glow as a unit — no wave. Follows the .fa-users / .fa-list block
   further down this file. */
@keyframes ig-ic-ellipsis {

    0%,
    100% {
        transform: translateY(0) scale(1);
    }

    40% {
        transform: translateY(-2px) scale(1.10);
    }
}

.btn i.fa-ellipsis-h,
.btn-glass i.fa-ellipsis-h,
.dt-button i.fa-ellipsis-h {
    display: inline-block;
    transition: color .2s ease, transform .25s ease, text-shadow .2s ease;
}

.btn:hover i.fa-ellipsis-h,
.btn-glass:hover i.fa-ellipsis-h,
.dt-button:hover i.fa-ellipsis-h {
    animation: ig-ic-ellipsis 1.5s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5), 0 0 12px rgba(75, 143, 239, .3);
}

/* Reduced motion: keep the colour and glow, drop the movement. */
@media (prefers-reduced-motion: reduce) {

    .btn:hover i.fa-ellipsis-h,
    .btn-glass:hover i.fa-ellipsis-h,
    .dt-button:hover i.fa-ellipsis-h {
        animation: none !important;
        transform: none !important;
    }
}

body.dark-mode .btn:hover i.fa-ellipsis-h,
body.dark-mode .btn-glass:hover i.fa-ellipsis-h,
body.dark-mode .dt-button:hover i.fa-ellipsis-h {
    color: #7db0f5 !important;
    text-shadow: 0 0 6px rgba(125, 176, 245, .6), 0 0 12px rgba(125, 176, 245, .4);
}

/* ════════════════════════════════════════════════════════════════
   Per-icon toolbar hover animations (extends the ig-ic-* convention)
   Each common toolbar glyph gets its OWN smooth, on-brand motion that loops
   gently while hovered, keyed on its Font Awesome class and scoped to glass /
   dt-button toolbars. All share the house blue glow and respect reduced-motion.
   ════════════════════════════════════════════════════════════════ */

/* Smooth easing for every animated toolbar glyph below */
.btn i.fa-users,
.btn-glass i.fa-users,
.dt-button i.fa-users,
.btn i.fa-list,
.btn-glass i.fa-list,
.dt-button i.fa-list,
.btn i.fa-calendar,
.btn-glass i.fa-calendar,
.dt-button i.fa-calendar,
.btn i.fa-calendar-alt,
.btn-glass i.fa-calendar-alt,
.dt-button i.fa-calendar-alt,
.btn i.fa-angle-double-left,
.btn-glass i.fa-angle-double-left,
.dt-button i.fa-angle-double-left,
.btn i.fa-angle-double-right,
.btn-glass i.fa-angle-double-right,
.dt-button i.fa-angle-double-right,
.btn i.fa-minus,
.btn-glass i.fa-minus,
.dt-button i.fa-minus {
    display: inline-block;
    transition: color .2s ease, transform .25s ease, text-shadow .2s ease;
}

/* ── PLUS ─ a crisp pop (scoped away from the composite toolbox +) ── */
@keyframes ig-ic-plus {

    0%,
    100% {
        transform: scale(1);
    }

    40% {
        transform: scale(1.22);
    }

    70% {
        transform: scale(1.04);
    }
}

.btn:hover i.fa-plus:not(.ig-tool-add__plus):not(.ig-tool-add__badge):not(.ig-cog-plus__badge),
.btn-glass:hover i.fa-plus:not(.ig-tool-add__plus):not(.ig-tool-add__badge):not(.ig-cog-plus__badge),
.dt-button:hover i.fa-plus:not(.ig-tool-add__plus):not(.ig-tool-add__badge):not(.ig-cog-plus__badge) {
    animation: ig-ic-plus 1.3s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
}

/* ── MINUS ─ a horizontal "contract" pulse, red (delete affordance) ── */
@keyframes ig-ic-minus {

    0%,
    100% {
        transform: scaleX(1);
    }

    50% {
        transform: scaleX(0.58);
    }
}

.btn:hover i.fa-minus,
.btn-glass:hover i.fa-minus,
.dt-button:hover i.fa-minus {
    animation: ig-ic-minus 1.1s ease-in-out infinite;
}

/* ── PEOPLE (fa-users) ─ a friendly "gather" bob: pop up, settle ── */
@keyframes ig-ic-users {

    0%,
    100% {
        transform: translateY(0) scale(1);
    }

    35% {
        transform: translateY(-2px) scale(1.12);
    }

    70% {
        transform: translateY(0) scale(1.04);
    }
}

.btn:hover i.fa-users,
.btn-glass:hover i.fa-users,
.dt-button:hover i.fa-users {
    animation: ig-ic-users 1.5s ease-in-out infinite;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5), 0 0 12px rgba(75, 143, 239, .3);
}

/* ── LIST (fa-list) ─ rows "settle in" with a tiny vertical shuffle ── */
@keyframes ig-ic-list {

    0%,
    100% {
        transform: translateY(0);
    }

    25% {
        transform: translateY(-1.5px);
    }

    50% {
        transform: translateY(1px);
    }

    75% {
        transform: translateY(-0.5px);
    }
}

.btn:hover i.fa-list,
.btn-glass:hover i.fa-list,
.dt-button:hover i.fa-list {
    animation: ig-ic-list 1.4s ease-in-out infinite;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5);
}

/* ── CALENDAR (fa-calendar / -alt) ─ a gentle page-turn flip ── */
@keyframes ig-ic-cal {

    0%,
    100% {
        transform: perspective(70px) rotateX(0deg);
    }

    45% {
        transform: perspective(70px) rotateX(-24deg);
    }
}

.btn:hover i.fa-calendar,
.btn-glass:hover i.fa-calendar,
.dt-button:hover i.fa-calendar,
.btn:hover i.fa-calendar-alt,
.btn-glass:hover i.fa-calendar-alt,
.dt-button:hover i.fa-calendar-alt {
    animation: ig-ic-cal 1.5s ease-in-out infinite;
    transform-origin: center top;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5);
}

/* ── « move-left / include (fa-angle-double-left) ─ slide left ── */
@keyframes ig-ic-dleft {

    0%,
    100% {
        transform: translateX(0);
    }

    50% {
        transform: translateX(-3px);
    }
}

.btn:hover i.fa-angle-double-left,
.btn-glass:hover i.fa-angle-double-left,
.dt-button:hover i.fa-angle-double-left {
    animation: ig-ic-dleft 0.85s ease-in-out infinite;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5);
}

/* ── » move-right / exclude (fa-angle-double-right) ─ slide right ── */
@keyframes ig-ic-dright {

    0%,
    100% {
        transform: translateX(0);
    }

    50% {
        transform: translateX(3px);
    }
}

.btn:hover i.fa-angle-double-right,
.btn-glass:hover i.fa-angle-double-right,
.dt-button:hover i.fa-angle-double-right {
    animation: ig-ic-dright 0.85s ease-in-out infinite;
    color: #4b8fef !important;
    text-shadow: 0 0 6px rgba(75, 143, 239, .5);
}

/* Reduced-motion: keep the colour/glow, drop the movement */
@media (prefers-reduced-motion: reduce) {

    .btn:hover i.fa-plus,
    .btn-glass:hover i.fa-plus,
    .dt-button:hover i.fa-plus,
    .btn:hover i.fa-minus,
    .btn-glass:hover i.fa-minus,
    .dt-button:hover i.fa-minus,
    .btn:hover i.fa-users,
    .btn-glass:hover i.fa-users,
    .dt-button:hover i.fa-users,
    .btn:hover i.fa-list,
    .btn-glass:hover i.fa-list,
    .dt-button:hover i.fa-list,
    .btn:hover i.fa-calendar,
    .btn-glass:hover i.fa-calendar,
    .dt-button:hover i.fa-calendar,
    .btn:hover i.fa-calendar-alt,
    .btn-glass:hover i.fa-calendar-alt,
    .dt-button:hover i.fa-calendar-alt,
    .btn:hover i.fa-angle-double-left,
    .btn-glass:hover i.fa-angle-double-left,
    .dt-button:hover i.fa-angle-double-left,
    .btn:hover i.fa-angle-double-right,
    .btn-glass:hover i.fa-angle-double-right,
    .dt-button:hover i.fa-angle-double-right {
        animation: none !important;
        transform: none !important;
    }
}

/* Dark-mode glow tints for the blue glyphs */
body.dark-mode .btn:hover i.fa-users,
body.dark-mode .btn-glass:hover i.fa-users,
body.dark-mode .dt-button:hover i.fa-users,
body.dark-mode .btn:hover i.fa-list,
body.dark-mode .btn-glass:hover i.fa-list,
body.dark-mode .dt-button:hover i.fa-list,
body.dark-mode .btn:hover i.fa-calendar,
body.dark-mode .btn-glass:hover i.fa-calendar,
body.dark-mode .dt-button:hover i.fa-calendar,
body.dark-mode .btn:hover i.fa-calendar-alt,
body.dark-mode .btn-glass:hover i.fa-calendar-alt,
body.dark-mode .dt-button:hover i.fa-calendar-alt,
body.dark-mode .btn:hover i.fa-angle-double-left,
body.dark-mode .btn-glass:hover i.fa-angle-double-left,
body.dark-mode .dt-button:hover i.fa-angle-double-left,
body.dark-mode .btn:hover i.fa-angle-double-right,
body.dark-mode .btn-glass:hover i.fa-angle-double-right,
body.dark-mode .dt-button:hover i.fa-angle-double-right {
    color: #7db0f5 !important;
    text-shadow: 0 0 6px rgba(125, 176, 245, .6), 0 0 12px rgba(125, 176, 245, .4);
}

/* ═══════════════════════════════════════════════════════════════════════
   Participants tree  (moved here from projectParticipantsTab.html)
   ═══════════════════════════════════════════════════════════════════════ */
#projectParticipantsTab .pp-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 6px 0;
    line-height: 1;
}

#projectParticipantsTab .pp-head h4 {
    margin: 0;
    display: inline-block;
}

#projectParticipantsTab .pp-count {
    font-size: 12px;
    color: #9aa6b2;
    font-weight: 400;
}

/* ═══════════════════════════════════════════════════════════════════════
   Works Components tree  (moved here from projectWorksComponentsTab.html)
   ═══════════════════════════════════════════════════════════════════════ */
#projectWorksComponentsTab .wc-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 6px 0;
    line-height: 1;
}

#projectWorksComponentsTab .wc-head h4 {
    margin: 0;
    display: inline-block;
}

#projectWorksComponentsTab .wc-count {
    font-size: 12px;
    color: #9aa6b2;
    font-weight: 400;
}

/* ═══════════════════════════════════════════════════════════════════════
   Participant Services tree  (#ppTree, projectParticipantSdTab)
   Third scoped copy of the #ppParticipantsTree block above. Deliberate:
   hoisting all three onto one shared class would rewrite the selectors two
   working tabs depend on. If that hoist ever happens, these three blocks
   collapse together.
   ═══════════════════════════════════════════════════════════════════════ */
#projectParticipantSdTab .ppsd-head {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 6px 0;
    line-height: 1;
}

#projectParticipantSdTab .ppsd-head h4 {
    margin: 0;
    display: inline-block;
}

#projectParticipantSdTab .ppsd-count {
    font-size: 12px;
    color: #9aa6b2;
    font-weight: 400;
}

/* ── Layout floors: nothing overlaps, nothing wraps ──────────────────────
   The markup is one row with each heading inside the column it labels, so
   headings and content can no longer drift apart. What was still missing is
   a FLOOR: col-md-* are percentages that shrink with the window while the
   headings and the tree toolbar do not, which is what slid the three h4s
   over each other at narrow widths and at zoom.

   Anything that no longer fits is reached by side-scrolling the pane, which
   carries .pannable — grab-to-pan and tilt-wheel routing come from
   ignite.js for free. The generic .pannable > .row > .col-md-* floors in
   this file do NOT reach here: they need .row to be a direct child of the
   pannable, and this one sits four levels down. Hence the local floors. */

/* Bootstrap's .container caps at ~1140px and centres itself — with floors
   wider than that the content would spill out of a box it can't grow into. */
#projectParticipantSdTab .johannes-container {
    max-width: none;
    min-width: 1880px;
    margin-left: 0;
    margin-right: 0;
}

#projectParticipantSdTab .johannes-container > .row > .col-md-9 {
    flex: 1 1 auto;
    min-width: 1460px;
}

#projectParticipantSdTab #ppRolesAndFunctionsPanel {
    flex: 0 0 400px;
    min-width: 400px;
}

/* The three working columns. */

/* Tree and Assignments share 900px; change both together so the col-md-9 floor still holds. */
#projectParticipantSdTab #ppsdContentRow > .col-md-4 {
    flex: 0 0 460px;
    width: 460px;
    min-width: 460px;
    max-width: none;
}

#projectParticipantSdTab #ppsdContentRow > .col-md-2 {
    flex: 0 0 440px;
    width: 440px;
    min-width: 440px;
    max-width: none;
}

/* The only column that grows — spare width goes to the widest table. */
#projectParticipantSdTab #ppsdContentRow > .col-md-6 {
    flex: 1 1 520px;
    min-width: 520px;
    max-width: none;
}

/* Headings hold one line and are never truncated. flex:0 0 auto is the
   load-bearing half: in a nowrap flex row the heading is the only item that
   CAN shrink (buttons and search are fixed), so without it the heading eats
   every pixel the row is short — which is exactly how "PROJECT PARTICIPANT
   TREE" became "PROJECT PARTICIPANT …". No ellipsis: if the row ever runs
   short again the pane should side-scroll, not silently hide the label. */
#projectParticipantSdTab #ppsdContentRow h4 {
    white-space: nowrap;
    flex: 0 0 auto;
}

/* Tree toolbar: buttons keep their size, the search keeps its 200px, the
   heading keeps its line — so this row can never jump to two. */
#projectParticipantSdTab .ppsd-head {
    flex-wrap: nowrap;
}

#projectParticipantSdTab .ppsd-head h4 {
    white-space: nowrap;
}

#projectParticipantSdTab .ppsd-head .btn {
    flex: 0 0 auto;
}

/* Document Type Links: card floor = the four col-min-* floors in the markup.
   The row's -15px margins cancel the panel body's 15px padding; 4px is the two card borders. */
#projectDocumentTypeLinkTabSd > section.panel {
    min-width: calc(350px + 350px + 500px + 350px + 4px);
}

/* One line each so side-by-side tables start level; full text is in the title attribute. */
#pdtlFilingCategoryHeader,
#pdtlDocumentSubTypeHeader,
#pdtlFileFormatHeader,
#pdctSubTypeHeader,
#pdctFilingCategoryHeader,
#pdctFormatLinkHeader,
#pdctSdHeader,
#wsParticipantHeader,
#wsProgrammeHeader,
#wsProjectHeader {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Same cap as .ddt-set-title (deliverableEdit.css): on the span, since max-width does not apply to a td. */
#projectDocumentTypeTab .pdct-description-cell {
    display: inline-block;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}

/* ════════════════════════════════════════════════════════════════
   Mode strip (.ig-modebar / .ig-mode-btn)
   One setting, two values, shown as segmented pills with a label.
   Restated from .advf-mode-btn (deliverableAdvancedFilter.css:435)
   rather than reused: that file only reaches the project page because
   the Schedule tab happens to include the filter dialog.
   ════════════════════════════════════════════════════════════════ */
.ig-modebar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    margin-bottom: 10px;
}

.ig-modebar-label {
    font-size: 13px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0;
    color: #5a6c7c;
    white-space: nowrap;
    margin-right: 4px;
}

/* The first shadow layer is a hard zero-blur 1px edge, the moulded lip
   that makes the pill read as pressable while its fill is still empty. */
.ig-mode-btn {
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    padding: 4px 13px;
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    line-height: 1.5;
    white-space: nowrap;
    border: 1px solid rgba(0, 0, 0, 0.20);
    border-radius: 12px;
    background: transparent;
    color: var(--igd-ink, #495057);
    cursor: pointer;
    outline: none;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.09),
        0 1px 2px rgba(0, 0, 0, 0.05);
    transition: transform 0.12s ease, box-shadow 0.12s ease,
        filter 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}

.ig-mode-btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 0 rgba(0, 0, 0, 0.20),
        0 4px 10px rgba(0, 0, 0, 0.16);
}

.ig-mode-btn:hover:not(.ig-mode-on) {
    background: rgba(96, 125, 149, 0.12);
}

.ig-mode-btn.ig-mode-on:hover {
    filter: saturate(1.18) brightness(1.03);
}

.ig-mode-btn:active {
    transform: translateY(1px);
    box-shadow: 0 0 0 rgba(0, 0, 0, 0.20),
        0 1px 2px rgba(0, 0, 0, 0.12);
}

.ig-mode-btn:disabled,
.ig-mode-btn.disabled {
    opacity: 0.4;
    cursor: default;
    pointer-events: none;
}

.ig-mode-btn.ig-mode-on:active {
    filter: saturate(1.18) brightness(0.96);
}

/* Outline + offset, not a shadow ring: a ring would fight the lip. */
.ig-mode-btn:focus-visible {
    outline: 2px solid var(--ig-accent-steel, #86ADDC);
    outline-offset: 1px;
}

@media (prefers-reduced-motion: reduce) {
    .ig-mode-btn {
        transition: none;
    }
}

.ig-mode-btn.ig-mode-on {
    background-color: var(--ig-accent-periwinkle, #CBDFF3);
    color: var(--ig-btn-primary-ink, #1e5288);
}

/* Segmented control (Gantt zoom, filter Selection). JS toggles .active on the buttons. */
.ig-segment-group {
    display: inline-flex;
    flex-shrink: 0;
    gap: 0;
    border: 1px solid #cfd8dc;
    border-radius: 7px;
    overflow: hidden;
    background: #f4f6f8;
}

.ig-segment-group > button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 5px 11px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.2px;
    border: none;
    border-right: 1px solid #cfd8dc;
    border-radius: 0;
    background: #f0f4f7;
    color: #2c3e50;
    cursor: pointer;
    transition: all 0.15s;
}

.ig-segment-group > button:last-child {
    border-right: none;
}

.ig-segment-group > button:hover:not(.active) {
    background: #dde4ea;
    border-color: #90a4ae;
}

.ig-segment-group > button.active {
    background: #2563eb;
    color: #fff;
    border-color: #2563eb;
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.18);
}

/* ── Scrollbar ── */
#pfsGeneralTreePanel,
#tfsGeneralTreePanel,
#typicalWorksComponentTreePanel,
#sdroleTreePanel,
#splTreePanel,
#srvProjectSdTreePanel,
.ig-sd-tree-panel,
#bcBudgetCategoryTreePanel {
    overflow-y: scroll !important;
    overflow-x: hidden !important;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: rgba(160, 185, 210, 0.4) transparent;
}

#pfsGeneralTreePanel::-webkit-scrollbar,
#tfsGeneralTreePanel::-webkit-scrollbar,
#typicalWorksComponentTreePanel::-webkit-scrollbar,
#sdroleTreePanel::-webkit-scrollbar,
#splTreePanel::-webkit-scrollbar,
#srvProjectSdTreePanel::-webkit-scrollbar,
.ig-sd-tree-panel::-webkit-scrollbar,
#bcBudgetCategoryTreePanel::-webkit-scrollbar {
    width: 4px;
}

#pfsGeneralTreePanel::-webkit-scrollbar-track,
#tfsGeneralTreePanel::-webkit-scrollbar-track,
#typicalWorksComponentTreePanel::-webkit-scrollbar-track,
#sdroleTreePanel::-webkit-scrollbar-track,
#splTreePanel::-webkit-scrollbar-track,
#srvProjectSdTreePanel::-webkit-scrollbar-track,
.ig-sd-tree-panel::-webkit-scrollbar-track,
#bcBudgetCategoryTreePanel::-webkit-scrollbar-track {
    background: transparent;
}

#pfsGeneralTreePanel::-webkit-scrollbar-thumb,
#tfsGeneralTreePanel::-webkit-scrollbar-thumb,
#typicalWorksComponentTreePanel::-webkit-scrollbar-thumb,
#sdroleTreePanel::-webkit-scrollbar-thumb,
#splTreePanel::-webkit-scrollbar-thumb,
#srvProjectSdTreePanel::-webkit-scrollbar-thumb,
.ig-sd-tree-panel::-webkit-scrollbar-thumb,
#bcBudgetCategoryTreePanel::-webkit-scrollbar-thumb {
    background: rgba(160, 185, 210, 0.4);
    border-radius: 4px;
}

#pfsGeneralTreePanel::-webkit-scrollbar-thumb:hover,
#tfsGeneralTreePanel::-webkit-scrollbar-thumb:hover,
#typicalWorksComponentTreePanel::-webkit-scrollbar-thumb:hover,
#sdroleTreePanel::-webkit-scrollbar-thumb:hover,
#splTreePanel::-webkit-scrollbar-thumb:hover,
#srvProjectSdTreePanel::-webkit-scrollbar-thumb:hover,
.ig-sd-tree-panel::-webkit-scrollbar-thumb:hover,
#bcBudgetCategoryTreePanel::-webkit-scrollbar-thumb:hover {
    background: rgba(120, 160, 200, 0.6);
}
/* ── Scrollbar ── */
/* Height comes from ig-viewport-scroll (tabViewportFit.css). */
#folderTreeColumn .card-body {
    overflow-y: auto !important;
    overflow-x: hidden !important;
    overscroll-behavior: contain !important;
    scrollbar-width: auto;
}

/* ================================================= */
/* multiple column filter */
.btn-active-sort {
    background-color: #4b8fef !important;
    color: #fff !important;
}

/* multiple column filter */


/* DropDown s */

/* Folder Animations */


/* Documents Animations */
/* ================================================================
   tblSubFolders Toolbar — balanced circular toolbar buttons
   Keeps ALL existing functionality intact
   ================================================================ */

/* ================================================================
   BUTTON LAYOUT / ICON FITTING
   ================================================================ */

/* Sizing/shape for .dt-button now lives entirely in igniteButtons.css
   (see its header comment: "The ONLY place button styling lives").
   This block used to hardcode every DataTables button to a 42px circle,
   fighting igniteButtons.css's --ig-control-h (34px) system at equal
   CSS specificity - which one won depended on load order, causing
   DataTables toolbar buttons to render bigger/inconsistent versus
   plain .btn-glass buttons elsewhere in the app. Removed so
   igniteButtons.css is the single source of truth everywhere. */

/* icon wrapper */
.dt-buttons .dt-button span {
    width: 18px;
    height: 18px;

    display: flex;
    align-items: center;
    justify-content: center;
}

/* trash icon */
#deleteFileBtn i {
    font-size: 15px;
    line-height: 1;
    display: block;
}


/* ================================================================
   DELETE
   ================================================================ */

@keyframes del-lid-open {

    0%,
    100% {
        transform: translateY(0) rotate(0deg);
        opacity: 0.9;
    }

    30% {
        transform: translateY(-3px) rotate(-8deg);
        opacity: 1;
    }

    60% {
        transform: translateY(-1px) rotate(-3deg);
        opacity: 1;
    }
}

.dt-button:has(.fa-trash):hover i {
    animation: del-lid-open 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #ef6b6b;
}


/* plus icon slightly smaller */
#btn-add-folder i.fa-plus {
    font-size: 13px !important;
}

/* ----------------------------------------------------------------
   Refresh
   ---------------------------------------------------------------- */

@keyframes ref-rotate {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

/* ----------------------------------------------------------------
   Delete
   ---------------------------------------------------------------- */

#btn-delete-folder:hover i.fa-trash {
    animation: del-lid-open 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #ef6b6b;
}

/* ----------------------------------------------------------------
   Share
   ---------------------------------------------------------------- */

@keyframes shr-glow {

    0%,
    100% {
        text-shadow: 0 0 0 rgba(46, 204, 113, 0);
        transform: scale(1);
    }

    50% {
        text-shadow: 0 0 5px rgba(46, 204, 113, 0.45);
        transform: scale(1.06);
        color: #2ecc71;
    }
}

/* #btn-share:hover i.fa-share-alt {
    animation: shr-glow 1.2s ease-in-out infinite;
} */

/* ----------------------------------------------------------------
   Add
   ---------------------------------------------------------------- */

#btn-add-folder:hover i.fa-plus {
    color: #6c9edc;
    transition: color 0.2s ease;
}

/* Delete bin animation */
#btn-pfr-delete-folder:hover i.fa-trash {
    animation: del-lid-open 1.6s cubic-bezier(0.34, 1.56, 0.64, 1) infinite;
    color: #ef6b6b;
}

/* Share pulse/glow */
#btn-pfr-share:hover i.fa-share {
    animation: shr-glow 1.2s ease-in-out infinite;
}

/* Add folder hover */
#btn-pfr-add-folder:hover i.fa-plus {
    color: #6c9edc;
    transition: color 0.2s ease;
}

/* Documents Animations */


/* Global Add and Delete Buttons */
/* ================================
   ADD / PLUS BUTTONS → GREEN GLOW
   ================================ */

.tree-header .dt-button:hover i.fa-plus,
.tree-header .dt-button:hover i.fa-plus-circle,
.dt-button:hover i.fa-plus {
    color: #4b8fef !important;
    text-shadow:
        0 0 6px rgba(75, 143, 239, 0.6),
        /* was green */
        0 0 12px rgba(75, 143, 239, 0.4);
    /* was green */
    transform: scale(1.08);
}

/* ================================
   DELETE / TRASH → RED GLOW
   ================================ */

.tree-header .dt-button:hover i.fa-trash,
.tree-header .dt-button:hover i.fa-minus,
.dt-button:hover i.fa-trash,
.dt-button:hover i.fa-minus {
    color: #ef6b6b !important;
    text-shadow:
        0 0 6px rgba(239, 107, 107, 0.7),
        0 0 12px rgba(239, 107, 107, 0.4);
    transform: scale(1.08);
}

/* Global Add and Delete Buttons */
/* Binoculars updated: */
/* =========================================================
   GLOBAL BINOCULAR → LIST PICKER (CLEAN VERSION)
   ========================================================= */

.input-group-addon {
    display: table-cell !important;
    vertical-align: middle;
    text-align: center;
    cursor: pointer;
    padding: 0 8px;
    min-width: 36px;
    overflow: visible;
    line-height: 1;
    border: 1px solid rgba(100, 140, 170, 0.30);
    border-left: none;
    border-radius: 0 4px 4px 0;
    background: linear-gradient(160deg,
            rgba(220, 232, 242, 0.90) 0%,
            rgba(195, 215, 232, 0.75) 50%,
            rgba(175, 200, 222, 0.65) 100%);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow:
        inset 0 1px 1px rgba(255, 255, 255, 0.80),
        inset 0 -1px 1px rgba(0, 0, 0, 0.06);
    transition: background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.input-group-addon.hidden,
.input-group-addon[hidden] {
    display: none !important;
}

.input-group-addon i.fa-binoculars,
.input-group-addon i.fa-grid-search {
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    font-size: 0 !important;
    width: 22px;
    height: 22px;
    position: relative;
}

.input-group-addon i.fa-grid-search {
    width: 28px;
}

.input-group-addon i.fa-binoculars::before {
    content: "" !important;
    display: block;
    width: 22px;
    height: 22px;
    min-width: 22px;
    background-color: #3a5468;
    -webkit-mask-image: url("/ignite/icons/listPicker-66a51ceb544a32dc32cd55baf6ba5036.svg");
    mask-image: url("/ignite/icons/listPicker-66a51ceb544a32dc32cd55baf6ba5036.svg");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
    transition: background-color 0.25s ease, transform 0.25s ease;
}


.input-group-addon i.fa-grid-search::before {
    content: "" !important;
    display: block;
    width: 28px;
    height: 22px;
    min-width: 28px;
    background-color: #3a5468;
    -webkit-mask-image: url("/ignite/icons/gridPicker-37e56fbe09526e137f3d4961f88a5207.svg");
    mask-image: url("/ignite/icons/gridPicker-37e56fbe09526e137f3d4961f88a5207.svg");
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
    transition: background-color 0.25s ease, transform 0.25s ease;
}

.input-group-addon:hover {
    background: linear-gradient(160deg,
            rgba(235, 245, 255, 0.98) 0%,
            rgba(210, 228, 245, 0.92) 50%,
            rgba(190, 215, 238, 0.85) 100%);
    border-color: rgba(80, 130, 170, 0.50);
    box-shadow:
        inset 0 1px 2px rgba(255, 255, 255, 0.95),
        inset 0 -1px 2px rgba(0, 0, 0, 0.08),
        inset 0 0 12px rgba(255, 255, 255, 0.40);
}

.input-group-addon:hover i.fa-binoculars::before,
.input-group-addon:hover i.fa-grid-search::before {
    background-color: #2ecc71;
    transform: scale(1.08);
}

.input-group-addon:active i.fa-binoculars::before,
.input-group-addon:active i.fa-grid-search::before {
    transform: scale(0.95);
    background-color: #27ae60;
}

body.dark-mode .input-group-addon {
    background: linear-gradient(160deg,
            rgba(255, 255, 255, 0.14) 0%,
            rgba(255, 255, 255, 0.07) 50%,
            rgba(255, 255, 255, 0.04) 100%);
    border-color: rgba(255, 255, 255, 0.16);
    box-shadow:
        inset 0 1px 1px rgba(255, 255, 255, 0.18),
        inset 0 -1px 1px rgba(0, 0, 0, 0.20);
}

body.dark-mode .input-group-addon i.fa-binoculars::before,
body.dark-mode .input-group-addon i.fa-grid-search::before {
    background-color: #c8d8e8;
}

body.dark-mode .input-group-addon:hover {
    background: linear-gradient(160deg,
            rgba(255, 255, 255, 0.22) 0%,
            rgba(255, 255, 255, 0.12) 50%,
            rgba(255, 255, 255, 0.07) 100%);
    border-color: rgba(255, 255, 255, 0.28);
}

body.dark-mode .input-group-addon:hover i.fa-binoculars::before,
body.dark-mode .input-group-addon:hover i.fa-grid-search::before {
    background-color: #2ecc71;
}

/* Binoculars updated: */
/* Testing */
/* =========================================================
   FIX: DataTables SVG BUTTON RENDERING (GLOBAL)
   ========================================================= */
/* Ensure SVG text inside DataTables buttons is always visible */
.dt-button {
    overflow: visible !important;
}

/* Prevent DataTables from constraining SVG containers */
.dt-button span {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    height: 100% !important;
    width: 100% !important;
}

/* Force SVGs to actually render */
.dt-button svg {
    display: block !important;
    width: 16px !important;
    height: 16px !important;
    overflow: visible !important;
}

/* Ensure icon inherits color properly — but NOT on hover, so animations fire */
.dt-button:not(:hover) svg,
.dt-button:not(:hover) i {
    color: currentColor;
}

/* Prevent flex collapse issues in DataTables toolbar */
.dt-buttons {
    align-items: center !important;
}

/* Optional: fix tiny misalignment in button centering */
.dt-buttons .dt-button {
    line-height: 1 !important;
}

/* Folder overright: */


/* ── Global unified scrollbar ─────────────────── */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(160, 185, 210, 0.45) transparent;
}

*::-webkit-scrollbar {
    width: 5px;
    height: 5px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background: rgba(160, 185, 210, 0.45);
    border-radius: 4px;
}

*::-webkit-scrollbar-thumb:hover {
    background: rgba(120, 160, 200, 0.65);
}

/* ── Global unified scrollbar ─────────────────── */


/* Updated Lamp Nav Styling */

/* Updated Lamp Nav Styling */

/* The one non-DataTables grid on the Defaults tab. Its height comes from
   ig-viewport-scroll in tabViewportFit.css; this keeps it from scrolling sideways. */
#pdNonWorkingDaysBox {
    overflow-y: auto;
     overflow-x: hidden;
}


/* Folder Register tree: the rest of its sizing comes from ig-viewport-scroll. */
#pfrFolderTreePanel {
    overflow-x: hidden;
}

/* ─────────────────────────────────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════
   Active Nav Item — Strong Blue Glow
   ═══════════════════════════════════════════════════ */

/* ── Active Nav Item — subtle blue accent ── */
#sidebar li.nav-active-item>a,
#sidebar li.nav-active-item>a:focus {
    background: linear-gradient(90deg,
            rgba(75, 143, 239, 0.18) 0%,
            rgba(75, 143, 239, 0.06) 70%,
            transparent 100%) !important;
    color: #CDF0F2 !important;
    border-left: 3px solid #4b8fef !important;
    padding-left: 18px !important;
    box-shadow: inset 0 0 12px rgba(75, 143, 239, 0.08) !important;
    text-shadow: none !important;
    animation: none !important;
}

#sidebar li.nav-active-item>a i {
    color: #7ab8f5 !important;
    filter: none !important;
    transform: none !important;
}


/* ══════════════════════════════════════════════════════════════
   Deliverables table — multi-select mode
   Checkboxes (DT Select column) are hidden by default and only
   revealed when the "Select" toggle button is activated.
   ══════════════════════════════════════════════════════════════ */

/* Hide DT Select checkbox column by default */
#delivDeliverablesTable thead th.dt-select,
#delivDeliverablesTable tbody td.dt-select,
#delivDeliverablesTable thead th:has(> input[type="checkbox"]),
#delivDeliverablesTable tbody td:has(> input[type="checkbox"]) {
    display: none !important;
}

/* Show them when multi-select mode is active */
#delivDeliverablesTable_wrapper.deliiv-multi-mode #delivDeliverablesTable thead th.dt-select,
#delivDeliverablesTable_wrapper.deliiv-multi-mode #delivDeliverablesTable tbody td.dt-select,
#delivDeliverablesTable_wrapper.deliiv-multi-mode #delivDeliverablesTable thead th:has(> input[type="checkbox"]),
#delivDeliverablesTable_wrapper.deliiv-multi-mode #delivDeliverablesTable tbody td:has(> input[type="checkbox"]) {
    display: table-cell !important;
}

/* "Select" toggle button active state — highlights using the glass family */
.deliiv-multi-select-dt-btn.deliiv-multi-btn-active,
.dt-button.deliiv-multi-select-dt-btn.deliiv-multi-btn-active {
    background: linear-gradient(160deg,
            rgba(195, 221, 248, 0.95) 0%,
            rgba(170, 205, 242, 0.88) 100%) !important;
    border-color: rgba(37, 99, 235, 0.55) !important;
    color: #1d4ed8 !important;
    box-shadow:
        inset 0 1px 1px rgba(255, 255, 255, 0.80),
        inset 0 -1px 1px rgba(0, 0, 0, 0.06),
        0 0 0 2px rgba(37, 99, 235, 0.12) !important;
}

body.dark-mode .deliiv-multi-select-dt-btn.deliiv-multi-btn-active,
body.dark-mode .dt-button.deliiv-multi-select-dt-btn.deliiv-multi-btn-active {
    background: rgba(33, 150, 243, 0.22) !important;
    border-color: #42a5f5 !important;
    color: #90caf9 !important;
}



/* ═══════════════════════════════════════════════════════════
  Alert styling for error dialog
═══════════════════════════════════════════════════════════ */
/* ── ezAlerts dialog — selectable text + dark mode ── */

#ezAlerts .modal-body,
#ezAlerts #ezAlerts-message,
#ezAlerts #ezAlerts-message * {
    user-select: text !important;
    -webkit-user-select: text !important;
    cursor: auto;
}

/* monospace error detail block */
#ezAlerts #ezAlerts-message div[style*="monospace"] {
    cursor: text;
}

/* ── Dark mode ── */
body.dark-mode #ezAlerts .modal-content {
    background-color: #0d1526 !important;
    border: 1px solid #1e2a3d !important;
    border-radius: 12px !important;
    box-shadow:
        0 0 0 1px rgba(77, 142, 240, 0.06),
        0 24px 64px rgba(0, 0, 0, 0.75) !important;
    color: #f0f4f8 !important;
}

body.dark-mode #ezAlerts .modal-body {
    background-color: #0d1526 !important;
    color: #e2e8f0 !important;
}

body.dark-mode #ezAlerts .modal-footer {
    background: linear-gradient(180deg, #0d1526 0%, #0a1020 100%) !important;
    border-top: 1px solid #1e2a3d !important;
}

/* Icon circles — adapt bg/border to dark */
body.dark-mode #ezAlerts .modal-body div[style*="rgba(220,60,60"] {
    background: rgba(220, 60, 60, 0.22) !important;
    border-color: rgba(220, 60, 60, 0.45) !important;
}

body.dark-mode #ezAlerts .modal-body div[style*="rgba(240,160,20"] {
    background: rgba(240, 160, 20, 0.18) !important;
    border-color: rgba(240, 160, 20, 0.42) !important;
}

/* Monospace error detail — readable on dark */
body.dark-mode #ezAlerts .modal-body div[style*="monospace"] {
    background: rgba(255, 255, 255, 0.06) !important;
    border-color: rgba(255, 255, 255, 0.10) !important;
    color: #c8d4e4 !important;
    opacity: 1 !important;
}

/* ── Group pills ─────────────────────────────────────────────────── */
#projectDashboardTab .db-group-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    border-radius: 10px;
    padding: 6px 12px;
    white-space: nowrap;
    box-shadow: 0 1px 4px rgba(16, 38, 73, 0.08);
    border: 1.5px solid;
}

#projectDashboardTab .db-group-pill-name {
    font-size: 11px;
    font-weight: 700;
    color: var(--db-text, #2c3e50);
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

#projectDashboardTab .db-group-pill-tag {
    font-size: 9px;
    padding: 1px 7px;
    border-radius: 8px;
    color: #fff;
    font-weight: 600;
}

#projectDashboardTab .db-group-pill-count {
    font-size: 9px;
    font-weight: 700;
    color: var(--db-muted-text, #8a96a3);
    background: rgba(255, 255, 255, 0.7);
    border-radius: 8px;
    padding: 1px 6px;
}

/* ── Service discipline chips ────────────────────────────────────── */
#projectDashboardTab .db-sd-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border-radius: 8px;
    padding: 5px 10px;
    font-size: 10px;
    font-weight: 600;
    color: var(--db-text, #2c3e50);
    white-space: normal;
    word-break: break-word;
    max-width: 180px;
    border: 1.5px solid;
    box-shadow: 0 1px 2px rgba(16, 38, 73, 0.05);
    transition: box-shadow 0.15s ease;
}

#projectDashboardTab .db-sd-chip:hover {
    box-shadow: 0 4px 10px rgba(16, 38, 73, 0.12);
}

#projectDashboardTab .db-sd-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* ── Card-header search inputs ───────────────────────────────────── */
#projectDashboardTab .db-card-search {
    height: 24px;
    font-size: 11px;
    width: 120px;
    border-radius: 12px;
    padding: 1px 10px;
}

/* ── Under-development panel tabs ────────────────────────────────── */
#projectDashboardTab .db-dev-tabs {
    display: flex;
    gap: 3px;
    background: var(--db-muted-bg, #eef2f7);
    border-radius: 14px;
    padding: 2px;
}

#projectDashboardTab .db-dev-tabs button {
    border: none;
    background: transparent;
    font-size: 10px;
    font-weight: 600;
    color: #6c7a89;
    border-radius: 12px;
    padding: 3px 8px;
    cursor: pointer;
    white-space: nowrap;
    display: flex;
    align-items: center;
    gap: 4px;
}

#projectDashboardTab .db-dev-tabs button.active {
    background: #fff;
    color: #2c3e50;
    box-shadow: 0 1px 3px rgba(16, 38, 73, 0.12);
}

#projectDashboardTab .db-dev-tabs .db-dev-count {
    font-size: 8.5px;
    font-weight: 700;
    background: rgba(108, 122, 137, 0.14);
    border-radius: 8px;
    padding: 0 5px;
    min-width: 14px;
    text-align: center;
}

#projectDashboardTab .db-dev-tabs button.active .db-dev-count {
    background: #eaf1fb;
    color: #4b8fef;
}

#projectDashboardTab .db-cp-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 8.5px;
    font-weight: 700;
    letter-spacing: 0.3px;
    color: #8e3b62;
    background: #fde2ee;
    border: 1px solid #f6b9d6;
    border-radius: 8px;
    padding: 1px 6px;
    text-transform: uppercase;
    flex-shrink: 0;
}

/* ── My Deliverables status chips ────────────────────────────────── */
#projectDashboardTab .db-chip-row {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    align-items: center;
}

#projectDashboardTab .db-chip-row button {
    border: 1px solid var(--db-card-border, #dfe6ef);
    background: #fff;
    color: #5b6b7c;
    font-size: 10px;
    font-weight: 600;
    border-radius: 12px;
    padding: 2px 10px;
    cursor: pointer;
    transition: all 0.12s ease;
}

#projectDashboardTab .db-chip-row button:hover {
    border-color: #aac4ea;
}

#projectDashboardTab .db-chip-row button.active {
    background: #4b8fef;
    border-color: #4b8fef;
    color: #fff;
}

/* ── Progress stat bar ───────────────────────────────────────────── */
#projectDashboardTab .db-stat-progress-track {
    height: 5px;
    border-radius: 3px;
    background: #edf1f6;
    overflow: hidden;
    margin-top: 4px;
    width: 110px;
}

#projectDashboardTab .db-stat-progress-fill {
    height: 100%;
    border-radius: 3px;
    background: linear-gradient(90deg, #90E3DE, #A5D6A7);
    transition: width 0.4s ease;
    width: 0;
}

/* ── Stat cards row ──────────────────────────────────────────────── */
/* Single line, side-scroll, never clip. Cards keep their natural
   content width. Wraps only below 560px (phone width). */
#projectDashboardTab #dbStatCards {
    display: flex;
    flex-wrap: nowrap;
    gap: 10px;
    padding: 12px 0 8px;
    overflow-x: auto;
    overflow-y: hidden;
    align-items: stretch;
}

#projectDashboardTab #dbStatCards .db-stat-card {
    --db-stat-accent-edge: color-mix(in srgb, var(--db-stat-accent, #CBDFF3) 70%, #000);
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    border-radius: 8px;
    padding: 12px 18px 12px 26px;
    /* Same outline as .db-card on three sides; the accent bar is the left side. */
    border: 1px solid var(--ig-border-light, #dee2e6);
    border-left-width: 0;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
    box-sizing: border-box;
    min-width: 0;
}

/* Left cap, like .gantt-bar-stage-strip-outer. Sits on the card's outer edge and covers its top and bottom border rows, so the two outlines meet with no seam. Width must stay >= the 8px radius. */
#projectDashboardTab #dbStatCards .db-stat-card::before {
    content: "";
    position: absolute;
    top: -1px;
    bottom: -1px;
    left: 0;
    width: 8px;
    box-sizing: border-box;
    border: 1px solid var(--db-stat-accent-edge);
    border-radius: 8px 0 0 8px;
    background: var(--db-stat-accent, #CBDFF3);
}

#projectDashboardTab #dbStatCards .db-stat-card .db-stat-label {
    font-size: 10px;
    color: #8a96a3;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    white-space: nowrap;
}

#projectDashboardTab #dbStatCards .db-stat-card .db-stat-value {
    font-size: 18px;
    font-weight: 700;
    color: #2c3e50;
    line-height: 1.2;
    white-space: nowrap;
}

#projectDashboardTab #dbStatCards .db-stat-card .db-stat-baseline {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: nowrap;
}

#projectDashboardTab #dbStatCards .db-stat-card .db-stat-sublabel {
    font-size: 9px;
    color: #8a96a3;
    line-height: 1.2;
    white-space: nowrap;
}

#projectDashboardTab #dbStatCards .db-stat-card .db-stat-icon {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    font-size: 15px;
}

@media (max-width: 560px) {
    #projectDashboardTab #dbStatCards {
        flex-wrap: wrap;
        overflow-x: visible;
    }

    #projectDashboardTab #dbStatCards .db-stat-card {
        padding: 9px 12px 9px 20px;
    }

    #projectDashboardTab #dbStatCards .db-stat-progress-track {
        display: none;
    }
}




/* ══════════════ MODULE 4 START — Signature canvas background ══════════════ */

body:not(.dark-mode),
body:not(.dark-mode) section.wrapper,
body:not(.dark-mode) .wrapper {
    background:
        /* faint engineering-paper dot grid */
        radial-gradient(rgba(96, 125, 149, 0.055) 1px, transparent 1px) 0 0 / 26px 26px fixed,
        /* palette blooms — periwinkle top-left, mint bottom-right */
        radial-gradient(1000px 560px at 8% -6%, rgba(203, 223, 243, 0.50) 0%, transparent 62%) fixed,
        radial-gradient(900px 540px at 100% 104%, rgba(189, 236, 233, 0.36) 0%, transparent 60%) fixed,
        radial-gradient(700px 420px at 88% -4%, rgba(254, 219, 207, 0.22) 0%, transparent 55%) fixed,
        /* porcelain base */
        linear-gradient(180deg, #f7f9fc 0%, #f2f5f9 100%) fixed !important;
}

/* Content panels stay crisp on the textured canvas */
body:not(.dark-mode) .panel,
body:not(.dark-mode) .modal-content {
    background-color: #ffffff;
}

/* ══════════════ MODULE 4 END ══════════════ */

/* ══════════════ MODULE 4 START — Signature canvas background ══════════════ */

body:not(.dark-mode),
body:not(.dark-mode) section.wrapper,
body:not(.dark-mode) .wrapper {
    background:
        /* faint engineering-paper dot grid */
        radial-gradient(rgba(96, 125, 149, 0.055) 1px, transparent 1px) 0 0 / 26px 26px fixed,
        /* palette blooms — periwinkle top-left, mint bottom-right */
        radial-gradient(1000px 560px at 8% -6%, rgba(203, 223, 243, 0.50) 0%, transparent 62%) fixed,
        radial-gradient(900px 540px at 100% 104%, rgba(189, 236, 233, 0.36) 0%, transparent 60%) fixed,
        radial-gradient(700px 420px at 88% -4%, rgba(254, 219, 207, 0.22) 0%, transparent 55%) fixed,
        /* porcelain base */
        linear-gradient(180deg, #f7f9fc 0%, #f2f5f9 100%) fixed !important;
}

/* Content panels stay crisp on the textured canvas */
body:not(.dark-mode) .panel,
body:not(.dark-mode) .modal-content {
    background-color: #ffffff;
}

/* ══════════════ MODULE 4 END ══════════════ */

/* ══════════════ MODULE 5 START — Panels & tables card language ══════════════ */

/* ── Panels as floating cards ── */
body:not(.dark-mode) .panel {
    border: 1px solid rgba(96, 125, 149, 0.14);
    border-radius: 12px;
    box-shadow:
        0 1px 2px rgba(31, 45, 61, 0.05),
        0 10px 28px -14px rgba(31, 45, 61, 0.14);
    /* Was overflow: clip + clip-margin: 24px. Unlike overflow: hidden,
       overflow: clip clips EVERY descendant including position: fixed
       ones — so any Bootstrap .modal rendered inside a panel (all the
       financials dialogs live inside section.panel) was scissored to
       the panel box + 24px. Back to visible; shadows/glows fade
       naturally and dialogs escape to the viewport as intended. */
    overflow: visible;
    transition: box-shadow 0.25s ease;
}

body:not(.dark-mode) .panel:hover {
    box-shadow:
        0 1px 2px rgba(31, 45, 61, 0.06),
        0 14px 34px -14px rgba(31, 45, 61, 0.20);
}

/* ── Table headers: engineered small-caps ── */
body:not(.dark-mode) table.dataTable thead th,
body:not(.dark-mode) .table thead th {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #5c7183;
    background: #f6f8fb;
    border-bottom: 2px solid rgba(96, 125, 149, 0.18) !important;
}

/* ── Rows: whisper zebra + clear hover ── */
body:not(.dark-mode) table.dataTable tbody tr:nth-child(even)>td {
    background-color: rgba(96, 125, 149, 0.025);
}

body:not(.dark-mode) table.dataTable tbody tr:hover>td,
body:not(.dark-mode) .table tbody tr:hover>td {
    background-color: rgba(134, 173, 220, 0.10) !important;
}

body:not(.dark-mode) table.dataTable tbody td {
    border-color: rgba(96, 125, 149, 0.10);
}

/* ── Dark mode counterparts (tokens fall back safely without Module 1) ── */
body.dark-mode .panel {
    background: var(--dm-surface-1, #101a2c) !important;
    border: 1px solid var(--dm-border, rgba(148, 184, 226, 0.13)) !important;
    border-radius: 12px;
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.30),
        0 12px 32px -14px rgba(0, 0, 0, 0.50);
    /* see light-mode note: overflow: clip also clipped position: fixed
       dialogs nested inside the panel — visible instead */
    overflow: visible;
}

body.dark-mode table.dataTable thead th,
body.dark-mode .table thead th {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--dm-text-muted, #a3b5ca) !important;
    background: var(--dm-surface-2, #17233a) !important;
    border-bottom: 2px solid var(--dm-border-strong, rgba(148, 184, 226, 0.26)) !important;
}

body.dark-mode table.dataTable tbody tr:nth-child(even)>td {
    background-color: rgba(148, 184, 226, 0.035);
}

body.dark-mode table.dataTable tbody tr:hover>td,
body.dark-mode .table tbody tr:hover>td {
    background-color: rgba(91, 155, 245, 0.12) !important;
}

body.dark-mode table.dataTable tbody td {
    border-color: var(--dm-border, rgba(148, 184, 226, 0.10)) !important;
}

/* ══════════════ MODULE 5 END ══════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   IG-BANNER — global inline message standard
   One component, four intents. JS never sets colours; it only swaps the
   intent class via igShowBanner(). Dark mode overrides live in darkMode.css.
   ═══════════════════════════════════════════════════════════════════════════ */
.ig-banner {
    display: none;
    align-items: flex-start;
    gap: 8px;
    margin-bottom: 8px;
    padding: 8px 12px;
    border: 1px solid transparent;
    border-left-width: 3px;
    border-radius: 6px;
    font-size: 12px;
    line-height: 1.45;
    transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}

.ig-banner>i {
    align-self: center;
    flex-shrink: 0;
}

.ig-banner .ig-banner-sub {
    display: block;
    margin-top: 3px;
    font-size: 11px;
    opacity: .75;
}

.ig-banner--info {
    background: #eef6fb;
    border-color: #cfe6f3;
    border-left-color: #2b7db8;
    color: #1d5a85;
}

.ig-banner--warning {
    background: #fdf6e6;
    border-color: #f4e2b5;
    border-left-color: #d99a1b;
    color: #7a5a10;
}

.ig-banner--error {
    background: #fdecea;
    border-color: #f6c9c2;
    border-left-color: #d93025;
    color: #8a2018;
}

.ig-banner--success {
    background: #edf7ef;
    border-color: #cfe8d4;
    border-left-color: #2e8b46;
    color: #1e5c30;
}


/* ── Inner panels/fieldsets inside a dialog: give the content air.
      This is the fix for inputs sitting flush against the panel border. ── */
body:not(.dark-mode) .modal-body section.panel,
body:not(.dark-mode) .modal-body .panel {
    padding: 18px 20px !important;
    border: 1px solid rgba(96, 125, 149, 0.12) !important;
    border-radius: 10px !important;
    box-shadow: none !important;
    /* flat inside the dialog card */
    margin-bottom: 14px !important;
}

body:not(.dark-mode) .modal-body fieldset {
    padding: 2px 2px 6px !important;
    /* small inset inside the panel */
    border: none !important;
    min-inline-size: unset;
}

/* If a fieldset is the bordered element itself (no .panel wrapper) */
body:not(.dark-mode) .modal-body>form>fieldset,
body:not(.dark-mode) .modal-body>fieldset {
    padding: 16px 18px !important;
}


/* ═══════════════════════════════════════════════════════════════════════
   MODULE — "Ignite Glass" Dialogs  (v3 · consistency pass)
   ─────────────────────────────────────────────────────────────────────────
   One dialog language for BOTH themes, aligned to the CARD language the
   rest of the system already speaks (.panel-body / .dataTables_wrapper):
   12px radius · flat panel surface · solid 2px accent line · one blur.

   v3 changes (vs v2)
   · Radius 16px → 12px to MATCH the card system (Module 5 panels).
   · Accent stripe: gradient → solid 2px line, same weight and colour
     family as the .dataTables_wrapper top border. Still painted as an
     inset ::before (not border-top) so it hugs the clipped radius with
     zero sliver.
   · Body wash REMOVED — flat var(--ig-panel), same as every card.
     The radial-gradient wash + light bloom read as a second design
     system sitting inside the first. Gone.
· Blur (v4): ONE backdrop-filter, on .modal-content only. The v3.2
     removal was right for header/footer/inner-panels — over the dialog's
     own surface there is nothing to blur, and stacking filters costs
     compositing layers — and that still holds: they stay filter-free.
     But .modal-content sits over the scrim + live page, so IT gets the
     .ig-liquid-glass recipe (ignite.css ~1913), verbatim. Everything
     inside it — body, section.panel, .panel-body — is TRANSPARENT so
     the dialog reads as one continuous pane. Header/footer tint the
     glass (translucent gradients); controls stay opaque so the glass
     has something solid to be read against.   · Motion: spring/overshoot/filter-blur/header-stagger REMOVED.
     Plain fade + 8px rise, 0.20s in / 0.14s out. Honours
     prefers-reduced-motion.
   · Input focus: 3px halo → accent border + 2px low-opacity ring.
     Focus now reads as a border change, not padding around the field.
   · Input-groups fixed: the global .btn margin-right:3px is zeroed
     inside groups, joined corners squared, heights locked to
     --ig-control-h so inputs and buttons sit flush (the "gap" bug).
   · select option colours set explicitly — options were inheriting
     the readonly grey from the control (the grey bar in the Type
     dropdown).

   Load order: append to ignite.css AFTER MODULE 5 and make sure ignite.css
   loads after darkMode.css, or paste this at the very end of your bundle.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Shared dialog tokens ───────────────────────────────────────────── */
:root {
    --igd-radius: 12px;
    /* = card radius     */
    --igd-accent: var(--ig-stage-1, #3B82C4);
    --igd-hairline: rgba(96, 125, 149, 0.16);
    /* Flat, NOT a gradient. --igd-header-bg cannot be transitioned to a
       solid colour, so anything that animates idle->active uses this.
       Shared by .ig-segmented and every idle nav tab. */
    --igd-wash: rgba(240, 245, 251, 0.75);
    --igd-header-bg: linear-gradient(180deg, rgba(238, 244, 251, 0.55) 0%, rgba(246, 249, 253, 0.35) 100%);
    --igd-footer-bg: linear-gradient(0deg, rgba(238, 244, 251, 0.55) 0%, rgba(248, 250, 253, 0.35) 100%);
    /* glass CHROME: the button material's cool
                            212° gradient, fully opaque — glass look
                            without backdrop-filter (see v3.2 note) */
    --igd-ink: var(--ig-glass-ink, #2c465e);
    --igd-ink-strong: var(--ig-glass-ink-hover, #1b3550);
    --igd-input-border: rgba(96, 125, 149, 0.30);
    --igd-input-focus: 0 0 0 2px rgba(59, 130, 196, 0.16);
    /* = .ig-liquid-glass shadow, verbatim: top-highlight inset,
       bottom-seat inset, tight ambient, wide soft drop */
    /* v5 edge-light: the thinner glass sells itself through its edges —
       the sharp top inset stays, the outer drop goes slightly wider and
       softer so the more transparent card still visibly floats. */
    --igd-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.90),
        inset 0 -1px 1px rgba(31, 45, 61, 0.05),
        0 2px 8px rgba(31, 45, 61, 0.08),
        0 18px 44px -12px rgba(31, 45, 61, 0.28);
    /* THE pour: one token pair for every glass surface —
       .modal-content, .ig-liquid-glass, .gantt-tooltip and the Gantt
       popovers all reference these, so they can never drift apart.
       v5: 0.74/0.58 → 0.60/0.44 — clearer, less frosted. */
    --igd-pop-bg: linear-gradient(160deg,
            rgba(255, 255, 255, 0.60) 0%,
            rgba(244, 248, 252, 0.44) 100%);
    --igd-pop-border: rgba(255, 255, 255, 0.55);
}

body.dark-mode {
    --igd-accent: #4d8ef0;
    --igd-hairline: rgba(148, 184, 226, 0.14);
    --igd-wash: rgba(148, 184, 226, 0.07);
    --igd-header-bg: linear-gradient(180deg, rgba(28, 42, 64, 0.60) 0%, rgba(24, 34, 52, 0.40) 100%);
    --igd-footer-bg: linear-gradient(0deg, rgba(26, 39, 64, 0.60) 0%, rgba(22, 31, 48, 0.40) 100%);
    --igd-ink: #c8d8f0;
    --igd-ink-strong: #f0f4f8;
    --igd-input-border: rgba(148, 184, 226, 0.24);
    --igd-input-focus: 0 0 0 2px rgba(112, 168, 224, 0.22);
    --igd-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.10),
        inset 0 -1px 1px rgba(0, 0, 0, 0.30),
        0 2px 8px rgba(0, 0, 0, 0.35),
        0 20px 48px -12px rgba(0, 0, 0, 0.60);
    /* v5: 0.78/0.68 → 0.64/0.52 — clearer dark glass */
    --igd-pop-bg: linear-gradient(160deg,
            rgba(44, 52, 64, 0.64) 0%,
            rgba(30, 37, 47, 0.52) 100%);
    --igd-pop-border: rgba(255, 255, 255, 0.13);
}

/* ═══════════════════════════════════════════════════════════════════════
   1. Backdrop — light frost, both themes, same recipe
   ═══════════════════════════════════════════════════════════════════════ */

.modal-backdrop.in,
.modal-backdrop.show {
    /* v4: 0.28 → 0.20. The scrim shows THROUGH the glass now (the
       whole body is translucent, not just the chrome), so at 0.28 it
       muddied the show-through. 0.20 keeps enough dimming that the
       dialog reads modal over dense tables; the Gantt key popover has
       NO scrim behind it, which is why it reads so clean — a dialog
       still needs some for focus, so we don't go to zero. */
    background: rgba(21, 34, 49, 0.16) !important;
    /* v5: 0.20 → 0.16 — the clearer glass shows more scrim through,
       so the scrim itself backs off a notch. */
    opacity: 1 !important;
    transition: opacity 0.20s ease;
}

body.dark-mode .modal-backdrop.in,
body.dark-mode .modal-backdrop.show {
    /* 0.44 → 0.34: the dark glass gradient is already 0.68–0.78
       opaque; anything above ~0.35 behind it makes the page
       show-through invisible. */
    background: rgba(2, 6, 14, 0.28) !important;
    /* v5: 0.34 → 0.28, matching the thinner dark pour. */
}

/* ═══════════════════════════════════════════════════════════════════════
   2. Shell & content card — same surface as .panel-body
   ═══════════════════════════════════════════════════════════════════════ */

.modal-dialog {
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
}

.modal-content {
    position: relative;
    background-color: transparent !important;
    /* v5: pour comes from the shared token — one recipe for dialogs,
       popovers, tooltips and .ig-liquid-glass panels. Clearer glass:
       thinner pour, blur 20 → 14, saturate 1.7 → 1.8. */
    background-image: var(--igd-pop-bg) !important;
    backdrop-filter: blur(14px) saturate(1.8);
    -webkit-backdrop-filter: blur(14px) saturate(1.8);
    border: 1px solid var(--igd-pop-border) !important;
    border-radius: var(--igd-radius) !important;
    box-shadow: var(--igd-shadow) !important;

    /* overflow: visible; */
    overflow: clip;
    outline: none !important;
    isolation: isolate;
    /* keeps backdrop-filter layers local */
}

/* Accent strip — painted, not a border, so it hugs the clipped radius
   with zero sliver. 3px = the SAME strip grammar as the approval
   cards (.ab-ch-card border-top: 3px): "the coloured strip tells you
   what kind of thing this is". One grammar, cards and dialogs. */
.modal-content::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    z-index: 3;
    background: var(--igd-accent);
    pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════════════
   3. Header — quiet slab, one blur
   ═══════════════════════════════════════════════════════════════════════ */

.modal-header {
    position: relative;
    z-index: 2;
    background: var(--igd-header-bg) !important;
    border: none !important;
    border-bottom: 1px solid var(--igd-hairline) !important;
    border-radius: 0 !important;
    /* content card owns the radius     */
    padding: 14px 20px 12px !important;
    min-height: unset;
    display: flex !important;
    align-items: center !important;
    gap: 10px !important;
    color: var(--igd-ink) !important;
    text-shadow: none !important;
    /* kills legacy 0 1px #fff          */
    /* Hairline top highlight under the accent line — a 1px lift that
       reads as machined edge, not glow. Costs nothing, no layers. */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55);
}

body.dark-mode .modal-header {
    box-shadow: inset 0 1px 0 rgba(148, 184, 226, 0.10);
}

.modal-header .modal-title,
.modal-header h4.modal-title {
    color: var(--igd-ink-strong) !important;
    font-size: 16px !important;
    /* = base h4 (style.css) */
    font-weight: 300 !important;
    /* = Lato light, same as tab/section h4 */
    letter-spacing: 0.6px !important;
    text-transform: uppercase !important;
    line-height: 1.3 !important;
    text-shadow: none !important;
    margin: 0 !important;
}

.modal-header>i,
.modal-header .modal-title>i {
    color: var(--igd-accent) !important;
}

/* Close — quiet chip */
.modal-header .close {
    font-size: 18px !important;
    font-weight: 400 !important;
    line-height: 1 !important;
    width: 28px;
    height: 28px;
    display: flex !important;
    align-items: center;
    justify-content: center;
    padding: 0 !important;
    margin: 0 0 0 auto !important;
    border-radius: 50%;
    border: 1px solid transparent;
    background: transparent;
    color: var(--igd-ink) !important;
    opacity: 0.65 !important;
    text-shadow: none !important;
    transition: background 0.18s var(--ig-ease, ease),
        border-color 0.18s var(--ig-ease, ease),
        opacity 0.18s var(--ig-ease, ease);
}

.modal-header .close:hover {
    background: var(--ig-glass-grad-hover) !important;
    border-color: var(--ig-glass-border, rgba(96, 133, 168, 0.42));
    color: var(--igd-ink-strong) !important;
    opacity: 1 !important;
}

.modal-header .close:focus-visible {
    outline: none;
    box-shadow: var(--ig-focus-ring, 0 0 0 3px rgba(59, 130, 196, 0.30));
    opacity: 1 !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   4. Body — NO surface of its own (v4). The .modal-content glass is
      the surface, edge to edge; an opaque body here was the white
      rectangle that broke the pane in two. Controls stay opaque —
      that contrast is what makes the glass legible as glass.
   ═══════════════════════════════════════════════════════════════════════ */

.modal-body {
    position: relative;
    z-index: 1;
    background: transparent !important;
    padding: 16px 18px !important;
}

/* Inner cards & panels — same recipe as .panel-body (Module 5),
   NOT a new one. No blur here: they sit on a flat surface, there is
   nothing meaningful to blur (same reasoning as the buttons). */
body:not(.dark-mode) .modal-body .card,
body:not(.dark-mode) .modal-body section.panel,
body:not(.dark-mode) .modal-body .panel {
    background: transparent !important;
    /* bounds only — the glass is the surface. Hairline token, NOT white:
       white reads as a hard edge against the frosted backdrop. */
    border: 1px solid var(--igd-hairline) !important;
    border-radius: var(--igd-radius) !important;
    /* faint top catch-light keeps the glass feel without a white ring */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35) !important;
}

body.dark-mode .modal-body .card,
body.dark-mode .modal-body section.panel,
body.dark-mode .modal-body .panel {
    background: transparent !important;
    border: 1px solid var(--igd-hairline) !important;
    border-radius: var(--igd-radius) !important;
    box-shadow: none !important;
}

/* Panels inside a glass dialog carry no surface of their own — the
   .modal-content glass is the surface. */
body:not(.dark-mode) .modal-body .panel-body {
    background-color: transparent !important;
    border-color: var(--igd-hairline) !important;
}

body.dark-mode .modal-body .panel-body {
    background-color: transparent !important;
    border-color: rgba(255, 255, 255, 0.08) !important;
}

/* Section h4 inside dialogs — SAME voice as the tab pages: the base
   heading rule (style.css: Lato, 300, uppercase, bootstrap 18px).
   No size/weight override here; only the ink, so dialog and tab
   headings read identically. .modal-title (the dialog TITLEBAR) is
   separate chrome and keeps its own .modal-header rules. */
.modal-body h4 {
    color: var(--igd-ink);
    margin-bottom: 10px;
    /* Stated, not inherited. The note above says these should take the base
       h4 voice (Lato 300, uppercase) and set only the ink -- but something
       later in the cascade is handing them a bold, mixed-case default, so
       the SD picker's section headings rendered heavier than every other
       dialog's. Naming the two properties here is what makes the intent in
       that note actually hold. .modal-title is separate chrome and keeps its
       own .modal-header rules. */
    font-weight: 300;
    text-transform: uppercase;
}


.modal-body label {
    font-size: 11.5px;
    font-weight: 500 !important;
    /* = tab label typography (ignite
                                         .css nav-tabs). Bold is RESERVED
                                         for mandatory fields only.     */
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--igd-ink);
    opacity: 0.8;
    margin-bottom: 5px;
}

/* Mandatory — the one place bold is allowed. Full ink, no fade. */
.modal-body label.form-label-mandatory,
.modal-body .form-label-mandatory {
    font-weight: 700 !important;
    color: var(--igd-ink-strong) !important;
    opacity: 1 !important;
}

.modal-body label span[style*="red"],
.modal-body label .required {
    color: #c25050 !important;
    opacity: 1;
}

/* Dark mode labels — darkMode.css pins these to #6a7d94 !important,
   which is ALSO its readonly-value colour, so labels and greyed data
   were identical and near-invisible. Re-pin one step brighter.
   Brightness ladder: entered text #e8edf5 › labels #a9bcd4 › readonly
   #6a7d94. Weight matches the tab labels (500); bold is reserved for
   mandatory fields, same rule as light mode. */
body.dark-mode .modal-body label,
body.dark-mode .modal-body .col-form-label,
body.dark-mode .modal-body .control-label {
    color: #a9bcd4 !important;
    font-weight: 500 !important;
    opacity: 1 !important;
}

body.dark-mode .modal-body label.form-label-mandatory,
body.dark-mode .modal-body .form-label-mandatory {
    color: #d4e2f4 !important;
    /* brighter than plain labels,
                                         still below entered text      */
    font-weight: 700 !important;
    opacity: 1 !important;
}

/* ── Inputs ──────────────────────────────────────────────────────────
   4px radius = the legacy corner the untouched input-group addons use
   (ignite.css .input-group-text / .input-group-addon), so fields look
   identical alone or grouped. 34px height = --ig-control-h so inputs
   and buttons always align. */
.modal-body .form-control,
.modal-body .form-control-mandatory {
    height: var(--ig-control-h, 34px);
    border: 1px solid var(--igd-input-border);
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.92);
    box-shadow: inset 0 1px 2px rgba(31, 45, 61, 0.05);
    transition: border-color 0.18s var(--ig-ease, ease),
        box-shadow 0.18s var(--ig-ease, ease),
        background 0.18s var(--ig-ease, ease);
}

body:not(.dark-mode) .modal-body .form-control:hover:not([readonly]):not([disabled]) {
    border-color: rgba(66, 112, 156, 0.45);
}

/* Focus — border change + faint 2px ring. NOT the fat 3px halo:
   at input scale that ring read as padding around the field. */
.modal-body .form-control:focus,
.modal-body .form-control-mandatory:focus {
    border-color: var(--igd-accent) !important;
    background-color: #ffffff;
    box-shadow: var(--igd-input-focus) !important;
    outline: none !important;
}

body.dark-mode .modal-body .form-control:focus,
body.dark-mode .modal-body .form-control-mandatory:focus {
    background-color: rgba(255, 255, 255, 0.06);
}

/* Readonly / disabled — softer than bootstrap's #e9ecef slab */
body:not(.dark-mode) .modal-body .form-control[readonly],
body:not(.dark-mode) .modal-body .form-control[disabled] {
    background: rgba(96, 125, 149, 0.08);
    border-color: var(--igd-input-border);
    box-shadow: none;
    color: var(--igd-ink);
}

/* Select options — set explicitly. Options were INHERITING the
   readonly grey from the control (the grey bar in the Type list). */
.modal-body select.form-control {
    background-color: rgba(255, 255, 255, 0.92);
}

.modal-body select.form-control option {
    background-color: #ffffff;
    color: #2c3e50;
}

body.dark-mode .modal-body select.form-control option {
    background-color: #1a222c;
    color: #c8d8f0;
}

/* ── Input groups ────────────────────────────────────────────────────
   v3.6: ALL join/rounding geometry REMOVED by design. The legacy
   Bootstrap 3 table layout + ignite.css addon rules own input-groups
   again, untouched. Less code, no corner gymnastics, nothing to fight. */

/* ═══════════════════════════════════════════════════════════════════════
   5. Footer — quiet shelf, buttons right, same blur as header
   ═══════════════════════════════════════════════════════════════════════ */

.modal-footer,
.fixed-dialog-footer {
    position: relative;
    z-index: 2;
    background: var(--igd-footer-bg) !important;
    border: none !important;
    border-top: 1px solid var(--igd-hairline) !important;
    border-radius: 0 !important;
    padding: 10px 16px !important;
    text-shadow: none !important;
    display: flex !important;
    align-items: center !important;
    justify-content: flex-end !important;
    gap: 8px !important;
}

.modal-footer .btn {
    margin: 0 !important;
    /* gap owns spacing */
}

/* Push a utility button (Delete / Close) to the left edge */
.modal-footer>.btn-left,
.modal-footer>.mr-auto {
    margin-right: auto !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   6. Motion — plain fade + small rise. No spring, no overshoot, no
      filter blur, no header stagger. transform / opacity only.
   ═══════════════════════════════════════════════════════════════════════ */

@keyframes igd-dialog-in {

    /* transform ONLY — no opacity. An opacity ramp on this ancestor
       makes it the backdrop root, which blanks the .modal-content
       backdrop-filter until the ramp ends: the glass "snaps" on and
       the inner panel looks like it loads on a different schedule.
       The scrim (.modal-backdrop, 0.20s) owns the fade instead. */
    from {
        transform: translateY(10px) scale(0.985);
    }

    to {
        transform: translateY(0) scale(1);
    }
}

@keyframes igd-dialog-out {
    from {
        opacity: 1;
        transform: translateY(0);
    }

    to {
        opacity: 0;
        transform: translateY(4px);
    }
}

/* Bootstrap toggles .in/.show on open; on close the class is removed
   while .fade remains for the transition window. */

/* Kill Bootstrap 3's own .fade opacity ramp on the .modal ROOT — it
   stacks a second 0.15s linear fade under ours AND (being an opacity
   ancestor) blanks the glass blur for its duration. display:none/block
   still hides/shows the modal, so nothing flashes. */
.modal.fade {
    opacity: 1 !important;
    transition: none !important;
}

.modal.fade .modal-dialog {
    transition: none !important;
    /* keyframes own the motion */
    animation: igd-dialog-out 0.14s ease-in both;
}

.modal.fade.in .modal-dialog,
.modal.fade.show .modal-dialog {
    /* fill-mode backwards, NOT both: `both` retains transform:translateY(0)
       after the animation, which keeps the dialog on its own compositing
       layer forever — at fractional zoom that layer is resampled and
       everything inside (buttons, text) renders fuzzy. `backwards` covers
       the pre-animation frame, then releases the layer at the end. */
    animation: igd-dialog-in 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
}

@media (prefers-reduced-motion: reduce) {

    .modal.fade .modal-dialog,
    .modal.fade.in .modal-dialog,
    .modal.fade.show .modal-dialog {
        animation: none !important;
        transition: none !important;
        transform: none !important;
        opacity: 1 !important;
    }   
}

/* ═══════════════════════════════════════════════════════════════════════
   7. Semantic accents — add one class to .modal-content:
      ig-dlg-success · ig-dlg-warning · ig-dlg-danger
      Retints the 2px line + title/icon ink. The header slab stays
      neutral — the line is the signal, same as the card system.
   ═══════════════════════════════════════════════════════════════════════ */

.modal-content.ig-dlg-success {
    --igd-accent: var(--ig-stage-7, #27AE60);
}

.modal-content.ig-dlg-warning {
    --igd-accent: #E8A82C;
}

.modal-content.ig-dlg-danger {
    --igd-accent: var(--ig-stage-6, #C0392B);
}

body:not(.dark-mode) .modal-content.ig-dlg-success .modal-title,
body:not(.dark-mode) .modal-content.ig-dlg-success .modal-header>i {
    color: var(--ig-stage-7-text, #0e4d27) !important;
}

body:not(.dark-mode) .modal-content.ig-dlg-warning .modal-title,
body:not(.dark-mode) .modal-content.ig-dlg-warning .modal-header>i {
    color: #5e3c00 !important;
}

body:not(.dark-mode) .modal-content.ig-dlg-danger .modal-title,
body:not(.dark-mode) .modal-content.ig-dlg-danger .modal-header>i {
    color: var(--ig-stage-6-text, #5c1510) !important;
}

/* ══════════════ MODULE END — Ignite Glass Dialogs v3 ══════════════ */


/* Declare the app's scheme so UA-drawn controls (checkboxes, radios,
   date pickers, select arrows, scrollbars) follow OUR theme, not the OS.
   Without this, an OS-dark machine draws dark checkboxes on our light
   popovers whenever the user has overridden to light mode. */
:root {
    color-scheme: light;
}

html.dark-mode,
body.dark-mode {
    color-scheme: dark;
}

html:not(.dark-mode) input,
html:not(.dark-mode) select,
html:not(.dark-mode) textarea {
    color-scheme: light;
}


/* ═══════════════════════════════════════════════════════════════════════
   MODULE — Global Table Shell  (v1)
   ─────────────────────────────────────────────────────────────────────────
   One radius language for every table in the system. The card
   (.dataTables_wrapper) has been rounded at 12px since ignite.css:575 —
   the TABLE inside it never was, so a sharp slab sat inside a rounded
   card. This module gives the table its own hairline frame + radius so
   it reads as a nested pane, matching .panel-body / .modal-content.

   Design notes
   · Radius 10px = card radius (12px) minus the wrapper's optical inset.
     A nested surface must sit BELOW its parent's radius or the corners
     fight. Never set this equal to 12px.
   · border-collapse: separate is REQUIRED — the CSS table model ignores
     border-radius on cells under `collapse`. darkMode.css:375 already
     forces `separate` globally, so light mode is simply catching up;
     nothing new is being introduced to the box model.
   · NO new overflow:hidden anywhere. The wrapper's `overflow: visible`
     (see the note at ignite.css:583 — backdrop-filter + clipping trapped
     position:fixed dialogs, datepickers and select dropdowns inside the
     tab) is left untouched. Scrolling tables are clipped by the
     containers DataTables ALREADY clips: it writes `overflow:hidden`
     inline on .dataTables_scrollHead, and .dataTables_scrollBody is
     overflow:auto. We only add radii to those two — no new clipping,
     so no new dialog traps.
   · Borders use !important because DataTables' own stylesheet ships
     `table.dataTable.no-footer { border-bottom: 1px solid #111 }`
     (0,2,1) which outranks any sane selector we could write here.

   Load order: append to the END of ignite.css, after the Ignite Glass
   Dialogs module. ignite.css must load after darkMode.css (as it
   already does) — darkMode.css:379 sets `border: none !important` on
   every table, so the dark-mode block below re-asserts the frame at
   higher specificity.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Tokens ─────────────────────────────────────────────────────────── */
:root {
    --ig-tbl-radius: 10px;
    /* inner radius = radius minus the 1px frame, so the cell corner
       nests inside the border instead of poking through it */
    --ig-tbl-radius-inner: 9px;
    --ig-tbl-border: rgba(96, 125, 149, 0.18);
    --ig-tbl-shadow: 0 1px 2px rgba(31, 45, 61, 0.045);
}

body.dark-mode {
    --ig-tbl-border: rgba(148, 184, 226, 0.16);
    --ig-tbl-shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
}


/* ═══ 1. Geometry prerequisite ═══════════════════════════════════════
   Cell radii are inert under border-collapse:collapse. No !important —
   any local rule that genuinely needs `collapse` can still win.
   ═══════════════════════════════════════════════════════════════════ */
table.dataTable,
.panel-body .table,
.modal-body .table,
.dataTables_wrapper .table {
    border-collapse: separate;
    border-spacing: 0;
}


/* ═══ 2. The frame — non-scrolling tables ════════════════════════════
   For a table WITHOUT scrollX/scrollY, DataTables leaves <table> as a
   direct child of .dataTables_wrapper. The `>` combinator is what
   separates this case from the split scrollHead/scrollBody case in
   section 3 — do not relax it to a descendant selector.
   ═══════════════════════════════════════════════════════════════════ */
body .dataTables_wrapper>table.dataTable,
body .dataTables_wrapper>table.table,
body .panel-body>.table,
body .panel-body>div>.table,
body .modal-body .panel-body>.table {
    border: 1px solid var(--ig-tbl-border) !important;
    border-radius: var(--ig-tbl-radius);
    box-shadow: var(--ig-tbl-shadow);
}

body.dark-mode .dataTables_wrapper>table.dataTable,
body.dark-mode .dataTables_wrapper>table.table,
body.dark-mode .panel-body>.table,
body.dark-mode .panel-body>div>.table,
body.dark-mode .modal-body .panel-body>.table {
    border: 1px solid var(--ig-tbl-border) !important;
    border-radius: var(--ig-tbl-radius);
    box-shadow: var(--ig-tbl-shadow);
}


/* ═══ 3. The frame — scrolling tables (scrollX / scrollY) ════════════
   DataTables splits the table in two: .dataTables_scrollHead holds the
   <thead> clone, .dataTables_scrollBody holds the <tbody>. The frame
   therefore belongs to .dataTables_scroll (which spans both), and each
   half rounds the two corners it actually owns.
   ═══════════════════════════════════════════════════════════════════ */
body .dataTables_wrapper .dataTables_scroll {
    border: 1px solid var(--ig-tbl-border);
    border-radius: var(--ig-tbl-radius);
    box-shadow: var(--ig-tbl-shadow);
}

body.dark-mode .dataTables_wrapper .dataTables_scroll {
    border: 1px solid var(--ig-tbl-border) !important;
    border-radius: var(--ig-tbl-radius);
    box-shadow: var(--ig-tbl-shadow);
}

/* The two halves clip themselves — scrollHead via the inline
   overflow:hidden DataTables writes on it, scrollBody via the
   overflow rules at ignite.css:854 / :1514. */
body .dataTables_scroll>.dataTables_scrollHead {
    border-top-left-radius: var(--ig-tbl-radius-inner);
    border-top-right-radius: var(--ig-tbl-radius-inner);
}

body .dataTables_scroll>.dataTables_scrollBody {
    border-bottom-left-radius: var(--ig-tbl-radius-inner);
    border-bottom-right-radius: var(--ig-tbl-radius-inner);
}

/* Defensive: if a table ever grows a <tfoot>, the bottom corners move
   to the foot and the body goes square again. No <tfoot> exists in the
   codebase today; this keeps the module correct if one is added. */
body .dataTables_scroll:has(>.dataTables_scrollFoot)>.dataTables_scrollBody {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

body .dataTables_scroll>.dataTables_scrollFoot {
    overflow: hidden;
    border-bottom-left-radius: var(--ig-tbl-radius-inner);
    border-bottom-right-radius: var(--ig-tbl-radius-inner);
}

/* The inner tables must NOT carry a frame of their own — the scroll
   container is the frame. Without this you get a double hairline. */
body .dataTables_scroll table.dataTable,
body .dataTables_scroll .table,
body.dark-mode .dataTables_scroll table.dataTable,
body.dark-mode .dataTables_scroll .table {
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
}


/* ═══ 4. Corner cells ════════════════════════════════════════════════
   A <table> does not clip its cells, so the header's fill would paint
   square over the frame's corners. Each corner cell rounds itself.
   Applies to both cases: in the split layout the head table's first
   row and the body table's last row are still the visual corners.
   ═══════════════════════════════════════════════════════════════════ */
body table.dataTable>thead>tr:first-child>th:first-child,
body table.dataTable>thead>tr:first-child>td:first-child,
body .panel-body .table>thead>tr:first-child>th:first-child,
body .modal-body .table>thead>tr:first-child>th:first-child {
    border-top-left-radius: var(--ig-tbl-radius-inner);
}

body table.dataTable>thead>tr:first-child>th:last-child,
body table.dataTable>thead>tr:first-child>td:last-child,
body .panel-body .table>thead>tr:first-child>th:last-child,
body .modal-body .table>thead>tr:first-child>th:last-child {
    border-top-right-radius: var(--ig-tbl-radius-inner);
}

body table.dataTable>tbody>tr:last-child>td:first-child,
body table.dataTable>tbody>tr:last-child>th:first-child,
body .panel-body .table>tbody>tr:last-child>td:first-child,
body .modal-body .table>tbody>tr:last-child>td:first-child {
    border-bottom-left-radius: var(--ig-tbl-radius-inner);
}

body table.dataTable>tbody>tr:last-child>td:last-child,
body table.dataTable>tbody>tr:last-child>th:last-child,
body .panel-body .table>tbody>tr:last-child>td:last-child,
body .modal-body .table>tbody>tr:last-child>td:last-child {
    border-bottom-right-radius: var(--ig-tbl-radius-inner);
}

/* The empty-state cell ("Showing 0 to 0 of 0 entries", screenshot 2) is
   a single full-width td — it owns BOTH bottom corners. */
body table.dataTable>tbody>tr>td.dataTables_empty {
    border-bottom-left-radius: var(--ig-tbl-radius-inner);
    border-bottom-right-radius: var(--ig-tbl-radius-inner);
}


/* ═══ 5. Seam repair ═════════════════════════════════════════════════
   Under `collapse` the header's 2px bottom border and Bootstrap's 1px
   `.table > tbody > tr > td { border-top }` merged into one line. Under
   `separate` they stack into a 3px bar under the header. The header
   border (ignite.css:55 / :6861) is the one we want, so the first body
   row drops its top border. Row separators are unaffected — they are
   drawn with border-BOTTOM (ignite.css:95 / :102).
   ═══════════════════════════════════════════════════════════════════ */
body table.dataTable>tbody>tr:first-child>td,
body table.dataTable>tbody>tr:first-child>th,
body .panel-body .table>tbody>tr:first-child>td,
body .modal-body .table>tbody>tr:first-child>td {
    border-top: 0 !important;
}

/* Last row's own bottom rule is redundant once the frame exists —
   it would read as a double line just inside the border. */
body table.dataTable>tbody>tr:last-child>td,
body .panel-body .table>tbody>tr:last-child>td,
body .modal-body .table>tbody>tr:last-child>td {
    border-bottom: 0 !important;
}

/* Split layout: the head table sits flush against the body table, so
   the frame must not be duplicated at the seam either. */
body .dataTables_scrollHead table.dataTable {
    margin-bottom: 0 !important;
}


/* ═══ 6. Selected-row accent ═════════════════════════════════════════
   ignite.css:118 puts a 2px left bar on the selected row's first cell.
   On a rounded first/last row that bar now has to follow the corner —
   otherwise it spears past the radius. Same colour, same weight.
   ═══════════════════════════════════════════════════════════════════ */
body .table-striped>tbody>tr.selected>td:first-child {
    border-top-left-radius: inherit;
    border-bottom-left-radius: inherit;
}


/* ═══ 7. Softening the remaining sharp edges in the table chrome ══════
   The frame is only half the job — a rounded table above a square
   pager still reads as sharp. These match the 6px control radius the
   button system already uses (igniteButtons.css).
   ═══════════════════════════════════════════════════════════════════ */
body .dataTables_paginate.paging_bootstrap.pagination li:first-child a {
    border-top-left-radius: 6px;
    border-bottom-left-radius: 6px;
}

body .dataTables_paginate.paging_bootstrap.pagination li:last-child a {
    border-top-right-radius: 6px;
    border-bottom-right-radius: 6px;
}

body .dataTables_length select {
    border-radius: 6px;
}

/* ══════════════ MODULE END — Global Table Shell v1 ══════════════ */


/* ═══════════════════════════════════════════════════════════════════════
   MODULE — Global Table Shell  ·  v1.1 patch
   ─────────────────────────────────────────────────────────────────────────
   Append DIRECTLY BELOW the "MODULE END — Global Table Shell v1" line.
   Two fixes, both caused by !important rules in darkMode.css winning on
   specificity against rules that carry fewer classes.

   FIX A — the phantom row between header and first data row (dark only)
   FIX B — dark mode was flattening the v1 radius back to square
   ═══════════════════════════════════════════════════════════════════════ */


/* ═══ FIX A · Re-collapse the scroll-body sizing clone ═══════════════
   Every generic table runs scrollX + scrollY (ignite.js:1373), so
   DataTables splits it: the real <thead> goes to .dataTables_scrollHead
   and a DUPLICATE <thead> is left inside .dataTables_scrollBody purely
   as a width-measuring reference. DataTables collapses that clone with

       div.dataTables_scrollBody > table > thead > tr > th {
           height: 0 !important; padding-top: 0 !important; ... }

   — one class, so darkMode.css:385 (`body.dark-mode .table thead>tr>th`,
   TWO classes, also !important) outranks it and restores
   `padding: 10px 12px` + `border-bottom: 1px` + an opaque background.
   The invisible clone inflates into a ~21px slab. Light mode escapes
   only because ignite.css:49 sets that padding WITHOUT !important.

   The selectors below carry three classes so they win in both themes,
   and this is written theme-neutrally on purpose: the clone must never
   be visible under ANY future skin.

   CRITICAL — left/right padding is deliberately NOT touched. DataTables
   measures column widths off this clone; zeroing its horizontal padding
   desynchronises the body columns from the header columns. Only the
   vertical box is collapsed.
   ═══════════════════════════════════════════════════════════════════ */
body .dataTables_scrollBody>table>thead>tr>th,
body .dataTables_scrollBody>table>thead>tr>td,
body.dark-mode .dataTables_scrollBody>table>thead>tr>th,
body.dark-mode .dataTables_scrollBody>table>thead>tr>td {
    height: 0 !important;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    border-top-width: 0 !important;
    border-bottom-width: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
}

/* DataTables wraps the clone's cell content in this div at height 0 —
   darkMode's line-height/font-size rules can push it back open. */
body .dataTables_scrollBody>table>thead .dataTables_sizing,
body.dark-mode .dataTables_scrollBody>table>thead .dataTables_sizing {
    height: 0 !important;
    overflow: hidden !important;
}

/* The clone's row itself must not hold a border either — darkMode.css
   sets border-collapse:separate globally (:379), and under the
   separated model a row border is dropped, but a stray box-shadow or
   outline on the row would still paint. */
body .dataTables_scrollBody>table>thead>tr,
body.dark-mode .dataTables_scrollBody>table>thead>tr {
    height: 0 !important;
    box-shadow: none !important;
}


/* ═══ FIX B · Restore the v1 radius in dark mode ═════════════════════
   darkMode.css:2929 flattens both scroll halves:

       body.dark-mode .dataTables_scrollHead,
       body.dark-mode .dataTables_scrollBody { border-radius: 0 !important }

   Two classes + !important, so the v1 rules (`body .dataTables_scroll >
   .dataTables_scrollHead`, also two classes but no !important) lose.
   Result: a rounded frame drawn around square-cornered contents — the
   corners read as "almost rounded", which is worse than either extreme.
   Same selectors, one class deeper, !important to match.
   ═══════════════════════════════════════════════════════════════════ */
body.dark-mode .dataTables_scroll>.dataTables_scrollHead {
    border-top-left-radius: var(--ig-tbl-radius-inner) !important;
    border-top-right-radius: var(--ig-tbl-radius-inner) !important;
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

body.dark-mode .dataTables_scroll>.dataTables_scrollBody {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    border-bottom-left-radius: var(--ig-tbl-radius-inner) !important;
    border-bottom-right-radius: var(--ig-tbl-radius-inner) !important;
}

body.dark-mode .dataTables_scroll:has(>.dataTables_scrollFoot)>.dataTables_scrollBody {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
}

body.dark-mode .dataTables_scroll>.dataTables_scrollFoot {
    overflow: hidden !important;
    border-bottom-left-radius: var(--ig-tbl-radius-inner) !important;
    border-bottom-right-radius: var(--ig-tbl-radius-inner) !important;
}

/* ══════════════ MODULE END — Global Table Shell v1.1 ══════════════ */

/* ═══════════════════════════════════════════════════════════════════
   MODULE — Table toolbar band
   The DataTables toolbar (dt-buttons · length · filter) has no wrapper
   element of its own, so the strip is painted as a sized background
   layer on .dataTables_wrapper: full card width, fading out just below
   the controls. A fade rather than a hard edge so a wrapped toolbar
   never shows a cut-off slab. Cannot tint the whole card — odd rows
   are transparent (ignite.css Module 5) and would take the wash.
   ═══════════════════════════════════════════════════════════════════ */


.dataTables_wrapper,
body.dark-mode .dataTables_wrapper,
body.dark-mode .table-scrollable .dataTables_wrapper,
body.dark-mode .modal-body .dataTables_wrapper {
    background-image: var(--ig-tbl-toolbar-wash) !important;
    background-repeat: no-repeat !important;
    background-position: 0 0 !important;
    background-size: 100% var(--ig-tbl-toolbar-h) !important;
}


/* ═══════════════════════════════════════════════════════════════════
   MODULE — Pinned table columns  ·  .ig-pin-right / .ig-pin-left

   A column that stays visible against one edge of the table's scroll
   viewport while the other columns scroll underneath it. Same idea as
   the dialog footer: the content moves, the thing you act on doesn't.

   ── USAGE — one line, no JS ──────────────────────────────────────
   In the table's columnDefsArray:

       { className: "ig-pin-right", targets: [8] }

   DataTables applies columns.className to the header <th> AND to every
   body <td> of that column, on EVERY draw. That is why this survives
   `scroller: true` (rows are virtualised — recreated as you scroll)
   and `deferRender` with no re-sync code. It is also why this is a
   pure-CSS module: there is nothing for JS to keep in step.

   ── WHY NOT FixedColumns ─────────────────────────────────────────
   The DataTables FixedColumns plugin is NOT in the bundle. The
   .DTFC_LeftBodyWrapper / .DTFC_RightBodyWrapper rules in darkMode.css
   are dead leftovers — do not treat them as evidence it is loaded.
   FixedColumns also works by cloning the column into a second overlaid
   table, which has to be re-synced against every virtual-scroll draw.
   Sticky cells have no clone and no sync.

   ── PREREQUISITES (all already true, do not regress them) ────────
   · border-collapse: separate on table.dataTable — set by the Global
     Table Shell module above. Under `collapse`, `position` is inert on
     table cells and this module silently does nothing.
   · The scroll container must be .dataTables_scrollBody, i.e. the
     table needs scrollX (every generic table has it) AND must sit
     inside .table-scrollable — style.css pins .dataTables_scrollBody
     to overflow-x: hidden otherwise, so there is no horizontal scroll
     to pin against in the first place.

   ── THE BACKGROUND TRADE-OFF (deliberate) ────────────────────────
   A sticky cell MUST be opaque or the columns sliding beneath it show
   through. The row-stripe colours are opaque in both themes, but the
   dark-mode selected-row tint is not (rgba(75,143,239,0.12)), so
   inheriting the row background is not safe across the board. Instead
   the pinned cell owns its own opaque fill — like a frozen column in a
   spreadsheet — and the hover / selected states are restated below as
   OPAQUE equivalents so the row highlight still reads across the pin.
   The dark values are the light-mode tints composited over the dark
   row colour, computed, not invented:
       #0d1526 + rgba(75,143,239,0.12) ≈ #142440  (selected)
       #0d1526 + rgba(75,143,239,0.06) ≈ #101c33  (hover)
   ═══════════════════════════════════════════════════════════════════ */

/* ── The palette ──────────────────────────────────────────────────
   A sticky cell cannot use a translucent colour — the columns sliding
   underneath would show through it. Every row state in this app is
   painted with a TRANSLUCENT wash, so each token below is that wash
   pre-composited over whatever sits behind it. Opaque in, opaque out.

   These are read off the rules that actually WIN on a row, not off the
   first rule that mentions the state — several of them are outranked:

     odd / even  ignite.css:110 / :118  #ffffff / #f8f8fa   (0,2,3)!
     hover       MODULE 5, ignite.css:7601
                 rgba(134,173,220,0.10) (0,3,5)! — beats ignite.css:117.
                 It REPLACES the stripe colour rather than layering over
                 it, so it composites over the panel white and the
                 result is the same on odd and even rows: ONE token.
                 0.10×(134,173,220) + 0.90×white = #f3f7fb
     selected    ignite.css:90  #9ab0be (0,3,3)! — beats ignite.css:124's
                 #eeeeff (0,2,3)!. #eeeeff is what the pin used to show,
                 which is why it read as a white block on a grey row.
     sel+hover   MODULE 5 (0,3,5) outranks selected (0,3,3), washing over
                 the tr background #99acbd set at ignite.css:82:
                 0.10×(134,173,220) + 0.90×(153,172,189) = #97acc0

   If any of those source colours is ever changed, change it here too —
   that is the standing cost of a frozen column and there is no way
   around it in CSS, which cannot read a computed background. */
:root {
    --ig-pin-bg-odd: #ffffff;
    --ig-pin-bg-even: #f8f8fa;
    --ig-pin-bg-hover: #f3f7fb;
    --ig-pin-bg-sel: #9ab0be;
    --ig-pin-bg-sel-hover: #97acc0;
    --ig-pin-bg-head: #f4f7fb;
    --ig-pin-edge: rgba(96, 125, 149, 0.22);
    /* Directional: the shadow falls AWAY from the pinned edge, so the
       eye reads the pin as sitting above the scrolling columns. */
    --ig-pin-shadow-right: -6px 0 10px -6px rgba(31, 45, 61, 0.22);
    --ig-pin-shadow-left: 6px 0 10px -6px rgba(31, 45, 61, 0.22);
}

/* Dark mode, same method:
     odd / even  darkMode.css:437 / :445  #0d1526 / #0a1122  (0,3,4)!
     hover       MODULE 5 dark, ignite.css:7639
                 rgba(91,155,245,0.12) (0,3,5)! over the panel
                 --dm-surface-1 #101a2c  →  #192944
     selected    two stacked washes: tr rgba(75,143,239,0.12)
                 (darkMode.css:476) then td rgba(75,143,239,0.18)
                 (darkMode.css:482), both over #101a2c  →  #203b64
     sel+hover   darkMode.css:482 and MODULE 5 dark TIE at (0,3,5);
                 darkMode.css loads later, so selected wins and the
                 colour does not change on hover.
   ASSUMPTION: the panel behind is --dm-surface-1. If a pinned table is
   ever placed on a different dark surface these two shift slightly —
   eyedropper a neighbouring cell and paste the value in. */
/* RECOMPUTED against --dm-surface-1 = #18243a (24,36,58).
     odd       = the surface itself, since rows are now transparent
     even      = rgba(160,196,236,0.035) over it   → #1D2A40
     hover     = rgba(91,155,245,0.12)  over it    → #203250
     selected  = rgba(75,143,239,0.12) then 0.18   → #26426D
   These MUST stay literal — a sticky cell has to be opaque or the
   scrolling columns show through it. HARD DEPENDENCY: if
   --dm-surface-1 changes again, recompute all four or the pinned
   column visibly detaches from the rows. */
body.dark-mode {
    --ig-pin-bg-odd: #18243a;
    --ig-pin-bg-even: #1D2A40;
    --ig-pin-bg-hover: #203250;
    --ig-pin-bg-sel: #26426D;
    --ig-pin-bg-sel-hover: #26426D;
    --ig-pin-bg-head: var(--dm-surface-2, #1f2d47);
    --ig-pin-edge: var(--dm-border, rgba(160, 196, 236, 0.16));
    --ig-pin-shadow-right: -6px 0 10px -6px rgba(0, 0, 0, 0.45);
    --ig-pin-shadow-left: 6px 0 10px -6px rgba(0, 0, 0, 0.45);
}

/* ── Body cells — geometry and edge only ──
   The fill is set by the state rules below, which have to stripe with
   the table. --ig-pin-bg-odd is left here as a floor so a pinned cell
   is never transparent even if a future rule outruns the state block. */
body table.dataTable>tbody>tr>td.ig-pin-right,
body .table>tbody>tr>td.ig-pin-right {
    position: sticky !important;
    right: 0;
    z-index: 2;
    background-color: var(--ig-pin-bg-odd);
    border-left: 1px solid var(--ig-pin-edge) !important;
    box-shadow: var(--ig-pin-shadow-right);
}

body table.dataTable>tbody>tr>td.ig-pin-left,
body .table>tbody>tr>td.ig-pin-left {
    position: sticky !important;
    left: 0;
    z-index: 2;
    background-color: var(--ig-pin-bg-odd);
    border-right: 1px solid var(--ig-pin-edge) !important;
    box-shadow: var(--ig-pin-shadow-left);
}

/* ── Stripes ──
   The pin has to alternate with the table or it reads as one flat column
   painted over the zebra. Same doubled-class trick as the states below;
   these come FIRST so hover and selected, at equal specificity, win on a
   row that matches both.

   .table-striped is not in the selector: the tokens are the stripe
   colours regardless, and a non-striped table's rows are all the "odd"
   colour anyway, so nothing changes there. */
body table.dataTable tbody tr:nth-child(odd)>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr:nth-child(odd)>td.ig-pin-left.ig-pin-left,
body .table tbody tr:nth-child(odd)>td.ig-pin-right.ig-pin-right,
body .table tbody tr:nth-child(odd)>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-odd) !important;
}

body table.dataTable tbody tr:nth-child(even)>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr:nth-child(even)>td.ig-pin-left.ig-pin-left,
body .table tbody tr:nth-child(even)>td.ig-pin-right.ig-pin-right,
body .table tbody tr:nth-child(even)>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-even) !important;
}

/* ── Stripes ──
   The pin has to alternate with the table or it reads as one flat column
   painted over the zebra. Same doubled-class trick as the states below;
   these come FIRST so hover and selected, at equal specificity, win on a
   row that matches both. */
body table.dataTable tbody tr:nth-child(odd)>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr:nth-child(odd)>td.ig-pin-left.ig-pin-left,
body .table tbody tr:nth-child(odd)>td.ig-pin-right.ig-pin-right,
body .table tbody tr:nth-child(odd)>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-odd) !important;
}

body table.dataTable tbody tr:nth-child(even)>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr:nth-child(even)>td.ig-pin-left.ig-pin-left,
body .table tbody tr:nth-child(even)>td.ig-pin-right.ig-pin-right,
body .table tbody tr:nth-child(even)>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-even) !important;
}

/* Hover / selected — restated as OPAQUE, see the trade-off note above.
   A sticky cell must never be translucent or the columns sliding beneath
   show through it — which is exactly what these rules exist to prevent.

   The class is repeated (.ig-pin-right.ig-pin-right) to buy one extra
   point of specificity WITHOUT touching the DOM. It is needed because the
   row-hover rules that must be beaten are stronger than they look:

       MODULE 5  body:not(.dark-mode) table.dataTable tbody tr:hover>td
                 → 3 class-level parts (.dark-mode inside :not(),
                   .dataTable, :hover) + 5 elements, !important,
                   and a TRANSLUCENT rgba() colour.

   The previous .table-striped-scoped selectors were one element short of
   that and lost outright, so hovering a row made the pin see-through.
   .table-striped is dropped here as well: Module 5's rule does not
   require it, so neither can the rule that overrides it.

   Themes are not branched — the --ig-pin-bg-* tokens are already
   redefined under body.dark-mode, so one rule serves both. Selected is
   listed AFTER hover so a hovered selected row still reads as selected,
   matching stock behaviour elsewhere in the app. */
body table.dataTable tbody tr:hover>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr:hover>td.ig-pin-left.ig-pin-left,
body .table tbody tr:hover>td.ig-pin-right.ig-pin-right,
body .table tbody tr:hover>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-hover) !important;
}

/* ── Hover, the WHOLE row ──────────────────────────────────────────
   The rules above give the pin an opaque hover colour derived by hand
   from the translucent wash MODULE 5 paints on the other cells. That
   derivation can only ever be approximate — it depends on which of the
   competing hover rules wins and on the exact surface behind the row —
   and being a few units out is visible as a lighter block sitting in
   the middle of a hovered row.

   So the row is driven from the token instead of the token chasing the
   row: every cell of a hovered row that CONTAINS a pin takes the same
   value. Exact by construction, in both themes, with no arithmetic to
   go stale if a stripe or wash colour is ever changed.

   :has(> td.ig-pin-right) scopes this to pinned tables only — no other
   table in the app is touched. :has() contributes the specificity of
   its most specific argument, so these land at (0,4,6): above MODULE 5
   (0,3,5), above darkMode.css's hover (0,3,4), and above the pin's own
   doubled-class rules (0,4,5) — which carry the same token anyway, so
   whichever wins the result is identical.

   Selected is split out rather than excluded: a hovered selected row
   must keep reading as selected, so it takes the selected+hover token
   across the full row for exactly the same reason. */
body table.dataTable tbody tr:hover:not(.selected):has(>td.ig-pin-right)>td,
body table.dataTable tbody tr:hover:not(.selected):has(>td.ig-pin-left)>td,
body .table tbody tr:hover:not(.selected):has(>td.ig-pin-right)>td,
body .table tbody tr:hover:not(.selected):has(>td.ig-pin-left)>td {
    background-color: var(--ig-pin-bg-hover) !important;
}

body table.dataTable tbody tr.selected:hover:has(>td.ig-pin-right)>td,
body table.dataTable tbody tr.selected:hover:has(>td.ig-pin-left)>td,
body .table tbody tr.selected:hover:has(>td.ig-pin-right)>td,
body .table tbody tr.selected:hover:has(>td.ig-pin-left)>td {
    background-color: var(--ig-pin-bg-sel-hover) !important;
}

/* Hovering a SELECTED row: MODULE 5's wash (0,3,5) outranks the selected
   rule (0,3,3) on every other cell, so the row lightens under the cursor.
   Without this the pin alone would stay at the flat selected colour and
   fall out of step. Last in the block, so it wins the tie against both
   rules above on a row that matches all three. */
body table.dataTable tbody tr.selected:hover>td.ig-pin-right.ig-pin-right,
body table.dataTable tbody tr.selected:hover>td.ig-pin-left.ig-pin-left,
body .table tbody tr.selected:hover>td.ig-pin-right.ig-pin-right,
body .table tbody tr.selected:hover>td.ig-pin-left.ig-pin-left {
    background-color: var(--ig-pin-bg-sel-hover) !important;
}

/* ── Selected, the WHOLE row ───────────────────────────────────────
   The narrow `tr.selected>td.ig-pin-*.ig-pin-*` rules above sit at
   (0,4,5) — the SAME weight as the hover rule beside them. On a row
   that is both selected and hovered the winner is therefore decided by
   source order alone, and the pale hover token landing last is what put
   a near-white block inside a grey-blue selected row.

   Fixed the same way hover was: the row is driven FROM the token rather
   than the token chasing the row, so the pin and its neighbours are
   identical by construction in both themes.

   .selected is repeated for specificity, exactly as .ig-pin-right is
   elsewhere in this module. Counting the class :has() contributes, that
   puts these at (0,4,6) and (0,5,6) — clear of the hover rules at
   (0,4,5) and (0,4,6) regardless of order, so the ordering trap this
   block exists to fix cannot come back.

   These supersede the narrow selected rules above on any row that
   actually has a pin. Those are left in place as the fallback for a
   pinned cell whose row somehow escapes :has(); they carry the same
   tokens, so there is no way for the two to disagree. */
body table.dataTable tbody tr.selected.selected:has(>td.ig-pin-right)>td,
body table.dataTable tbody tr.selected.selected:has(>td.ig-pin-left)>td,
body .table tbody tr.selected.selected:has(>td.ig-pin-right)>td,
body .table tbody tr.selected.selected:has(>td.ig-pin-left)>td {
    background-color: var(--ig-pin-bg-sel) !important;
}

body table.dataTable tbody tr.selected.selected:hover:has(>td.ig-pin-right)>td,
body table.dataTable tbody tr.selected.selected:hover:has(>td.ig-pin-left)>td,
body .table tbody tr.selected.selected:hover:has(>td.ig-pin-right)>td,
body .table tbody tr.selected.selected:hover:has(>td.ig-pin-left)>td {
    background-color: var(--ig-pin-bg-sel-hover) !important;
}

/* ── Header cells ──
   The visible header is a SEPARATE table inside .dataTables_scrollHead,
   which DataTables scrolls by setting its scrollLeft to match the body.
   That div is a scroll container in its own right, so sticky works
   there too — the header pin and the body pin move as one.

   background-image: none kills igniteButtons.css §7, which paints a
   TRANSLUCENT gradient on every thead th — through which the scrolling
   columns would be visible. Two classes vs its one means this wins on
   specificity despite igniteButtons.css loading last. */
body table.dataTable thead th.ig-pin-right,
body .table thead>tr>th.ig-pin-right {
    position: sticky !important;
    right: 0;
    z-index: 4;
    background-color: var(--ig-pin-bg-head) !important;
    background-image: none !important;
    border-left: 1px solid var(--ig-pin-edge) !important;
    box-shadow: var(--ig-pin-shadow-right);
}

body table.dataTable thead th.ig-pin-left,
body .table thead>tr>th.ig-pin-left {
    position: sticky !important;
    left: 0;
    z-index: 4;
    background-color: var(--ig-pin-bg-head) !important;
    background-image: none !important;
    border-right: 1px solid var(--ig-pin-edge) !important;
    box-shadow: var(--ig-pin-shadow-left);
}

/* The width-sizing <thead> clone DataTables leaves inside
   .dataTables_scrollBody is collapsed to height 0 by the Global Table
   Shell v1.1 patch, so it paints nothing and needs no exclusion here.
   Left as a note so nobody "fixes" a phantom. */

/* ══════════════ MODULE END — Pinned table columns ══════════════ */

/* ── Pending FileSync sync ──────────────────────────────────────────
   A folder rename applies in Ignite immediately and moves in FileSync
   afterwards, in the background. On a large subtree those can be minutes
   apart, so the tree shows the new name while FileSync still has the old
   one. These marks say so, in place, rather than leaving it looking broken.

   Deliberately no inline styles at the JS end: an inline style attribute
   cannot be overridden by the dark theme, which is why the documents sync
   overlay had to be moved out of inline styles in the first place.        */

.ig-pending-sync-icon {
    margin-left: 6px;
    font-size: 11px;
    color: #b8860b;
    opacity: 0.95;
    vertical-align: baseline;
    cursor: help;
    /* Slow and shallow. This can sit on screen for minutes at a time, so
       anything faster reads as an error state rather than a wait.          */
    animation: ig-pending-sync-pulse 2.4s ease-in-out infinite;
}

/* Applied to the <li>, so the whole row reads as provisional rather than
   just the icon. Kept subtle — the name shown IS the correct one.          */
.ig-pending-sync > .jqtree-element .jqtree-title,
.ig-pending-sync > a {
    font-style: italic;
    opacity: 0.88;
}

@keyframes ig-pending-sync-pulse {
    0%, 100% { opacity: 0.95; }
    50%      { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .ig-pending-sync-icon {
        animation: none;
    }
}

/* Reconcile overlay text: the phase messages change every couple of
   seconds, so a fixed minimum width stops the panel jumping about as the
   wording changes length.                                                  */
#documents-loading-message {
    display: inline-block;
    min-width: 260px;
}

/* The inline note shown beneath a folder-name field while its rename is
   still queued in FileSync. Sits where the person is looking, because the
   natural reaction to "the folder has not moved" is to rename again -- the
   one action that makes it worse.                                          */
.ig-pending-sync-note {
    margin-top: 4px;
    padding: 6px 9px;
    font-size: 12px;
    line-height: 1.4;
    color: #7a5c17;
    background: #fdf6e3;
    border: 1px solid #f0e0b0;
    border-radius: 4px;
}

.ig-pending-sync-note .ig-pending-sync-icon {
    margin-left: 0;
    margin-right: 6px;
}

/* ═══════════════════════════════════════════════════════════════════════
   #ig_footer — the copyright line
   Rendered by initializeFooter() into layouts/default.html, where it sits
   as a bare sibling BELOW the page content with no styling of its own. Its
   spacing was therefore inherited from whatever element happened to precede
   it, so on a page ending in a rounded .panel the text landed hard against
   the panel's bottom border and read as being inside the card.
   Fixed spacing here makes it consistent on every page.
   Styled once, globally -- no page or tab should override it.
   ═══════════════════════════════════════════════════════════════════════ */
#ig_footer {
    display: block;
    width: 100%;
    clear: both;
    /* The gap that keeps it clear of the panel above. margin, not padding,
       so it stays outside any border or background the footer might later
       be given. 6px, not 16: this margin is INSIDE the span igFitBlockToViewport
       reserves, so every pixel here is a pixel the tab content does not get.
       6px is still enough that the copyright does not read as panel chrome. */
    margin: 4px 0;
    padding: 0 4px;
    text-align: right;
    font-size: 11px;
    line-height: 1.5;
    letter-spacing: 0.2px;
    color: #93a3b5;
    /* Deliberately not a link or a control -- keep it visually quiet so it
       never competes with panel chrome for attention. */
    font-weight: 400;
}

/* The OUTERMOST page card only -- the one that is a direct child of the
   content fragment in layouts/default.html, with #ig_footer as its next
   sibling. .panel carries margin-bottom:30px globally (style.css:1373), which
   is right for panels stacked in a column and for panels inside a modal, but
   on the page card that 30px sits between the card and the footer -- inside
   the span igFitBlockToViewport reserves -- so it is subtracted from every
   tab's usable height on every screen in the system.
   Scoped by ancestor rather than by a class so no page shell has to opt in,
   and so a page whose content fragment nests its panel deeper simply keeps
   the 30px rather than breaking. Dialog panels are nested and never match. */
#johannes-main-content .col-lg-12 > * > section.panel {
    margin-bottom: 1px;
}

/* section.wrapper pads all four sides by 15px (style.css:723, restated at :779).
   Three of those sides are doing real work. The bottom one sits UNDER
   #ig_footer -- below the last painted thing on the page -- so it is a dead
   strip, and because igFitBlockToViewport measures belowSpan as the lowest
   bottom edge among the footer AND its ancestors, that strip is subtracted
   from every tab's usable height on every screen in the system.
   The footer already carries its own margin-top, so nothing here is what keeps
   the copyright clear of the card above it.
   section.wrapper, not .wrapper: #johannes-main-content .wrapper restates the
   padding, and matching the element name outranks it without needing
   !important or a guaranteed stylesheet order. Direct child so only the
   page-level wrapper is hit -- darkMode.css touches .wrapper only for
   background and colour, so neither theme fights this. */
#johannes-main-content > section.wrapper {
    margin-top: var(--ig-header-h);
    padding: 10px 10px 0 10px;
}

/* The page row in layouts/default.html. The global .row rule (style.css:5721)
   is form spacing; here it made the right gutter 22px against the left's 10px
   and left 12px dead under the footer. */
#johannes-main-content > section.wrapper > .row {
    padding-right: 0;
    padding-bottom: 0;
    margin-bottom: 0;
}

/* Compact page chrome. Bootstrap's 15px panel padding, the 16/18px table
   card padding and heading margins stacked into 60px+ of empty frame around
   every tab. Anchored like the margin-bottom rule above, so dialog and
   nested panels never match. */
/* Outer card: room above the group bar, a thin seam on the other three sides. */
#johannes-main-content .col-lg-12 > * > section.panel > .panel-body {
    padding: 10px 4px 4px 4px;
}

#johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-body {
    padding: 10px;
}

/* One border weight on all four sides of both page cards. style.css:1462's
   2px bottom made the bottom seam read heavier than the sides. */
#johannes-main-content .col-lg-12 > * > section.panel > .panel-body,
#johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-body {
    border-bottom-width: 1px;
}

/* Light only: dark mode sets all four sides from --dm-border already
   (darkMode.css:5279). Same #ccc style.css uses on the other three sides. */
body:not(.dark-mode) #johannes-main-content .col-lg-12 > * > section.panel > .panel-body,
body:not(.dark-mode) #johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-body {
    border-top-color: #ccc !important;
}

/* Group bar and tab strip move left together: both lose 4px so the first
   tab stays aligned under the first segment. */
#johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-heading .ig-segwrap {
    padding-left: 4px;
}

/* One left line: group bar, tab strip and tab content all start at the inner
   card's content edge. 7 = card border 1 + card padding 10 - the 4px inset the
   segment track and first tab both carry. Beats style.css:1376 (padding 0 15px). */
#johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-heading {
    padding-left: 7px;
}

#johannes-main-content .col-lg-12 > * > section.panel > .panel-body > .panel-heading ul.nav.nav-tabs {
    padding-left: 0 !important;
}

#johannes-main-content .tab-pane .dataTables_wrapper:not(.modal .dataTables_wrapper) {
    padding: 12px 12px 8px 12px;
}

#johannes-main-content .tab-pane h4:not(.modal h4) {
    margin-top: 0;
    margin-bottom: 6px;
}

/* A panel inside a tab is layout, not a second card. The tab's own card is
   the surface; a bordered panel inside it was a box in a box with 15px
   padding and a 30px bottom margin (style.css:1373). Dialogs excluded. */
#johannes-main-content .tab-pane section.panel:not(.modal section.panel),
#johannes-main-content .tab-pane section.panel:not(.modal section.panel) > .panel-body {
    background: transparent !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 0;
    margin-bottom: 0;
}

/* Compact top bar. One height token; flex centres everything in the bar,
   replacing the hand-tuned top margins in style.css (logo 15px, hamburger
   20px, notification row 15px + 7px, bell 7px) that only worked at 60px. */
:root {
    --ig-header-h: 48px;
}

header.header {
    display: flex;
    align-items: center;
    height: var(--ig-header-h);
    min-height: var(--ig-header-h);
}

header.header .toggle-nav,
header.header .top-nav,
header.header .bell-notify {
    margin-top: 0;
}

/* float:none is load-bearing: a floated child collapses its parent to zero
   height, and a zero-height flex item is centred as a point, not a box. */
header.header a.logo {
    float: none;
    margin-top: 0;
}

header.header .notification-row {
    margin-left: auto;
}

header.header ul.top-menu {
    float: none;
    display: flex;
    align-items: center;
    margin: 0;
}

ul.sidebar-menu {
    margin-top: var(--ig-header-h);
}

/* ═══════════════════════════════════════════════════════════════════
   sdProjectLinksTab
   ═══════════════════════════════════════════════════════════════════ */

/* THE PANNABLE IS THE ONLY HORIZONTAL SCROLLER HERE.
   .dataTables_scrollBody is overflow-x:hidden !important globally
   (see the rule near the top of this file), so a table can never scroll
   sideways on its own — a column that needs more room can only get it by
   the PANEL being wider. That is why the min-widths below are per-panel
   and generous rather than the shared 600px default. */

/* Lets the pannable shrink inside the flex row and scroll, instead of
   blowing the row out past the viewport. */
#sdProjectLinksTab .panel-body > .row > .col-md-8 {
    min-width: 0;
}

/* Panels sit on ONE line, always. Without nowrap the third panel drops
   under the first two the moment their min-widths exceed the row. */
#sdProjectLinksTab .pannable > .row {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
}

/* Per-panel floors, sized to each table's own columns. Ordered by
   nth-child because the panel divs carry no ids. Overrides the shared
   .pannable > .row > .col-md-4 { min-width: 600px } on id specificity. */
#sdProjectLinksTab .pannable > .row > .col-md-4 {
    flex-shrink: 0;
}

/* Panel 1 — Projects. Programme is the widest column: it renders as
   number + title joined ("0094 - 0776 - JRA Ennerdale SWMP - JRA
   Ennerdale SWMP"), not the number alone. */
#sdProjectLinksTab .pannable > .row > .col-md-4:nth-child(1) {
    min-width: 860px;
}

/* Panel 2 — Participants. Six visible columns, three of them free-text
   names that routinely run past 30 characters. */
#sdProjectLinksTab .pannable > .row > .col-md-4:nth-child(2) {
    min-width: 1000px;
}

/* Panel 3 — Timesheet Entries. The widest of the three by a distance:
   sdAndFunction is a full pipe-separated name path plus the function
   name, and Description is a 255-char free-text column. */
#sdProjectLinksTab .pannable > .row > .col-md-4:nth-child(3) {
    min-width: 1250px;
}

/* NO WRAPPING — this is the rule that stops rows growing downward.
   Applies to the scrollHead clone as well as the body table: DataTables
   splits the table in two under scrollX, and if only one side wraps the
   header and body columns drift out of alignment. */
#sdProjectLinksTab table.dataTable > thead > tr > th,
#sdProjectLinksTab table.dataTable > tbody > tr > td {
    white-space: nowrap;
}

/* =============================================================================
   FOLDER TREE SYNC STATUS + ACTIVITY LOG
   Shared by the Documents tab and the Folder Register tab. Both trees compare
   the register against FileSync and report it the same way, so the styling
   lives here rather than in either tab.
   Dark-mode overrides: darkMode.css, same section name.
   ============================================================================= */

/* --- Positioning context -------------------------------------------------- */
/* Scoped to the two tree wrappers. A blanket `.card { position: relative }`
   would apply to every card on the page and quietly change stacking on
   unrelated content. */
#folderTreeColumn,
.ig-tree-anchor {
    position: relative;
}

/* --- Sync indicator ------------------------------------------------------- */
/* Sits in the top-right corner of the tree panel, OUTSIDE the scrolling body:
   absolutely positioned inside a scroll container it scrolls away with the
   content. One glyph, recoloured per state. Overlaying a second icon on top of
   a circular-arrows glyph read as clutter at this size, so state is carried by
   colour and a single shape. */
.ig-sync-indicator {
    position: absolute;
    top: 6px;
    right: 14px;
    z-index: 5;
    display: inline-flex;
    align-items: center;
}

.ig-sync-dot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    font-size: 13px;
    background: rgba(0, 0, 0, 0.04);
    position: relative;
}

.ig-sync-dot--ok {
    color: #1f9d5b;
}

.ig-sync-dot--pending {
    color: #b07d18;
}

.ig-sync-dot--warn {
    color: #b5651d;
    cursor: pointer;
}

.ig-sync-dot--unknown {
    color: #7a8797;
}

.ig-sync-dot--warn:hover {
    background: rgba(0, 0, 0, 0.09);
}

.ig-sync-dot__count {
    position: absolute;
    top: -3px;
    right: -6px;
    background: #b5651d;
    color: #ffffff;
    border-radius: 9px;
    padding: 0 5px;
    font-size: 10px;
    font-weight: 700;
    line-height: 15px;
}

/* --- Activity log panel --------------------------------------------------- */
/* Collapsed by default. It lists problems only, so an empty or closed panel is
   the normal state and should take almost no room. */
#docActivityCard,
#pfrActivityCard {
    margin-top: 12px;
}

.ig-log-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    cursor: pointer;
    user-select: none;
    font-size: 13px;
}

.ig-log-header:hover {
    background: rgba(0, 0, 0, 0.03);
}

.ig-log-header-title {
    flex: 1 1 auto;
    font-weight: 600;
}

.ig-log-badge {
    background: #c0392b;
    color: #ffffff;
    border-radius: 9px;
    padding: 0 7px;
    font-size: 11px;
    font-weight: 700;
    line-height: 17px;
}

.ig-log-empty {
    padding: 12px 14px;
    font-size: 12px;
    opacity: 0.75;
}

#docSyncLog,
#pfrSyncLog {
    padding: 4px 12px 10px 12px;
}

.ig-log-datatable {
    font-size: 12px;
}

.ig-log-datatable td {
    vertical-align: top;
}

/* Column widths. The panel is full width now (both tabs render the card as a
   sibling of the tree row, not a child of the tree column), so the explanation
   column can have the space and the other three are held back.
   Widths are declared on the DataTable in igSyncStatus.js; these carry the
   wrapping behaviour that goes with them. */
.ig-log-datatable td.ig-log-col-what {
    line-height: 1.55;
}

/* One line, always. The timestamp is fixed width text and wrapping it to two
   lines to save forty pixels makes the row taller, not narrower. */
.ig-log-datatable td.ig-log-col-when,
.ig-log-datatable th.ig-log-col-when {
    white-space: nowrap;
}

/* File permission tables sit BELOW the file list, as their own row. Reading
   order is folder permissions, then the files in that folder, then the
   permissions on the file picked from the list. Beside the folder's tables they
   read as a second pair of columns with nothing saying which is which. */
.doc-file-perm-row {
    margin-top: 14px;
}

/* --- Folder context bar ---------------------------------------------------- */
/* The path bar above the Documents and My Files trees. Built like the dashboard
   stat cards: the gold cap IS the left edge, with its own darker outline. */
#docFolderContextBar {
    --doc-ctx-accent: var(--ig-gold-honey, #FFBD81);
    --doc-ctx-accent-edge: color-mix(in srgb, var(--doc-ctx-accent) 70%, #000);
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 7px 12px 7px 20px;
    flex: 1 1 auto;
    min-width: 0;
    background: #fff;
    border: 1px solid var(--ig-border-light, #dee2e6);
    border-left-width: 0;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
    box-sizing: border-box;
}

/* Covers the top and bottom border rows so the two outlines meet with no seam. Width must stay >= the 8px radius. */
#docFolderContextBar::before {
    content: "";
    position: absolute;
    top: -1px;
    bottom: -1px;
    left: 0;
    width: 8px;
    box-sizing: border-box;
    border: 1px solid var(--doc-ctx-accent-edge);
    border-radius: 8px 0 0 8px;
    background: var(--doc-ctx-accent);
}

.doc-ctx-text {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
}

/* Only when the tree is on something else. Underline on hover rather than all
   the time: the bar is on screen whenever a folder is selected and a permanent
   link would read as one more thing to deal with. */
.doc-ctx-text.is-clickable {
    cursor: pointer;
}

.doc-ctx-text.is-clickable:hover .doc-ctx-name {
    text-decoration: underline;
    color: #4b8fef;
}

.doc-ctx-text.is-clickable:hover .doc-ctx-icon {
    color: #4b8fef;
}

.doc-ctx-icon {
    color: #d9a441;
}

.doc-ctx-name {
    font-weight: 600;
    white-space: nowrap;
}

/* The full path can be long. It truncates rather than wrapping the bar onto a
   second line, and the name beside it is the part that matters. */
.doc-ctx-path {
    font-size: 12px;
    color: #7a8ba0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Says what the bar is describing: the folder, or the file. */
.doc-ctx-kind {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: #7a8ba0;
    white-space: nowrap;
}

.doc-ctx-sep {
    margin: 0 4px;
    opacity: 0.6;
}

.doc-ctx-crumb.is-clickable {
    cursor: pointer;
}

.doc-ctx-crumb.is-clickable:hover {
    text-decoration: underline;
    color: #4b8fef;
}

.doc-ctx-copy {
    margin-left: auto;
    flex-shrink: 0;
}

.doc-view-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 16px;
    min-height: 34px;
    margin-bottom: 10px;
}

.doc-view-row > .ig-modebar {
    margin-bottom: 0;
}

.doc-top-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
    min-height: 50px;
}

.doc-node-information {
    font-size: 12px;
    color: #5b7083;
    white-space: nowrap;
}

.doc-node-information-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #8a949f;
}

.doc-node-information-separator {
    margin: 0 8px;
    opacity: 0.5;
}

#docNodeInformationPanel {
    margin-bottom: 12px;
}

.doc-node-information-table {
    table-layout: fixed;
    margin-bottom: 0;
}

.doc-node-information-table td {
    height: 34px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}


/* --- Documents in the selected folder -------------------------------------- */
#docFolderFilesRow {
    margin-top: 14px;
}


/* --- File table buttons ---------------------------------------------------- */
/* In the table's own toolbar, via the Buttons extension, like every other table
   in this tab. They act on the selected row, so they render disabled until
   there is one. */
#docFolderFilesTable_wrapper .dt-buttons {
    display: flex;
    gap: 6px;
    float: none;
}

.doc-file-action.disabled,
.doc-file-action:disabled {
    opacity: 0.4;
    cursor: default;
    pointer-events: none;
}

/* --- Preview dialog -------------------------------------------------------- */
/* modal-xl caps at 1140px, which is not enough for a drawing. */
.doc-preview-dialog {
    max-width: 95vw;
    width: 95vw;
    margin: 1.5vh auto;
}

.doc-preview-dialog .modal-content {
    height: 96vh;
}

.doc-preview-dialog .modal-body {
    overflow: auto;
}

/* --- Log panel placement -------------------------------------------------- */
/* The log card is a flex item in the same row as the tree, pushed to the bottom
   of that row. Flex items stretch to the tallest item, which is the tree, so
   justify-content: flex-end lands the card's bottom edge on the tree's without
   either panel carrying a hard-coded height.

   min-width is the floor at which the What happened column stops being useful.
   Below that the row wraps and the card drops under the tree, which is a worse
   layout but still a readable one. */
.ig-log-slot {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    flex: 1 1 40%;
    min-width: 360px;
}

.ig-log-slot > .card {
    margin-bottom: 0;
    /* Matches the tree's own max-height so a long log cannot push the row
       taller than the tree it is aligned to. Keep the two in step: the bottom
       alignment depends on the tree being the taller item. */
    max-height: min(650px, calc(100vh - 220px));
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
}

/* Documents tab: everything right of the tree is one column - permission
   tables across the top, log card at the bottom. The log was a direct flex
   item of the row before this, which parked it in a column beyond the
   permission tables and left the space under them empty. */
.doc-right-stack {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

/* The permission panels keep the row behaviour they had as siblings of the
   tree: nowrap, same gap, each column shown or hidden on its own by id. */
.doc-perm-row {
    display: flex;
    flex-wrap: nowrap;
    gap: 10px;
}

.doc-right-stack > .ig-log-slot {
    margin-top: auto;
    min-width: 0;
    /* Width comes from the stack now, not from a flex basis of its own. */
    flex: 0 0 auto;
}

/* Folder Register tab: the right-hand column holds its own content at the top
   and the log card at the bottom. */
.pfr-right-col {
    display: flex;
    flex-direction: column;
}

.pfr-right-col > .ig-log-slot {
    margin-top: auto;
    min-width: 0;
}

/* Tree keeps a readable width at any zoom; col-md-3 alone pinned it to 25%. */
#projectFolderRegisterTab #pfrContentRow > .col-md-3 {
    flex: 1 1 28%;
    min-width: 280px;
    max-width: 440px;
}

#projectFolderRegisterTab #pfrContentRow > .col-md-9 {
    flex: 1 1 0;
    min-width: 0;
    max-width: none;
}

/* Bootstrap's -15px right margin would let the pannable tab scroll 15px even when everything fits. */
#wsWorkspaceRow {
    margin-right: 0;
}

#wsParticipantTableFrame,
#wsProgrammeTableFrame,
#wsProjectTableFrame {
    position: relative;
    overflow: visible;
}

/* Drag handle for startIgPanelResize. The hit area straddles the panel's right
   border; ::after is the visible line on that border. The parent must be
   position: relative. */
.ig-panel-resize {
    position: absolute;
    top: 0;
    bottom: 0;
    right: -5px;
    width: 10px;
    z-index: 6;
    cursor: col-resize;
}

.ig-panel-resize::after {
    content: "";
    position: absolute;
    top: 8px;
    bottom: 8px;
    left: 3px;
    width: 2px;
    border-radius: 1px;
    background: transparent;
    transition: background 0.15s;
}

.ig-panel-resize:hover::after,
.ig-panel-resize.dragging::after {
    background: rgba(75, 143, 239, 0.6);
}
/* Column resize handle on a DataTables header cell (initializeIgTableColumnResize in ignite.js). */
table.dataTable thead th.ig-col-resizable {
    position: relative;
}

table.dataTable thead th .ig-col-resize {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 8px;
    z-index: 2;
    cursor: col-resize;
}

table.dataTable thead th .ig-col-resize::after {
    content: "";
    position: absolute;
    top: 25%;
    bottom: 25%;
    right: 0;
    width: 2px;
    border-radius: 1px;
    background: transparent;
    transition: background 0.15s;
}

table.dataTable thead th:hover .ig-col-resize::after {
    background: var(--ig-border-light);
}

table.dataTable thead th .ig-col-resize:hover::after,
table.dataTable thead th.ig-col-resizing .ig-col-resize::after {
    background: var(--ig-search-current-bar);
}

/* Custom widths: fixed layout makes each column exactly its set width, and the table is their sum.
   Three classes so this outranks .table-scrollable table.dataTable (max-content, min-width 100%). */
.dataTables_wrapper.ig-col-widths-custom table.dataTable {
    table-layout: fixed !important;
    width: max(var(--ig-table-columns-width), 100%) !important;
    min-width: 0 !important;
}

/* border-box so the head's 100% excludes the scrollbar padding DataTables adds, matching the body's width. */
.dataTables_wrapper.ig-col-widths-custom .dataTables_scrollHeadInner {
    box-sizing: border-box !important;
    width: 100% !important;
}

.dataTables_wrapper.ig-col-widths-custom table.dataTable > thead > tr > th {
    box-sizing: border-box;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dataTables_wrapper.ig-col-widths-custom table.dataTable > tbody > tr > td {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Workspace: the collapse button sits on the frame's top border. */
/* Centred on the middle of the frame's rounded top-right corner (8px radius), not its square corner point. */
#wsWorkspaceRow .ws-panel-collapse {
    position: absolute;
    top: -8px;
    right: -8px;
    z-index: 7;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 1px solid var(--ig-border-light);
    border-radius: 50%;
    background: var(--ig-header-bg);
    color: var(--ig-arrow-hover);
    font-size: 10px;
    line-height: 18px;
    cursor: pointer;
}

#wsWorkspaceRow .ws-panel-collapse:hover {
    color: var(--ig-search-current-bar);
}

/* Default widths. Participants and Programmes stay narrow; Projects takes the rest.
   A dragged width is written inline by startIgPanelResize and wins over these. */
#wsWorkspaceRow #wsParticipantColumn:not(.ws-panel-collapsed) {
    flex: 0 0 10%;
    min-width: 250px;
}

#wsWorkspaceRow #wsProgrammeColumn:not(.ws-panel-collapsed) {
    flex: 0 0 13%;
    min-width: 300px;
}

/* Projects takes the width that is left; a dragged width is its minimum. */
#wsWorkspaceRow #wsProjectColumn:not(.ws-panel-collapsed) {
    flex-grow: 1 !important;
    min-width: 700px;
}
/* Two ids so this outranks the display: flex that tabViewportFit.css gives fill elements.
   Only the column being animated stays visible; other collapsed columns stay hidden. */
#wsWorkspaceTab #wsWorkspaceRow > .ws-panel-collapsed:not(.ws-panel-animating) {
    display: none;
}

#wsWorkspaceTab.ws-panels-animating #wsWorkspaceRow,
#wsWorkspaceTab.ws-panels-animating #wsCollapsedDock {
    pointer-events: none;
}

/* Content held at its widest width and clipped, so tables are revealed instead of re-wrapping each frame. */
#wsWorkspaceTab #wsWorkspaceRow > .ws-panel-animating {
    overflow: hidden;
}

#wsWorkspaceTab #wsWorkspaceRow > .ws-panel-animating > * {
    width: var(--ws-panel-content-width);
    min-width: var(--ws-panel-content-width);
    max-width: none;
}

/* With Projects collapsed, the last panel still showing takes the width instead of leaving a gap. */
#wsWorkspaceRow:has(> #wsProjectColumn.ws-panel-collapsed) > #wsProgrammeColumn,
#wsWorkspaceRow:has(> #wsProjectColumn.ws-panel-collapsed):has(> #wsProgrammeColumn.ws-panel-collapsed) > #wsParticipantColumn {
    flex-grow: 1 !important;
}

/* Opens by growing its grid row from 0fr to 1fr, so the tables below move down smoothly. */
#wsCollapsedDock {
    display: grid;
    flex: 0 0 auto;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.46s cubic-bezier(0.32, 0.72, 0, 1);
}

#wsCollapsedDock.ws-collapsed-dock-open {
    grid-template-rows: 1fr;
}

#wsCollapsedDock .ws-collapsed-dock-clip {
    min-height: 0;
    overflow: hidden;
}

#wsCollapsedDock .ws-collapsed-dock-cards {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 3px 3px 12px;
}

/* max-width is a real number so the card can transition its width in and out. */
#wsCollapsedDock .ws-dock-card {
    display: none;
    align-items: center;
    gap: 8px;
    height: 32px;
    max-width: 240px;
    padding: 0 14px 0 10px;
    overflow: hidden;
    white-space: nowrap;
    border: 1px solid var(--ig-border-light);
    border-radius: 8px;
    background: var(--ig-header-bg);
    color: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: color 0.15s, transform 0.15s, box-shadow 0.15s;
}

#wsCollapsedDock .ws-dock-card-shown {
    display: inline-flex;
    transform-origin: left top;
    animation: wsDockCardIn 0.46s cubic-bezier(0.32, 0.72, 0, 1) backwards;
}

#wsCollapsedDock .ws-dock-card:hover {
    color: var(--ig-search-current-bar);
    transform: translateY(-1px);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

#wsCollapsedDock .ws-dock-card > i {
    font-size: 11px;
    color: var(--ig-arrow-hover);
    transition: color 0.15s;
}

#wsCollapsedDock .ws-dock-card:hover > i {
    color: inherit;
}

/* The -8px margin cancels the dock's gap, so neighbouring cards close up without a jump. */
#wsCollapsedDock .ws-dock-card-leaving {
    max-width: 0;
    padding-left: 0;
    padding-right: 0;
    border-width: 0;
    margin-left: -8px;
    opacity: 0;
    transform: scale(0.92);
    transition: max-width 0.46s cubic-bezier(0.32, 0.72, 0, 1), padding 0.46s cubic-bezier(0.32, 0.72, 0, 1),
        border-width 0.46s, margin-left 0.46s cubic-bezier(0.32, 0.72, 0, 1), opacity 0.25s ease-out, transform 0.46s;
}

@keyframes wsDockCardIn {
    from {
        max-width: 0;
        padding-left: 0;
        padding-right: 0;
        border-width: 0;
        margin-left: -8px;
        opacity: 0;
        transform: translateY(6px) scale(0.92);
    }
    to {
        max-width: 240px;
        padding-left: 10px;
        padding-right: 14px;
        border-width: 1px;
        margin-left: 0;
        opacity: 1;
        transform: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    #wsCollapsedDock,
    #wsCollapsedDock .ws-dock-card {
        transition: none;
        animation: none;
    }
}

/* Wraps on the column's own width, not the window's, so the panels stack when narrow. */
.pfr-perm-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.pfr-perm-col {
    flex: 1 1 380px;
    min-width: 0;
}

/* --- Log pager ------------------------------------------------------------ */
/* Scoped to the two log panels ON PURPOSE. The global .dataTables_paginate rule
   is `float: right; width: 50%`, which in a narrow container breaks the buttons
   onto three stacked lines. Overriding it globally would move every pager in
   the app; this only claims the two panels this module owns.

   Both pager markups are covered: DataTables' own span/a `.paginate_button`,
   and the bootstrap `li > a` variant, because which one renders depends on the
   paging plugin loaded at the time and neither tab pins it. */
#docSyncLog .dataTables_paginate,
#pfrSyncLog .dataTables_paginate {
    float: none;
    width: auto;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    margin: 10px 0 2px 0;
    padding: 0;
}

#docSyncLog .dataTables_paginate ul.pagination,
#pfrSyncLog .dataTables_paginate ul.pagination {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 0;
    padding: 0;
}

#docSyncLog .dataTables_paginate .paginate_button,
#pfrSyncLog .dataTables_paginate .paginate_button,
#docSyncLog .dataTables_paginate li > a,
#pfrSyncLog .dataTables_paginate li > a {
    display: inline-block;
    float: none;
    min-width: 30px;
    padding: 3px 9px;
    border: 1px solid rgba(120, 150, 180, 0.30);
    border-radius: 6px;
    background: transparent;
    font-size: 12px;
    line-height: 20px;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

#docSyncLog .dataTables_paginate .paginate_button:hover,
#pfrSyncLog .dataTables_paginate .paginate_button:hover,
#docSyncLog .dataTables_paginate li > a:hover,
#pfrSyncLog .dataTables_paginate li > a:hover {
    background: rgba(75, 143, 239, 0.10);
}

#docSyncLog .dataTables_paginate .paginate_button.current,
#pfrSyncLog .dataTables_paginate .paginate_button.current,
#docSyncLog .dataTables_paginate li.active > a,
#pfrSyncLog .dataTables_paginate li.active > a {
    background: rgba(75, 143, 239, 0.16);
    border-color: rgba(75, 143, 239, 0.55);
    font-weight: 600;
}

#docSyncLog .dataTables_paginate .paginate_button.disabled,
#pfrSyncLog .dataTables_paginate .paginate_button.disabled,
#docSyncLog .dataTables_paginate li.disabled > a,
#pfrSyncLog .dataTables_paginate li.disabled > a {
    opacity: 0.45;
    cursor: default;
    background: transparent;
}

/* --- Log table rows ------------------------------------------------------- */
/* Name only, no path. The path was six wrapped lines per row and dominated the
   table; it lives in the row tooltip and the Show in tree button instead.
   Colours are deliberately NOT set: this is a standard DataTable and inherits
   the app's table styling in both themes. */
.ig-log-name {
    font-weight: 600;
    /* Wraps. nowrap here made a long folder name the widest thing in the panel,
       and a table cell that will not wrap forces its whole column wider. Folder
       names in this system run to forty characters routinely. */
    word-break: break-word;
}

.ig-log-name i {
    opacity: 0.65;
    margin-right: 4px;
}

/* Action column: sized to its buttons so the text columns keep the width. */
td.ig-log-actions,
th.ig-log-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

td.ig-log-actions .btn {
    margin-left: 4px;
}


/* --- Offline gating ------------------------------------------------------- */
/* Applied to tree action buttons when the file server cannot be reached. Every
   one of them writes to it, so leaving them live is an invitation to a failure
   the user can do nothing about. Refresh stays enabled: it is how they retry. */
.ig-disabled-offline,
button.ig-disabled-offline:hover {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

/* !important throughout this jsTree section, deliberately, and it is the
   only place in this block that needs it. The jstree-default stylesheet is
   external and its load order relative to ignite.css is not ours to
   guarantee, and the skin above (ignite.css 4918 onward) is !important on
   every declaration, so an ordinary rule here would be one refactor away
   from silently losing. No leaf reach rule: jsTree renders the ocl slot on
   leaf rows too, so the row rhythm is already there and the horizontal has
   nothing empty to cross. */

   /* =================================================================
   PAN CURSOR POLICY (global)
   Rest = normal select cursor. The grab hand appears only once a
   press-and-hold (or a real drag) has armed a pan, driven by the
   single body.ig-panning switch set in ignite JS.
   ================================================================= */

.pannable,
.gantt-timeline,
.gantt-collapsible,
.advf-grid,
.db-tree-scroller,
#dbOrgViewport,
#dbGanttTimeline,
[data-ig-pan] {
    cursor: auto !important;   /* auto, not default — keeps the I-beam over text */
}

/* Live pan. The ID-scoped selectors are here deliberately: without them
   #dbOrgViewport / #dbGanttTimeline above out-specify this rule and the
   hand never appears on those two. */
body.ig-panning,
body.ig-panning *,
body.ig-panning #dbOrgViewport,
body.ig-panning #dbGanttTimeline {
    cursor: grabbing !important;
    user-select: none !important;
    -webkit-user-select: none !important;
}

/* Nothing can paint a selection mid-drag */
body.ig-panning ::selection { background: transparent; }

/* Opt-out: put this on any block whose text must stay drag-selectable */
.ig-selectable, .ig-selectable * { user-select: text !important; }


#dmReferenceDataPage #dmDocumentTypePane .row { height: auto; }


/* ═══════════════════════════════════════════════════════════
   PROJECT SERVICE DISCIPLINES DIALOG  (projectSdDialog)
   The .advf-grid recipe (deliverableAdvancedFilter.css:348-377, 588-663)
   applied to two panels instead of four. Height is a fraction of the
   VIEWPORT and an unbroken flex column carries it down to the one
   element that scrolls. min-height:0 on every link is load-bearing: a
   flex item defaults to min-height:auto and refuses to shrink below its
   content, so one missing link leaves every flex:1 below it inert.

   The chain is only four links long because the HTML no longer wraps the
   panels in section.panel > form > fieldset > .row > .col-md-6. That is
   the whole reason the advanced filter never had this problem.

   Colour, glass and both themes come from the shared .modal-content /
   .modal-body rules, .ig-liquid-glass and darkMode.css. Nothing here.
   ═══════════════════════════════════════════════════════════ */

.ig-sd-picker .modal-dialog {
    margin: 2vh auto;
}

.ig-sd-picker .modal-content {
    height: 88vh;
    display: flex;
    flex-direction: column;
}
/* Short lists (a handful of rows): the dialog sizes to its rows instead of filling the screen. */
.ig-sd-picker.ig-sd-picker-compact .modal-content {
    height: auto;
    max-height: 88vh;
}

.ig-sd-picker .modal-header,
.ig-sd-picker .modal-footer,
.ig-sd-picker .psd-error {
    flex: 0 0 auto;
}

/* Overrides .modal-lg90 .modal-body's max-height:80vh (ignite.css:1332)
   and the blanket .modal-body overflow:auto (ignite.css:1345). The body
   must NOT scroll: scroll belongs at the leaves. */
.ig-sd-picker .modal-body {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.ig-sd-picker .ig-modebar {
    flex: 0 0 auto;
}

/* = .advf-grid, two tracks. minmax(0,...) is not decoration: a bare 1fr
   carries an automatic minimum of the item's min-content width, and the
   table rows are wide, so the tracks would refuse to divide and the grid
   would overflow instead. Each grid has its own horizontal scroller from
   scrollX, so there is no .advf-scroll equivalent here. */
.ig-sd-picker .psd-grid {
    flex: 1 1 auto;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 12px;
}

/* = .advf-panel, but WITHOUT .advf-panel's surface. The frame belongs to
   .psd-panel-body (.ig-view-card), one level in, so the heading sits
   outside the card exactly as it does on every other dialog in the app.
   .ig-liquid-glass was tried here and is wrong: it paints a real pour and
   its own backdrop-filter, and nested inside .modal-content (which has a
   backdrop-filter of its own, making it the backdrop root) the blur can
   only sample the modal's painted surface, never the page. The result is
   an opaque slab, not glass.
   min-width:0 for the same reason as minmax(0,...) above; both needed. */
.ig-sd-picker .psd-panel {
    display: flex;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    background: transparent;
    border: none;
    box-shadow: none;
    padding: 0;
}

/* = .advf-panel-head. Never wraps: the two heads sit in one grid row but
   each panel lays out its own contents, so a head that grows to two lines
   on one side pushes that side's grid down while the other stays put. */
.ig-sd-picker .psd-panel-head {
    flex: 0 0 auto;
    display: flex;
    align-items: baseline;
    flex-wrap: nowrap;
    gap: 8px;
    min-height: 18px;
    margin-bottom: 8px;
}

/* SAME VOICE AS .modal-body h4, stated rather than inherited: these
   headings used to BE h4 elements and picked the voice up from the Ignite
   Glass module. In the .psd-grid layout they are spans inside
   .psd-panel-head, so that rule no longer reaches them and every property
   has to be named here or the heading falls back to a bold, mixed-case
   default -- which is what it was doing.

   The old comment claimed this equalled .advf-panel-title. It never did:
   that rule (deliverableAdvancedFilter.css:685) is 13px / 500 / uppercase,
   this one was 14px / 600 / no transform. The dialog family follows the
   DIALOG heading, not the filter's panel strip, so the h4 voice is the one
   copied here. */
.ig-sd-picker .psd-panel-title {
    font-size: 16px;
    font-weight: 300;
    text-transform: uppercase;
    /* The system's PANEL heading ink, not the dialog's body ink.
       --igd-ink is #2c465e -- meant for form values and labels, and far
       too dark for a section heading: it read as the heaviest thing on
       the dialog. #5a6c7c is what .advf-panel-title, .ig-modebar-label
       and label.col-form-label.h4 all use, so this rule finally matches
       the comment above it. A tab's own h4 carries no colour rule at all
       and lands on body #797979 (style.css:11), a shade lighter again --
       the panel token is chosen over that because it is a deliberate
       heading colour rather than an inherited default. */
    color: #5a6c7c;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Dark mode lifts rather than dims, same pair as .advf-panel-title
   (deliverableAdvancedFilter.css:1432): #5a6c7c is unreadable on the dark
   glass. Lives here, not in darkMode.css, because ignite.css loads last
   and this is the rule it belongs beside. */
body.dark-mode .ig-sd-picker .psd-panel-title {
    color: var(--igd-ink-strong);
}

/* Surface, border, radius, shadow and both themes all come from
   .ig-view-card in the markup (ignite.css:627, darkMode.css:6423). Only
   the flex behaviour and the clip are set here. */
.ig-sd-picker .psd-panel-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* Both views fill the body as BLOCKS, not as flex children.
   setDivVisibility writes an inline display:block/none on these two
   (projectSdDialog.js, switchProjectSdView), and an inline display beats
   any display:flex a stylesheet declares. height:100% resolves because
   .psd-panel-body's height is definite. */
.ig-sd-picker .psd-view {
    height: 100%;
}
/* ── The chain THROUGH DataTables ─────────────────────────────────────
   dom is "<RlBfr<t><br>ip>" (_initializeGenericTable, ignite.js:2131):
   one outer div holding the length menu, buttons, filter and processing
   element, then a div for <t>, then a div for <br>, then info and paging.
   All plain blocks. Only the div wrapping <t> may grow; everything beside
   it is pinned to its content, so an empty feature div contributes zero
   rather than a band of dead space.

   :has() is already used at deliverableAdvancedFilter.css:1433, so this
   is not a new dependency. */
.ig-sd-picker .psd-view > .dataTables_wrapper {
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.ig-sd-picker .psd-view .dataTables_wrapper > div {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.ig-sd-picker .psd-view .dataTables_wrapper > div > div {
    flex: 0 0 auto;
}

/* The toolbar row created by PSD_TABLE_DOM (projectSdDialog.js). float is
   dead on a flex item, so the filter is pushed right with an auto margin
   instead — one rule, and it survives the length menu or the buttons
   being absent. .dataTables_processing is position:absolute, so it sits
   in this row without taking width. */
.ig-sd-picker .psd-view .psd-toolbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
}

.ig-sd-picker .psd-view .psd-toolbar > .dataTables_filter {
    margin-left: auto;
}

/* Kills the margin-bottom these carry for the block layout they were
   written for; the row's own gap and margin now do that job. */
.ig-sd-picker .psd-view .psd-toolbar > .dataTables_length,
.ig-sd-picker .psd-view .psd-toolbar > .dt-buttons,
.ig-sd-picker .psd-view .psd-toolbar > .dataTables_filter {
    margin-bottom: 0;
    float: none;
}
.ig-sd-picker .psd-view .dataTables_wrapper > div > div:has(> .dataTables_scroll) {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* Shrinks to fit but never grows, so the frame ends at the last row and its rounded corners meet. */
.ig-sd-picker .psd-view .dataTables_scroll {
    flex: 0 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.ig-sd-picker .psd-view .dataTables_scrollHead {
    flex: 0 0 auto;
}

/* !important is required and is the point. Core DataTables rewrites
   max-height on this element INLINE from oScroll.sY on every
   _fnScrollDraw, so a plain declaration is reinstated on the next draw.
   Scroller is not on the page (see the script list in
   fragments/head-dependencies-full.html), so there is no virtual row
   window whose bookkeeping needs updating alongside this. */
.ig-sd-picker .psd-view .dataTables_scrollBody {
    flex: 0 1 auto;
    min-height: 0;
    height: auto !important;
    max-height: none !important;
}
/* No wrapper surface rule here on purpose. .ig-view-card on
   .psd-panel-body already strips the wrapper's border, radius and shadow
   (ignite.css:641-652), in both themes, and deliberately LEAVES its
   padding alone, because that padding is what insets the rows from the
   card edge. Tree view gets the matching inset from .ig-view-pane. */

/* ═══════════════════════════════════════════════════════════
   IMPORT SERVICE DISCIPLINES DIALOG  (aaaSdImportModal)
   projectSdRolesAndFunctionsTab.html
   ═══════════════════════════════════════════════════════════ */

#aaaSdImportModal .modal-dialog,
#wcIndustryImportModal .modal-dialog,
#cloneWcWorksComponentModal .modal-dialog {
    width: 70vw;
    max-width: 1000px;
    min-width: 560px;
}

#aaaSdImportModal .modal-content,
#wcIndustryImportModal .modal-content,
#cloneWcWorksComponentModal .modal-content {
    height: 80vh;
    display: flex;
    flex-direction: column;
}

#aaaSdImportModal .modal-header,
#aaaSdImportModal .modal-footer,
#wcIndustryImportModal .modal-header,
#wcIndustryImportModal .modal-footer,
#cloneWcWorksComponentModal .modal-header,
#cloneWcWorksComponentModal .modal-footer {
    flex-shrink: 0;
}

#aaaSdImportModal .modal-body,
#wcIndustryImportModal .modal-body,
#cloneWcWorksComponentModal .modal-body {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    padding: 14px 20px;
    display: flex;
    flex-direction: column;
}

#aaaSdImportErrorDiv,
#wcIndustryImportErrorDiv {
    margin: 8px 20px 0 20px;
}

#aaaSdImportGrid,
#wcIndustryImportGrid,
#cloneWcWorksComponentGrid {
    grid-template-columns: minmax(0, 1fr);
    min-width: 0;
}

#aaaSdImportTree .advf-row-alt,
#wcIndustryImportTree .advf-row-alt,
#cloneWcWorksComponentTree .advf-row-alt {
    margin-left: 8px;
}

#docGroupMembersDialog .modal-content {
    max-height: 85vh;
    display: flex;
    flex-direction: column;
}

#docGroupMembersDialog .modal-header,
#docGroupMembersDialog .modal-footer {
    flex-shrink: 0;
}

#docGroupMembersDialog .modal-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

#aaaSdImportHint {
    font-size: 12px;
    color: #8a949f;
    padding: 0 2px 8px 2px;
}


   /* ════════════════════════════════════════════════════════════════
   Mode strip INSIDE a toolbar row (.dt-buttons / .ig-tree-toolbar)
   The free-standing .ig-modebar carries margin-bottom:10px because it
   normally sits on its own line above a card. Dropped into a toolbar
   that centres its items, that margin pushes it a few pixels high.
   Colour and shape are untouched -- this is spacing only.
   ════════════════════════════════════════════════════════════════ */
/* Pin the row gap on BOTH toolbars that can hold the mode bar. The space
   before the pair is container-gap + its own margin-left, and the two
   containers declare different gaps (.ig-tree-toolbar 8px here,
   .dt-buttons its own in igniteButtons.css) -- so the bar landed a few px
   further along in tree view than in table view even once the order
   matched. Naming one value makes the sum identical: 8 + 8 in both.
   :has() scopes this to toolbars that actually carry a mode bar, so no
   other DataTables button strip in the app is touched. */
/* Make the two strips the SAME BOX, not just the same order.
   igniteButtons.css §9 gives .dt-buttons a containment box -- 3px side
   padding and a min-height of control-h + 10 -- so its first button
   starts 3px in and the row is 44px tall. .ig-tree-toolbar has neither,
   so tree view started its ellipsis flush at 0 in a 34px row and the
   whole strip sat left and high of the table-view one. Values copied
   from that block, not chosen: if --ig-control-h or that padding ever
   changes, these follow.
   The gap is pinned on both because the space before the mode bar is
   container-gap + its own margin-left, and the two containers declared
   different gaps. 3 (button margin-right) + 8 + 8 = 19px either side.
   :has() scopes this to toolbars that actually carry a mode bar, so no
   other button strip in the app is touched. */
.dt-buttons:has(> .ig-modebar),
.ig-tree-toolbar:has(> .ig-modebar) {
    gap: 8px;
    /* HEIGHT, not min-height. Measured: the tree row settled at 45px against
       the table row's 44 -- something in it is a pixel over the 34px control
       -- and with align-items:center that half pixel put the mode bar at
       8.42 from the top instead of 7.92. A floor lets the taller row win; a
       fixed height makes the two identical. Same arithmetic as
       igniteButtons.css §9: control height plus its 10px of border, shadow
       and focus-ring room. */
    height: calc(var(--ig-control-h) + 10px);
    min-height: calc(var(--ig-control-h) + 10px);
    padding: 4px 3px 6px 3px;
    align-items: center;
}

/* Measured: the ellipsis starts 6px from the toolbar's left edge in table
   view and 3px in tree view, and both toolbars declare padding-left:3px --
   so the table copy carries an extra 3px margin-left. It comes from the
   jQuery UI classes DataTables stamps on its buttons (ui-button /
   ui-buttonset), NOT from igniteButtons.css §2, which sets margin-right
   only. Zeroed rather than matched on the tree side: §2 is the system's
   button spacing and the left margin is the thing out of line with it.
   That 3px was the whole remaining offset -- the gap from the ellipsis to
   the mode bar already measured 19px in both. */
.dt-buttons:has(> .ig-modebar) > .dt-button {
    margin-left: 0 !important;
}


.dt-buttons > .ig-modebar {
    margin-bottom: 0;
    margin-left: 8px;
    flex: 0 0 auto;
}

/* Same cause as the size difference above: style.css's DataTables SVG block
   sets `.dt-button:not(:hover) svg { color: currentColor }`, which makes the
   glyph inherit the BUTTON's ink (--ig-glass-ink, #2c465e) instead of
   .ell-svg's own resting #6c7a8a. Two classes beats one, so the table copy
   drew noticeably darker dots than the tree copy, which is a plain .btn and
   keeps its own colour. Re-asserted here for the lead ellipsis only -- the
   blanket rule still governs every other DataTables icon. Dark mode is
   untouched: darkMode's `body.dark-mode .ell-svg` carries more weight than
   this and still wins. */
.dt-buttons:has(> .ig-modebar) > .dt-button:first-child .ell-svg,
.ig-tree-lead-btn .ell-svg {
    color: #6c7a8a;
}
/* ════════════════════════════════════════════════════════════════
   Settings page
   Rows, not cards: a dense list of one-line settings reads faster
   with hairline separators than with a stack of bordered boxes.
   .ig-modebar / .ig-mode-btn are reused as-is, so the Table/Tree
   control here is the same object as the one on the Services tab.
   ════════════════════════════════════════════════════════════════ */
.ig-settings {
    max-width: 900px;
    /* Literal: there is no --igd-panel token, and the dark case is handled
       by the body.dark-mode block below rather than by a fallback chain. */
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.10);
    border-radius: 12px;
    overflow: hidden;
}

.ig-settings-head { padding: 14px 20px 12px; border-bottom: 1px solid rgba(0, 0, 0, 0.08); }
.ig-settings-sub { font-size: 13px; color: #5a6c7c; }

.ig-settings-body { display: grid; grid-template-columns: 180px minmax(0, 1fr); }

.ig-settings-nav {
    border-right: 1px solid rgba(0, 0, 0, 0.08);
    padding: 12px 0;
    background: rgba(120, 155, 185, 0.05);
}

.ig-settings-navitem {
    display: block;
    padding: 9px 16px 9px 19px;
    font-size: 13px;
    color: #5a6c7c;
    text-decoration: none;
    border-left: 3px solid transparent;
}
.ig-settings-navitem:hover { background: rgba(96, 125, 149, 0.10); text-decoration: none; }

.ig-settings-navitem-on {
    background: var(--ig-accent-periwinkle, #CBDFF3);
    color: var(--ig-btn-primary-ink, #1e5288);
    border-left-color: var(--ig-accent-steel, #86ADDC);
}

.ig-settings-pane { padding: 4px 20px 18px; }

.ig-settings-secttl {
    font-size: 13px;
    font-weight: 500;
    text-transform: uppercase;
    color: #5a6c7c;
    margin: 16px 0 4px;
}

.ig-settings-row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 0;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.ig-settings-row:last-child { border-bottom: none; }

/* A seven-item checkbox group has no business being squeezed into the
   right-hand control column, so that one row stacks instead. */


.ig-settings-rowtext { flex: 1 1 auto; min-width: 0; }
.ig-settings-rowttl { font-size: 14px; color: var(--igd-ink, #2c3e50); }
.ig-settings-rowhelp { font-size: 12px; color: #5a6c7c; }
.ig-settings-rowctl { flex: 0 0 auto; }

/* The mode bar carries a 10px bottom margin for toolbar use; inside a
   centred row that pushes the pills off-axis. */
.ig-settings-rowctl .ig-modebar { margin-bottom: 0; }


.ig-switch {
    position: relative;
    width: 38px;
    height: 21px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, 0.20);
    border-radius: 11px;
    background: rgba(120, 155, 185, 0.30);
    cursor: pointer;
    transition: background-color 0.14s ease;
}
.ig-switch-on { background: var(--ig-accent-steel, #86ADDC); }

.ig-switch-knob {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    transition: transform 0.14s ease;
}
.ig-switch-on .ig-switch-knob { transform: translateX(17px); }

.ig-switch:focus-visible {
    outline: 2px solid var(--ig-accent-steel, #86ADDC);
    outline-offset: 1px;
}

.ig-settings-btn {
    padding: 5px 14px;
    font-size: 12px;
    border: 1px solid rgba(0, 0, 0, 0.20);
    border-radius: 6px;
    background: transparent;
    color: var(--igd-ink, #495057);
    cursor: pointer;
}
.ig-settings-btn-danger { border-color: #C08A8A; color: #A32D2D; }
.ig-settings-btn-danger:hover { background: rgba(163, 45, 45, 0.08); }

.ig-settings-foot {
    padding: 11px 20px;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    background: rgba(120, 155, 185, 0.05);
    font-size: 12px;
    color: #5a6c7c;
    min-height: 38px;
}
.ig-settings-status > i { color: #3B6D11; margin-right: 6px; }

@media (prefers-reduced-motion: reduce) {
    .ig-switch, .ig-switch-knob { transition: none; }
}


/* Dark mode. Written here rather than in darkMode.css because ignite.css
   loads last and this is the rule it belongs beside -- same reasoning as
   .ig-sd-picker .psd-panel-title above. Tokens, not literals: --dm-surface-*
   and --dm-border are what every other dark panel in the app is built on. */
body.dark-mode .ig-settings {
    background: var(--dm-surface-1, #101a2c);
    border-color: var(--dm-border, rgba(148, 184, 226, 0.13));
}

body.dark-mode .ig-settings-head,
body.dark-mode .ig-settings-nav,
body.dark-mode .ig-settings-foot {
    /* --igd-hairline, not --dm-border: this sits on the dialog glass now,
       and the dialog's own dividers are all drawn with the hairline token. */
    border-color: var(--igd-hairline);
}
/* The nav column carries no fill of its own inside the dialog: the divider
   is what separates it from the pane, and a wash here is one more layer
   between the user and the glass. The page-level (non-dialog) settings card
   keeps its light-mode tint from the base rule above. */
body.dark-mode .ig-settings-foot {
    background: rgba(148, 184, 226, 0.04);
}

.ig-settings-dialog .ig-settings-nav {
    background: transparent;
}

body.dark-mode .ig-settings-sub,
body.dark-mode .ig-settings-secttl,
body.dark-mode .ig-settings-rowhelp,
body.dark-mode .ig-settings-foot,
body.dark-mode .ig-settings-navitem {
    color: #a9bcd4;
}

body.dark-mode .ig-settings-navitem:hover {
    background: rgba(148, 184, 226, 0.10);
}

/* The active item keeps the periwinkle fill: it is the same signal the
   .ig-mode-on pill carries, and both read correctly on the dark panel. */
body.dark-mode .ig-settings-row {
    border-bottom-color: rgba(148, 184, 226, 0.10);
}

body.dark-mode .ig-settings-rowttl,
body.dark-mode .ig-settings-check {
    color: #e8edf5;
}

body.dark-mode .ig-settings-btn {
    border-color: rgba(148, 184, 226, 0.24);
    color: #c8d8f0;
}

body.dark-mode .ig-settings-btn-danger {
    border-color: #8a5050;
    color: #ef8a8a;
}

/* ── Settings inside the dialog ──────────────────────────────────
   The .modal-content glass IS the surface, so the card drops its own
   background, border and radius -- a bordered box inside a bordered
   dialog is the card-in-a-card the Ignite Glass module exists to avoid.
   The dialog header replaces the page heading and the modal footer
   replaces the status bar, so both of those go too. */
.ig-settings-dialog .ig-settings {
    max-width: none;
    background: transparent;
    border: none;
    border-radius: 0;
}

/* Restated for dark, at a specificity that clears body.dark-mode .ig-settings.
   That rule paints --dm-surface-1 for the PAGE-level settings card, where an
   opaque panel is correct. Inside the dialog it is an opaque slab laid over
   the .modal-content glass, which is what killed the show-through across the
   whole panel. Light mode never had the problem because its base rule is a
   single class and the dialog override already outranked it. */
body.dark-mode .ig-settings-dialog .ig-settings {
    background: transparent;
    border-color: transparent;
}

.ig-settings-dialog .ig-settings-head {
    padding: 0 0 10px;
    border-bottom: none;
}

/* A fixed height, not min-height: the sections have different row counts,
   and without it the dialog jumps taller and shorter as you move between
   them. 46vh with the body's own scroll is enough for the longest section
   (Deliverables) on a laptop. */
.ig-settings-dialog .ig-settings-body {
    /* NO SURFACE. The .modal-content glass is the surface, and a pour laid
       on top of it is a second coat over the first: the page stops showing
       through exactly where this panel sits, which is most of the dialog.
       That is what killed the glass, not the absence of a blur.

       A nested backdrop-filter cannot fix it either. .modal-content already
       has one, which makes it the backdrop root, so a filter on a child can
       only sample its parent's painted output and never the page behind.
       An inner panel can never be its own pane of glass; it can only be a
       bounded region OF the one pane. Hairline, radius, nothing else. */
    background: transparent;
    border: 1px solid var(--igd-hairline);
    border-radius: calc(var(--igd-radius) - 2px);
    box-shadow: none;
    overflow: hidden;
    height: 46vh;
}

.ig-settings-dialog .ig-settings-pane {
    overflow-y: auto;
}

/* The footer is the modal's, so the status text sits left of the Close
   button rather than in a bar of its own. .modal-footer is flex with
   justify-content:flex-end, so an auto right margin pushes it over. */
.ig-settings-dialog .ig-settings-status {
    margin-right: auto;
    font-size: 12px;
    color: #5a6c7c;
}

body.dark-mode .ig-settings-dialog .ig-settings-status {
    color: #a9bcd4;
}


/* ════════════════════════════════════════════════════════════════
   Theme strip (.ig-themebar)
   Four icon buttons over one sliding indicator. The indicator is a
   single absolutely positioned element and the selection moves it
   with a transform, so the animation runs on the compositor and the
   buttons themselves never reflow. data-on carries the index, which
   keeps the geometry in CSS rather than as inline style from JS.
   The overshoot in the easing curve is what gives it the small
   settle at the end instead of a flat slide.
   ════════════════════════════════════════════════════════════════ */
.ig-themebar {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 2px;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 8px;
    background: rgba(120, 155, 185, 0.10);
}

.ig-theme-ind {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 30px;
    height: 26px;
    border-radius: 6px;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.10);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.10);
    transition: transform 0.34s cubic-bezier(0.34, 1.28, 0.64, 1);
}

/* One step per button width. Four literals beat a calc chain that no
   one can read, and the option list is fixed. */
.ig-themebar[data-on="0"] .ig-theme-ind { transform: translateX(0); }
.ig-themebar[data-on="1"] .ig-theme-ind { transform: translateX(30px); }
.ig-themebar[data-on="2"] .ig-theme-ind { transform: translateX(60px); }
.ig-themebar[data-on="3"] .ig-theme-ind { transform: translateX(90px); }

.ig-theme-btn {
    position: relative;
    z-index: 1;                 /* above the indicator, so the icon stays legible */
    display: inline-flex;       /* the SVG is a block, so it needs centring */
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    padding: 0;
    border: 0;
    background: transparent;
    color: #5a6c7c;
    line-height: 1;
    cursor: pointer;
    transition: color 0.14s ease;
}

.ig-theme-btn:hover { color: var(--igd-ink, #2c3e50); }
/* Same ink as the other three. Selection is carried by the sliding indicator
   behind the icon; tinting the glyph blue as well meant the strip showed two
   different colours at rest. */
.ig-theme-btn.ig-theme-on { color: #5a6c7c; }

.ig-theme-btn:focus-visible {
    outline: 2px solid var(--ig-accent-steel, #86ADDC);
    outline-offset: 1px;
    border-radius: 6px;
}

/* The schedule row appears and disappears with the mode, so it fades in
   rather than snapping into the middle of the panel. */
.ig-theme-times {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    animation: igThemeFadeIn 0.22s ease;
}

@keyframes igThemeFadeIn {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: none; }
}

.ig-theme-timeitem {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #5a6c7c;
    font-size: 12px;
}

/* A label glyph, not a control. 18px is sized for a 30px button in the strip;
   beside a 30px input it competes with the digits, so it steps down. Weight is
   held by scaling stroke-width with it, or the line thins as the icon shrinks. */
.ig-theme-timeitem .ig-thm-svg {
    width: 15px;
    height: 15px;
    stroke-width: 2;
}

.ig-theme-time {
    width: 106px;
    height: 30px;
    padding: 3px 8px;
    font-size: 12px;
}

@media (prefers-reduced-motion: reduce) {
    .ig-theme-ind { transition: none; }
    .ig-theme-times { animation: none; }
}

/* Dark. Same tokens as the settings panel above: the strip sits on
   --dm-surface-1, so the indicator has to be a lift off that surface
   rather than white, which would read as a hole. */
body.dark-mode .ig-themebar {
    border-color: var(--dm-border, rgba(148, 184, 226, 0.16));
    background: rgba(148, 184, 226, 0.08);
}

body.dark-mode .ig-theme-ind {
    background: rgba(148, 184, 226, 0.18);
    border-color: rgba(148, 184, 226, 0.28);
    box-shadow: none;
}

body.dark-mode .ig-theme-btn { color: #a9bcd4; }
body.dark-mode .ig-theme-btn:hover { color: #e8edf5; }
body.dark-mode .ig-theme-btn.ig-theme-on { color: #a9bcd4; }

body.dark-mode .ig-theme-timeitem { color: #a9bcd4; }


/* ── Settings nav, dark ──────────────────────────────────────────────
   The light theme's periwinkle slab does not survive the move to dark
   glass: it is an opaque bright rectangle on a translucent surface, and
   the generic body.dark-mode .ig-settings-navitem colour rule outranks
   .ig-settings-navitem-on, so the label was painting muted grey on top
   of it. Selection is carried here by a tint plus the accent edge, the
   same signal the rest of the dark UI uses. */
body.dark-mode .ig-settings-navitem-on {
    background: rgba(112, 168, 224, 0.16);
    color: #e3edfb;
    border-left-color: var(--igd-accent, #4d8ef0);
    /* No ring. It boxed the item, and with the clearer pour the tint plus the
       accent edge already carry the selection — the ring only added a second
       rectangle to a dialog we are trying to de-box. */
    box-shadow: none;
}

body.dark-mode .ig-settings-navitem-on:hover {
    background: rgba(112, 168, 224, 0.22);
    color: #f0f5fc;
}

/* Movement between items, both themes. Without it the selection jumps
   between two flat states, which is what makes it read as unfinished. */
.ig-settings-navitem {
    transition: background 0.16s var(--ig-ease, ease),
        color 0.16s var(--ig-ease, ease),
        border-left-color 0.16s var(--ig-ease, ease);
}

@media (prefers-reduced-motion: reduce) {
    .ig-settings-navitem { transition: none; }
}



/* ── Settings dialog: a clearer pour, this dialog only ───────────────
   The panel inside is already surfaceless, so the only thing between the
   user and the page is .modal-content's own pour. In dark that token is
   0.64 over 0.52 -- tuned for small dialogs sitting over dense tables,
   and near-opaque across a box this large, which is why it reads as a
   slab rather than as glass.

   The token is REDEFINED here rather than the background being restated.
   darkMode.css declares `background-image: var(--igd-pop-bg) !important`
   on .modal-content, so an ordinary override loses; custom properties
   inherit, so changing the value the !important rule resolves against
   wins without needing !important of its own -- and the border, which
   reads --igd-pop-border from the same place, follows automatically.

   Scoped to .ig-settings-dialog: every other dialog in the app keeps the
   shared pour. If the whole system should get clearer glass, move these
   two values into the body.dark-mode block in the Ignite Glass module
   instead, which is the one place that changes them all together. */
body.dark-mode .ig-settings-dialog {
    /* Thinner than the shared dark pour AND thinner than the previous local
       one. At 0.40/0.28 over a 0.28 black backdrop the dark half of the page
       stopped showing through, so the dialog read as a slab. Light mode's
       pour is ~0.60/0.44 over a page that is bright to begin with; dark needs
       to go further down to reach the same show-through. */
    --igd-pop-bg: linear-gradient(160deg,
            rgba(40, 50, 66, 0.26) 0%,
            rgba(26, 33, 45, 0.16) 100%);
    --igd-pop-border: rgba(255, 255, 255, 0.16);
    /* NOT raised with the pour. That was the mistake: a 0.22 hairline draws a
       visible rectangle around .ig-settings-body and a hard rule down the nav,
       which is exactly what makes the settings panel read as a second box
       inside the dialog. Light mode's frame is rgba(0,0,0,0.08) and is almost
       invisible; this is its dark equivalent. The same token also feeds the
       modal header/footer dividers, which should be equally quiet here. */
    --igd-hairline: rgba(148, 184, 226, 0.09);
}

/* Nav divider only — the body panel's own frame comes from the token above.
   Stated so the column still separates from the pane without the full
   four-sided outline reappearing. */
body.dark-mode .ig-settings-dialog .ig-settings-nav {
    border-right-color: rgba(148, 184, 226, 0.12);
}