﻿/**
 * Optegrity UI — shared design system
 *
 * A Microsoft Teams / Fluent 2 flavoured layer that sits on top of Bootstrap 4
 * and the Stisla template. Loaded AFTER style.css and components.css so it wins.
 *
 * ONE copy for the whole suite: \OptegrityWebApps\assets\css\optegrity-ui.css.
 * After editing, bump ?rnd= on this file in all six apps' Views/head.html.
 *
 * Contents
 *   1. Design tokens
 *   2. Legacy overrides (rules in style.css that fight the system)
 *   3. Buttons
 *   4. Cards
 *   5. Tabs
 *   6. Modal footers
 *  14. Toasts — iziToast, and the toast colour standard
 *  15. Template Management — the template catalogue and its importer
 *  16. Drop-downs — bootstrap-select
 *  17. Form fields — inputs, textareas, addons
 *  23. Grid cell menu — right-click Copy on an ag-Grid cell
 *  24. Print previews — the report sheet and its print rules
 *
 * ---------------------------------------------------------------------------
 * BUTTON COLOUR STANDARD — colour is chosen by what the action DOES, never by
 * what looks nice on the page. Every page in every app follows this table.
 *
 *   btn-primary    Brand fill. The ONE main commit action of a view.
 *                  Save, Submit, Add, Create, Update, Apply, Send.
 *                  Never put two on the same row of buttons.
 *
 *   btn-secondary  Outlined neutral. Everything that is not the commit action
 *                  and does not destroy anything. The way out, a switch, or a
 *                  utility that only moves data around.
 *                  Close, Cancel, Reset, Back, View Form, Show Table/Card View,
 *                  Print, Export, Export to Excel, Send Email, Share.
 *                  Every button has a visible border - there is no borderless
 *                  variant, because an outline-less button reads as empty.
 *
 *   btn-danger     Red fill. Destroys or removes data, or records a terminal
 *                  negative decision in an approve/reject pair.
 *                  Delete, Remove, Deactivate, Clear All, Reject, Decline.
 *
 *   btn-success    Green fill. Positive completion of a workflow ONLY.
 *                  Approve, Complete, Mark Resolved, Close Issue.
 *                  NOT for Save / Export / Print — those are not completions.
 *
 *   btn-warning    Amber fill. Proceed-with-caution, reversible.
 *                  Override, Force, Reopen.
 *
 *   btn-subtle     Retired. Kept only as an alias of btn-secondary so any
 *                  stray markup still renders a proper bordered button.
 * ---------------------------------------------------------------------------
 */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

:root {
    /* --- BRAND -----------------------------------------------------------
       Optegrity brand palette:
         Anti-flash white  #EBEBEB    Denim   #2F60AE    Azure  #4285F4
         Malachite         #0ED145    Davy's grey #545454   Powder blue #A9CBF0

       Denim carries the primary fill: white text on it measures 6.17:1, well
       clear of the 4.5:1 needed for a button label. Azure is the lighter
       brand tone at 3.56:1, so it is used for accents rather than for any
       surface that has to hold white text.                                  */
    --oui-brand: #2f60ae; /* Denim */
    --oui-brand-hover: #2a5499;
    --oui-brand-pressed: #254a88;
    --oui-brand-accent: #4285f4; /* Azure - accent bars, brand-on-dark */
    /* Powder blue at full strength only reaches 3.67:1 behind Denim text, so
       the selected-pill tint is a lightened step off it (5.28:1). */
    --oui-brand-tint: #e6eefa;
    --oui-brand-tint-hover: #d6e4f6;
    --oui-brand-muted: #a9cbf0; /* Powder blue - borders on tinted surfaces */
    --oui-brand-fg: #ffffff; /* text on top of --oui-brand */
    /* --- SEMANTIC -------------------------------------------------------
       Malachite is a vivid green: white text on it is only 2.05:1, so it
       cannot be a filled button. It stays the status colour for accent bars
       and badges, and filled success buttons use a darkened sibling.        */
    --oui-success: #09842c; /* filled button - 4.83:1 with white */
    --oui-success-hover: #076e24;
    --oui-success-pressed: #055b1d;
    --oui-success-accent: #0ed145; /* Malachite - accent bars, status dots */
    /* The brand palette has no red or amber. These are chosen to sit with
       Denim and to clear 4.5:1 against white. */
    --oui-danger: #c4314b;
    --oui-danger-hover: #a72941;
    --oui-danger-pressed: #8b2236;
    --oui-warning: #c2540a;
    --oui-warning-hover: #a64709;
    --oui-warning-pressed: #8a3b07;
    /* Semantic tints, the same job --oui-brand-tint does for the brand: a
       surface a status colour can sit on without becoming a filled block.
       Each keeps its own status colour well past the 3:1 a non-text glyph
       needs — success 4.22:1, warning 4.05:1, danger 4.58:1. */
    --oui-success-tint: #e3f4e8;
    --oui-warning-tint: #fdeee2;
    --oui-danger-tint: #fae8eb;
    /* --- NEUTRALS -------------------------------------------------------
       Davy's grey is the brand's secondary text colour (7.57:1 on white);
       Anti-flash white is the canvas and the hairline colour.              */
    --oui-bg: #ffffff;
    --oui-bg-hover: #f0f0f0;
    --oui-bg-pressed: #e0e0e0;
    /* Anti-flash white. The canvas colour behind cards — available for pages
       that set their own background; no rule in this file claims it. */
    --oui-bg-canvas: #ebebeb;
    --oui-fg: #242424; /* primary text   */
    --oui-fg-2: #545454; /* Davy's grey - secondary text */
    --oui-fg-3: #757575; /* muted / hints  */
    --oui-stroke: #d1d1d1; /* control borders */
    --oui-stroke-2: #ebebeb; /* Anti-flash white - dividers */
    /* Disabled label. 1.88:1 on white, which is deliberate and allowed: WCAG
       exempts inactive controls, and the point of the colour is to read as
       unavailable. */
    --oui-fg-disabled: #bdbdbd;
    /* Scrollbar thumb. 1.74:1 on white — a scrollbar is browser furniture, not
       a UI control carrying meaning, and it darkens to --oui-fg-3 (4.61:1) on
       hover. Do not reuse this for anything that has to be seen. */
    --oui-scroll-thumb: #c4c4c4;
    /* A hair off white, for a surface that should read as inert. */
    --oui-bg-subtle: #fafafa;
    /* --- SHAPE ---------------------------------------------------------- */
    --oui-radius-sm: 4px;
    --oui-radius-md: 6px;
    --oui-radius-lg: 8px;
    /* --- ELEVATION (Fluent 2 shadow ramp) ------------------------------- */
    --oui-shadow-2: 0 1px 2px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);
    --oui-shadow-4: 0 2px 4px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);
    --oui-shadow-8: 0 4px 8px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);
    --oui-shadow-16: 0 8px 16px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);
    /* --- MOTION --------------------------------------------------------- */
    --oui-ease: cubic-bezier(.33,0,.67,1);
    --oui-duration: .1s;
    /* --- TYPE -----------------------------------------------------------
       Set to "Segoe UI", ... on the html element for the full Teams look.
       Left as `inherit` so the app keeps Nunito everywhere by default.     */
    --oui-font: inherit;
}

/* ==========================================================================
   2. Legacy overrides
   ========================================================================== */

/* style.css turns EVERY button white on hover, which forces each variant to
   fight back with !important. Neutralise it once, here. */
.btn:not(.btn-social):not(.btn-social-icon):hover,
.btn:not(.btn-social):not(.btn-social-icon):focus,
.btn:not(.btn-social):not(.btn-social-icon):active {
    background-color: inherit;
}

/* ==========================================================================
   3. Buttons
   ========================================================================== */

.btn {
    font-family: var(--oui-font);
    font-weight: 600;
    font-size: 14px;
    line-height: 20px;
    letter-spacing: 0;
    min-height: 32px;
    padding: 5px 12px;
    border: 1px solid transparent;
    border-radius: var(--oui-radius-sm);
    box-shadow: none;
    transition: background-color var(--oui-duration) var(--oui-ease), border-color var(--oui-duration) var(--oui-ease), color var(--oui-duration) var(--oui-ease);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    vertical-align: middle;
    white-space: nowrap;
    cursor: pointer;
}

    /* Fluent keeps icon and label optically joined; kill the template's margins. */
    .btn > i,
    .btn > .ion,
    .btn > .fas,
    .btn > .far,
    .btn > .fal,
    .btn > .fab {
        margin: 0 !important;
        font-size: 14px;
        line-height: 1;
    }

/* --- Focus: Fluent double stroke (dark inner, light outer) --------------- */
.btn:focus-visible {
    outline: 2px solid var(--oui-fg) !important;
    outline-offset: 1px;
    border-radius: var(--oui-radius-sm);
}

.btn:disabled,
.btn.disabled {
    background-color: var(--oui-bg-pressed) !important;
    border-color: var(--oui-bg-pressed) !important;
    color: var(--oui-fg-disabled) !important;
    box-shadow: none !important;
    cursor: not-allowed;
    opacity: 1;
}

/* --- PRIMARY: the single commit action ---------------------------------- */
.btn-primary,
.btn-primary.disabled {
    background-color: var(--oui-brand) !important;
    border-color: var(--oui-brand) !important;
    color: var(--oui-brand-fg) !important;
    box-shadow: none;
}

    .btn-primary:hover,
    .btn-primary:focus {
        background-color: var(--oui-brand-hover) !important;
        border-color: var(--oui-brand-hover) !important;
        color: var(--oui-brand-fg) !important;
    }

    .btn-primary:active {
        background-color: var(--oui-brand-pressed) !important;
        border-color: var(--oui-brand-pressed) !important;
        color: var(--oui-brand-fg) !important;
    }

/* --- SECONDARY: the way out (Close / Cancel / Reset / non-committing) ----
   `.btn-default` is a Bootstrap 3 class that does not exist in Bootstrap 4,
   so ~30 "Close" buttons across the app currently render as bare invisible
   text. Aliasing it here fixes them all at once; call sites are being moved
   to .btn-secondary as they are touched.                                   */
.btn-secondary,
.btn-secondary.disabled,
.btn-default,
.btn-default.disabled,
.btn-subtle,
.btn-subtle.disabled {
    background-color: var(--oui-bg) !important;
    border-color: var(--oui-stroke) !important;
    color: var(--oui-fg) !important;
    box-shadow: none;
}

    .btn-secondary:hover,
    .btn-secondary:focus,
    .btn-default:hover,
    .btn-default:focus,
    .btn-subtle:hover,
    .btn-subtle:focus {
        background-color: var(--oui-bg-hover) !important;
        border-color: var(--oui-stroke) !important;
        color: var(--oui-fg) !important;
    }

    .btn-secondary:active,
    .btn-default:active,
    .btn-subtle:active {
        background-color: var(--oui-bg-pressed) !important;
        border-color: var(--oui-stroke) !important;
        color: var(--oui-fg) !important;
    }

/* --- DANGER: destroys data ---------------------------------------------- */
.btn-danger,
.btn-danger.disabled {
    background-color: var(--oui-danger) !important;
    border-color: var(--oui-danger) !important;
    color: #fff !important;
    box-shadow: none;
}

    .btn-danger:hover,
    .btn-danger:focus {
        background-color: var(--oui-danger-hover) !important;
        border-color: var(--oui-danger-hover) !important;
        color: #fff !important;
    }

    .btn-danger:active {
        background-color: var(--oui-danger-pressed) !important;
        border-color: var(--oui-danger-pressed) !important;
        color: #fff !important;
    }

/* --- SUCCESS: positive completion of a workflow ------------------------- */
.btn-success,
.btn-success.disabled {
    background-color: var(--oui-success) !important;
    border-color: var(--oui-success) !important;
    color: #fff !important;
    box-shadow: none;
}

    .btn-success:hover,
    .btn-success:focus {
        background-color: var(--oui-success-hover) !important;
        border-color: var(--oui-success-hover) !important;
        color: #fff !important;
    }

    .btn-success:active {
        background-color: var(--oui-success-pressed) !important;
        border-color: var(--oui-success-pressed) !important;
        color: #fff !important;
    }

/* --- WARNING: proceed with caution, reversible -------------------------- */
.btn-warning,
.btn-warning.disabled {
    background-color: var(--oui-warning) !important;
    border-color: var(--oui-warning) !important;
    color: #fff !important;
    box-shadow: none;
}

    .btn-warning:hover,
    .btn-warning:focus {
        background-color: var(--oui-warning-hover) !important;
        border-color: var(--oui-warning-hover) !important;
        color: #fff !important;
    }

    .btn-warning:active {
        background-color: var(--oui-warning-pressed) !important;
        border-color: var(--oui-warning-pressed) !important;
        color: #fff !important;
    }

/* The AI buttons carry their own animated surface from style.css; keep the
   system's geometry and focus behaviour but let that artwork show through. */
.btn.AIButton,
.btn.AIButton:hover,
.btn.AIButton:focus,
.btn.AIButton:active {
    background-color: transparent !important;
    border-color: transparent !important;
}

/* --- Sizes --------------------------------------------------------------- */
.btn.btn-sm {
    min-height: 24px;
    padding: 2px 8px;
    font-size: 12px;
    line-height: 16px;
    gap: 4px;
}

    .btn.btn-sm > i {
        font-size: 12px;
    }

.btn.btn-lg {
    min-height: 40px;
    padding: 9px 16px;
    font-size: 16px;
    line-height: 20px;
    gap: 8px;
    margin-left: 0 !important;
}

    .btn.btn-lg > i {
        font-size: 16px;
    }

/* Square icon-only button — used for row actions in grids and card footers. */
.btn.btn-icon-only {
    min-width: 32px;
    padding: 5px;
    gap: 0;
}

    .btn.btn-icon-only.btn-sm {
        min-width: 24px;
        padding: 2px;
    }

    .btn.btn-icon-only.btn-lg {
        min-width: 40px;
        padding: 9px;
    }

/* Buttons emitted as bare siblings (most toolbars here are not flex
   containers) still need a predictable gap. One rule owns it, so no call site
   has to carry an inline margin. */
.btn + .btn,
.btn + .dropdown,
.dropdown + .btn {
    margin-left: 8px;
}

/* The template's utility margins would stack on top of the rule above. */
.btn.mr-1,
.btn.mr-2,
.btn.ml-1,
.btn.ml-2 {
    margin-right: 0 !important;
}

    .btn.mr-1 + .btn,
    .btn.mr-2 + .btn {
        margin-left: 8px !important;
    }

/* Toolbar / footer button rows: consistent gap, no stray margins. */
.oui-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

    .oui-actions > .btn {
        margin: 0 !important;
    }

    /* Pushes everything after it to the right — Teams puts the commit action
       on the right of a footer and utilities on the left. */
    .oui-actions > .oui-spacer {
        margin-left: auto;
    }

/* ==========================================================================
   4. Cards
   ========================================================================== */

.card {
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-2);
    margin-bottom: 16px;
    transition: box-shadow var(--oui-duration) var(--oui-ease), border-color var(--oui-duration) var(--oui-ease);
    position: relative;
}

    /* The template painted a blurred glow behind every card. Teams cards are
       flat with a crisp hairline, so remove it. */
    .card:before {
        display: none !important;
        content: none !important;
    }

    /* The template already made this a flex row. Do NOT add `gap` or
       `flex-wrap` here: many headers hold Bootstrap columns (col-7 + col-5,
       col-md-6 + col-md-6) that already total 100%, and any gap tips them
       over the line, wrapping the right-hand column onto its own row where
       `text-right` then reads as centred. Button spacing is owned by the
       `.btn + .btn` rule instead. */
    .card .card-header {
        display: flex;
        align-items: center;
        min-height: 48px;
        padding: 12px 16px !important;
        background: transparent;
        border-bottom: 1px solid var(--oui-stroke-2);
        border-radius: var(--oui-radius-lg) var(--oui-radius-lg) 0 0;
    }

        .card .card-header h4,
        .card .card-header h5,
        .card .card-header .card-title {
            font-family: var(--oui-font);
            font-size: 16px;
            font-weight: 600;
            line-height: 22px;
            color: var(--oui-fg);
            margin: 0;
            padding: 0;
        }

        /* Actions parked on the right of a header. */
        .card .card-header .card-header-action {
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 8px;
        }

            /* This is a gap container, so the sibling margin rule would
               double the spacing. */
            .card .card-header .card-header-action > .btn {
                margin: 0 !important;
            }

            /* style.css gives every button inside a card header a 10px
               radius, and any button following an h4 a 30px pill with 13px
               side padding. Both are more specific than the base .btn rule,
               so a header button ended up a different shape from the same
               button anywhere else. */
            .card .card-header .btn:not(.note-btn),
            .card .card-header h4 + .card-header-action .btn,
            .card .card-header .card-header-action .btn {
                border-radius: var(--oui-radius-sm) !important;
                padding-left: 12px !important;
                padding-right: 12px !important;
                font-size: 14px;
            }

                .card .card-header h4 + .card-header-action .btn.btn-icon-only,
                .card .card-header .card-header-action .btn.btn-icon-only {
                    padding-left: 5px !important;
                    padding-right: 5px !important;
                }

    .card .card-body {
        padding: 16px !important;
        background: transparent;
        color: var(--oui-fg-2);
    }

    .card .card-footer {
        padding: 12px 16px !important;
        background: transparent;
        border-top: 1px solid var(--oui-stroke-2);
        border-radius: 0 0 var(--oui-radius-lg) var(--oui-radius-lg);
        color: var(--oui-fg-3);
    }

    /* Header/footer sit flush against the card edge, so drop the template's
       blanket bottom padding. */
    .card .card-header,
    .card .card-body,
    .card .card-footer {
        padding-bottom: initial;
    }

/* Layout-only wrapper: keeps .card's spacing rhythm but drops the surface, for
   places that used .card purely as a grid container and then stripped the
   chrome back off with inline styles. */
.card.card-bare {
    background: none;
    border: none;
    box-shadow: none;
    margin-bottom: 0;
}

    .card.card-bare > .card-header,
    .card.card-bare > .card-body,
    .card.card-bare > .card-footer {
        border: none;
        background: none;
        padding: 0 !important;
        min-height: 0;
    }

/* Interactive cards (issue tiles, solution tiles) lift on hover the way Teams
   list items do. Opt in with .card-interactive. */
.card-interactive {
    cursor: pointer;
}

    .card-interactive:hover {
        box-shadow: var(--oui-shadow-8);
        border-color: var(--oui-stroke);
    }

    .card-interactive:active {
        box-shadow: var(--oui-shadow-4);
    }

/* Status accent — a 3px bar down the leading edge instead of tinting the whole
   card body. Replaces the ad-hoc `background-color:#fbebeb` on overdue cards. */
.card-accent-danger,
.card-accent-warning,
.card-accent-success,
.card-accent-brand {
    overflow: hidden;
}

    .card-accent-danger:after,
    .card-accent-warning:after,
    .card-accent-success:after,
    .card-accent-brand:after {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        width: 3px;
        border-radius: var(--oui-radius-lg) 0 0 var(--oui-radius-lg);
    }

.card-accent-danger:after {
    background: var(--oui-danger);
}

.card-accent-warning:after {
    background: var(--oui-warning);
}

/* Status bars use the vivid brand tones — nothing sits on top of them, so
   Malachite and Azure can be used at full strength here. */
.card-accent-success:after {
    background: var(--oui-success-accent);
}

.card-accent-brand:after {
    background: var(--oui-brand-accent);
}

/* ==========================================================================
   5. Tabs
   ========================================================================== */

/* --- Horizontal tabs: Teams underline style ----------------------------- */
.nav-tabs {
    border-bottom: 1px solid var(--oui-stroke-2);
    gap: 4px;
    flex-wrap: wrap;
}

    .nav-tabs .nav-item {
        margin-bottom: -1px;
    }

    .nav-tabs .nav-item .nav-link {
        position: relative;
        font-family: var(--oui-font);
        font-size: 14px;
        font-weight: 400;
        line-height: 20px;
        color: var(--oui-fg-2);
        background: transparent;
        border: none;
        border-radius: var(--oui-radius-sm) var(--oui-radius-sm) 0 0;
        padding: 10px 12px 12px;
        transition: color var(--oui-duration) var(--oui-ease), background-color var(--oui-duration) var(--oui-ease);
        cursor: pointer;
    }

        .nav-tabs .nav-item .nav-link:hover {
            color: var(--oui-fg);
            background: var(--oui-bg-hover);
            border: none;
        }

        .nav-tabs .nav-item .nav-link:active {
            background: var(--oui-bg-pressed);
        }

        /* The indicator: a 2px brand bar with rounded ends, inset from the
           edges of the tab exactly like Fluent 2. */
        .nav-tabs .nav-item .nav-link:after {
            content: "";
            position: absolute;
            left: 12px;
            right: 12px;
            bottom: 0;
            height: 2px;
            border-radius: 2px 2px 0 0;
            background: transparent;
            transition: background-color var(--oui-duration) var(--oui-ease);
        }

        /* This project's bootstrap.min.css is customised and fills the active
           tab with `background-color: #336fd4 !important`, which left the
           label unreadable once the text went brand-coloured. The underline
           treatment has to win that declaration outright. */
        .nav-tabs .nav-item.show .nav-link,
        .nav-tabs .nav-item .nav-link.active,
        .nav-tabs .nav-item .nav-link.active:hover,
        .nav-tabs .nav-item .nav-link.active:focus {
            color: var(--oui-brand) !important;
            font-weight: 600;
            background: transparent !important;
            background-color: transparent !important;
            border: none !important;
        }

            .nav-tabs .nav-item .nav-link.active:after {
                background: var(--oui-brand);
            }

        .nav-tabs .nav-item .nav-link:focus-visible {
            outline: 2px solid var(--oui-fg);
            outline-offset: -2px;
        }

        /* Count/status pill riding alongside a tab label. */
        .nav-tabs .nav-item .nav-link .badge {
            font-size: 11px;
            font-weight: 600;
            padding: 2px 6px;
            border-radius: 10px;
            margin-left: 6px;
            vertical-align: middle;
        }

/* --- Vertical pills: Teams left-rail style ------------------------------ */
.nav-pills .nav-item .nav-link {
    position: relative;
    font-family: var(--oui-font);
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
    color: var(--oui-fg-2);
    background: transparent;
    border-radius: var(--oui-radius-sm);
    padding: 8px 12px !important;
    margin-bottom: 2px;
    box-shadow: none;
    transition: background-color var(--oui-duration) var(--oui-ease), color var(--oui-duration) var(--oui-ease);
}

    .nav-pills .nav-item .nav-link:hover {
        background: var(--oui-bg-hover);
        color: var(--oui-fg);
    }

.nav-pills .nav-link.active,
.nav-pills .show > .nav-link,
.nav-pills .nav-item .nav-link.active {
    background: var(--oui-brand-tint) !important;
    border: 1px solid var(--oui-brand-muted);
    color: var(--oui-brand) !important;
    font-weight: 600;
    box-shadow: none !important;
    /* Keep the selected item the same height as its unselected siblings
       once the border is added. */
    padding: 7px 11px !important;
}

    .nav-pills .nav-item .nav-link.active:hover {
        background: var(--oui-brand-tint-hover) !important;
    }

/* Leading rail marker on the selected item, Teams-style. */
.nav-pills.flex-column .nav-item .nav-link.active:before {
    content: "";
    position: absolute;
    left: 0;
    top: 8px;
    bottom: 8px;
    width: 3px;
    border-radius: 0 2px 2px 0;
    background: var(--oui-brand);
}

.nav-pills .nav-item .nav-link .badge {
    padding: 2px 6px;
    border-radius: 10px;
    margin-left: 6px;
    font-size: 11px;
}

/* --- Tab panels ---------------------------------------------------------- */
.tab-content > .tab-pane {
    padding: 16px 0;
    line-height: 20px;
    color: var(--oui-fg-2);
}

.tab-content.no-padding > .tab-pane {
    padding: 0;
}

.tab-bordered .tab-pane {
    padding: 16px;
    border: 1px solid var(--oui-stroke-2);
    border-top: none;
    border-radius: 0 0 var(--oui-radius-md) var(--oui-radius-md);
    margin-top: 0;
}

/* Table toolbars are emitted inconsistently: App Configuration wraps them in a
   .card-header, but most pages drop the buttons in as bare siblings of the
   grid. Rather than chase each shape, the adjacent-sibling universal selector
   covers all of them - a grid gets top spacing whenever anything precedes it,
   and none when it is the first thing in its container.

   The exclusions matter. Several grids run with `popupParent: document.body`,
   and AG Grid then appends its own themed wrapper - `ag-styled-root
   ag-theme-inherit-N` - straight onto the body. That carries "ag-theme-" in
   its class list, so without these guards every popup cell editor and filter
   would be pushed down by 12px. */
* + [class*="ag-theme-"]:not(.ag-popup):not(.ag-styled-root),
* + .dataTables_wrapper,
* + .table-responsive {
    margin-top: 12px;
}

/* Backstop: anything AG Grid mounts directly on the body is chrome, not
   page content, whatever it ends up being called. */
body > [class*="ag-theme-"],
body > .ag-styled-root {
    margin-top: 0 !important;
}

/* Dashboard tiles.
   gridstack.min.css was customised with the old card chrome - a 15px radius
   and a 1rem glow - on the drag wrapper. That wrapper is the surface the user
   actually sees, so it takes the card treatment and the .card nested inside it
   goes flat, otherwise the two borders and shadows stack. */
.grid-stack > .grid-stack-item > .grid-stack-item-content {
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg) !important;
    box-shadow: var(--oui-shadow-2) !important;
}

.grid-stack > .grid-stack-item.ui-draggable-dragging > .grid-stack-item-content,
.grid-stack > .grid-stack-item.ui-resizable-resizing > .grid-stack-item-content {
    box-shadow: var(--oui-shadow-16) !important;
    opacity: 1;
}

.grid-stack-item-content > .card {
    height: 100%;
    margin-bottom: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
}

    /* The tile clips its own corners, so the inner header must not try to
       round them a second time. */
    .grid-stack-item-content > .card > .card-header {
        border-radius: 0;
    }

/* ==========================================================================
   6. Statistic cards
   ==========================================================================
   The template floated an 80px circle next to the text, which made these
   cards tall, unequal in height, and heavy with saturated colour. Rebuilt as
   a compact horizontal tile: a small rounded icon square in brand tint, then
   a label over a value. */

.card.card-statistic-1,
.card.card-statistic-2 {
    display: flex !important;
    /* Bootstrap's .card is `flex-direction: column`. Without resetting it the
       icon and the text stack and `align-items: center` centres them, which
       is not the intended side-by-side tile. */
    flex-direction: row !important;
    align-items: center;
    gap: 12px;
    width: 100%;
    /* Equal heights across the row regardless of content length. */
    height: calc(100% - 16px);
    min-height: 76px;
    padding: 14px 16px;
    overflow: hidden;
}

    .card.card-statistic-1 .card-icon,
    .card.card-statistic-2 .card-icon {
        flex: 0 0 auto;
        width: 40px !important;
        height: 40px !important;
        margin: 0 !important;
        float: none !important;
        border-radius: var(--oui-radius-md) !important;
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 1 !important;
        /* Tinted rather than filled: four saturated circles in a row was the
           loudest thing on the page. */
        background-color: var(--oui-brand-tint) !important;
        color: var(--oui-brand) !important;
    }

        .card.card-statistic-1 .card-icon i,
        .card.card-statistic-2 .card-icon i {
            font-size: 17px !important;
            line-height: 1 !important;
            margin: 0 !important;
            color: inherit !important;
        }

    .card.card-statistic-1 .card-wrap,
    .card.card-statistic-2 .card-wrap {
        flex: 1 1 auto;
        min-width: 0;
        text-align: left;
    }

    .card.card-statistic-1 .card-header,
    .card.card-statistic-2 .card-header {
        display: block !important;
        min-height: 0 !important;
        padding: 0 !important;
        border: none !important;
        background: none !important;
    }

        .card.card-statistic-1 .card-header h4,
        .card.card-statistic-2 .card-header h4 {
            font-size: 12px !important;
            font-weight: 400 !important;
            line-height: 16px !important;
            letter-spacing: 0;
            color: var(--oui-fg-3) !important;
            margin: 0 !important;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

    .card.card-statistic-1 .card-body,
    .card.card-statistic-2 .card-body {
        padding: 2px 0 0 !important;
        font-size: 15px !important;
        font-weight: 600 !important;
        line-height: 20px !important;
        color: var(--oui-fg) !important;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

        /* A select or link sitting in the value slot should not inherit the
           600-weight display treatment. */
        .card.card-statistic-1 .card-body .select2,
        .card.card-statistic-1 .card-body .form-group,
        .card.card-statistic-1 .card-body form {
            font-size: 14px;
            font-weight: 400;
            margin: 0;
        }

/* ==========================================================================
   7. Command bar
   ==========================================================================
   The Dashboard header was a full card wrapping a row of links, which gave it
   card padding, a card shadow and far more height than a toolbar needs. */

.card#DashboardTab {
    width: 100% !important;
    margin: 0 0 16px !important;
    border-radius: var(--oui-radius-lg);
    box-shadow: none;
    border: 1px solid var(--oui-stroke-2);
    margin-bottom: 0 !important;
}

    .card#DashboardTab > .card-body {
        display: flex !important;
        align-items: center;
        flex-wrap: wrap;
        gap: 4px;
        padding: 8px 12px !important;
        font-size: 14px !important;
    }

        /* Two markup shapes exist. Most apps put the links straight in the
           card body; Calibration wraps them in a pair of floated divs. Floats
           do nothing inside a flex container, so the groups are re-expressed
           as flex items and the trailing group is pushed to the right edge. */
        .card#DashboardTab > .card-body > div {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            gap: 8px;
            float: none !important;
        }

            .card#DashboardTab > .card-body > div + div {
                margin-left: auto;
            }

            .card#DashboardTab > .card-body > div > .btn {
                margin: 0 !important;
            }

        /* The links in this bar are actions, so give them button geometry
           without the fill. Matched at any depth so both shapes are covered. */
        .card#DashboardTab .card-body a {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding: 6px 10px !important;
            margin: 0 !important;
            border-radius: var(--oui-radius-sm);
            font-size: 14px;
            font-weight: 400;
            color: var(--oui-fg-2);
            text-decoration: none;
            cursor: pointer;
            transition: background-color var(--oui-duration) var(--oui-ease);
        }

            .card#DashboardTab .card-body a:hover {
                background: var(--oui-bg-hover);
                color: var(--oui-fg);
            }

            .card#DashboardTab .card-body a p {
                display: inline !important;
                margin: 0;
            }

            /* These icons ship at 20px inline, which is what made the bar tall. */
            .card#DashboardTab .card-body a i {
                font-size: 14px !important;
                margin: 0 !important;
                line-height: 1 !important;
                color: var(--oui-fg-3);
            }

            /* The selected dashboard is the one filled item. */
            .card#DashboardTab .card-body a.btn-primary {
                font-weight: 600;
                color: var(--oui-brand-fg) !important;
            }

                .card#DashboardTab .card-body a.btn-primary i {
                    color: inherit;
                }

        /* Keep the bar to a single control-height row. */
        .card#DashboardTab .card-body .btn {
            min-height: 32px;
        }

/* ==========================================================================
   8. Pagination
   ========================================================================== */

.pagination {
    gap: 4px;
    flex-wrap: wrap;
}

/* Every pager is the same size. Bootstrap's small modifier is neutralised so a
   page that reaches for it cannot end up with controls smaller than the pagers
   beside it. */
.pagination-sm .page-link,
.pagination-lg .page-link {
    min-width: 32px;
    height: 32px;
    padding: 0 8px;
    font-size: 14px;
    line-height: 20px;
    border-radius: var(--oui-radius-sm) !important;
}

.page-item .page-link,
.page-link {
    min-width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 0 8px;
    font-family: var(--oui-font);
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
    color: var(--oui-fg) !important;
    background-color: transparent !important;
    border: 1px solid transparent !important;
    border-radius: var(--oui-radius-sm) !important;
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .page-link:hover {
        background-color: var(--oui-bg-hover) !important;
        border-color: transparent !important;
        color: var(--oui-fg) !important;
    }

    .page-link:focus {
        box-shadow: none;
    }

    .page-link:focus-visible {
        outline: 2px solid var(--oui-fg);
        outline-offset: -2px;
    }

.page-item.active .page-link {
    background-color: var(--oui-brand) !important;
    border-color: var(--oui-brand) !important;
    color: var(--oui-brand-fg) !important;
    font-weight: 600;
}

.page-item.disabled .page-link {
    background-color: transparent !important;
    border-color: transparent !important;
    color: var(--oui-fg-disabled) !important;
    opacity: 1;
    cursor: not-allowed;
}

/* ==========================================================================
   9. Scrollbars
   ==========================================================================
   The template painted the thumb solid brand blue at 10px, which read as a
   piece of UI rather than a scrollbar. Neutral, thinner, and rounded on all
   corners instead of only the bottom two. */

* {
    scrollbar-width: thin;
    scrollbar-color: var(--oui-scroll-thumb) transparent;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background-color: transparent !important;
    border-radius: 8px;
}

::-webkit-scrollbar-thumb {
    background-color: var(--oui-scroll-thumb) !important;
    /* The template used a 2000px inset shadow to fake the fill; clear it. */
    box-shadow: none !important;
    border-radius: 8px !important;
    border: 2px solid transparent;
    background-clip: content-box;
}

    ::-webkit-scrollbar-thumb:hover {
        background-color: var(--oui-fg-3) !important;
    }

::-webkit-scrollbar-corner {
    background: transparent;
}

/* ag-Grid renders its own scroll viewports, which do not inherit the
   selectors above in every browser. */
.ag-theme-alpine .ag-body-viewport,
.ag-theme-alpine .ag-body-horizontal-scroll-viewport,
.ag-theme-alpine .ag-body-vertical-scroll-viewport,
.ag-theme-alpine .ag-center-cols-viewport,
.ag-theme-alpine .ag-virtual-list-viewport {
    scrollbar-width: thin;
    scrollbar-color: var(--oui-scroll-thumb) transparent;
}

    .ag-theme-alpine ::-webkit-scrollbar {
        width: 10px;
        height: 10px;
    }

    .ag-theme-alpine ::-webkit-scrollbar-track {
        background-color: transparent !important;
    }

    .ag-theme-alpine ::-webkit-scrollbar-thumb {
        background-color: var(--oui-scroll-thumb) !important;
        box-shadow: none !important;
        border-radius: 8px !important;
        border: 2px solid transparent;
        background-clip: content-box;
    }

        .ag-theme-alpine ::-webkit-scrollbar-thumb:hover {
            background-color: var(--oui-fg-3) !important;
        }

/* ==========================================================================
   10. Modal footers
   ========================================================================== */

/* Teams puts the commit action last (right-most) with the dismiss beside it.
   Bootstrap already right-aligns; this just normalises spacing and removes
   the template's stray button margins. */
.modal-content {
    border: none;
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
}

.modal-header {
    align-items: center;
    padding-top: 0;
    border-bottom: 1px solid var(--oui-stroke-2);
}

    .modal-header .modal-title {
        font-family: var(--oui-font);
        font-size: 20px;
        font-weight: 600;
        line-height: 28px;
        color: var(--oui-fg);
    }

.modal-body {
    padding: 16px;
    color: var(--oui-fg-2);
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 16px;
    border-top: 1px solid var(--oui-stroke-2);
}

    .modal-footer > .btn {
        margin: 0 !important;
    }

/* ==========================================================================
   11. App shell — top bar and left rail
   ==========================================================================
   Most of the suite runs in `body.sidebar-mini`, so the sidebar is already a
   narrow icon rail, which is the shape Teams uses. What it was missing was
   Teams' visual grammar: a solid brand title bar, a quiet rail surface, and
   selection shown by a tinted item with a leading accent bar rather than a
   saturated fill that bleeds to the edge.

   GlobalSystem is the exception: its twelve pages run in `body.layout-3` and
   carry their navigation as a horizontal menu inside the title bar, with no
   rail at all. The title bar is therefore shared between the two layouts —
   that is the part users read as "the same product" — and only the navigation
   below it diverges, because the shapes genuinely differ.

   The bar's rules carry no body-class prefix. `.navbar-bg` and
   `.navbar.main-navbar` only ever appear in the app shell, and `.navbar-brand`
   / `#MainNavbar` / `.nav-collapse` are emitted by GlobalSystem's header and
   by nothing else — so the prefixes bought no safety, and dropping them means
   the styling survives scripts.js stripping the layout class below 1024px.

   The rail stays 65px wide on purpose. `.navbar { left: 65px }` and
   `.main-content { padding-left: 90px }` are keyed to that number in
   style.css, and widening it would mean chasing those offsets for two pixels
   of fidelity. */

/* --- Top bar (both layouts) --------------------------------------------- */
/* The bar is pinned to the viewport in every app. style.css positions both of
   its halves `absolute` inside .main-wrapper, so one screenful down they had
   scrolled off the top and the page lost its identity, its user menu and its
   only route to another page — on a long form or a long grid that is most of
   the time spent on the page.

   `fixed` costs nothing in layout: both halves were already out of flow, and
   .main-content's 64px top padding (below) already reserves the space, so
   nothing moves. It also means the bar no longer drifts sideways when a wide
   grid scrolls the page horizontally.

   889/890 keeps the painted band directly under the bar's own controls and
   both of them just below the rail (891) — the rail starts at y=48 so they
   never actually meet — and well below modals (1050) and the filter drawer
   (9998). The template left .navbar-bg on z-index 1, which cleared the page's
   positioned cards only by accident of source order; once the band is fixed
   and permanently over the content, that has to be deliberate. */
.navbar-bg {
    position: fixed !important;
    top: 0;
    z-index: 889 !important;
    height: 48px !important;
    background-color: var(--oui-brand) !important;
    box-shadow: var(--oui-shadow-4) !important;
    /* The template rounded the bottom corners and let the page background show
       through; Teams' title bar is a flat, full-bleed band. */
    border-radius: 0 !important;
    backdrop-filter: none !important;
}

/* The title bar spans the full width, over the rail column, the way Teams'
   does. style.css offsets it by the rail width, which left an empty brand-
   coloured square in the corner. */
.navbar.main-navbar {
    position: fixed !important;
    top: 0;
    z-index: 890;
    left: 0 !important;
    right: 0 !important;
    height: 48px !important;
    min-height: 48px;
    padding: 0 12px;
    background-color: transparent;
    box-shadow: none;
}

    .navbar.main-navbar .navbar-nav {
        align-items: center;
        gap: 2px;
    }

    /* Every top-bar control is a 32px square target with a subtle wash on
       hover, the way the Teams title bar behaves. */
    .navbar.main-navbar .nav-link {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 8px;
        min-height: 32px;
        min-width: 32px;
        padding: 0 8px !important;
        border-radius: var(--oui-radius-sm);
        color: rgba(255,255,255,.92) !important;
        font-size: 14px;
        line-height: 20px;
        transition: background-color var(--oui-duration) var(--oui-ease);
    }

        .navbar.main-navbar .nav-link:hover,
        .navbar.main-navbar .nav-link:focus {
            background-color: rgba(255,255,255,.16);
            color: #fff !important;
        }

        .navbar.main-navbar .nav-link:active {
            background-color: rgba(255,255,255,.24);
        }

        .navbar.main-navbar .nav-link:focus-visible {
            outline: 2px solid #fff;
            outline-offset: -2px;
        }

        /* The template sets these to 24px inline, which is why the bar needed
           to be 67px tall to contain them; 22px still clears a 48px bar. */
        .navbar.main-navbar .nav-link i {
            font-size: 22px !important;
            line-height: 1;
            margin: 0 !important;
            margin-right: 6px !important;
        }

        .navbar.main-navbar .nav-link.nav-link-user img,
        .navbar.main-navbar .nav-link img {
            width: 24px;
            height: 24px;
            border-radius: 50%;
            margin: 0 !important;
        }

    .navbar.main-navbar .dropdown-menu {
        border: 1px solid var(--oui-stroke-2);
        border-radius: var(--oui-radius-lg);
        box-shadow: var(--oui-shadow-16);
        padding: 4px;
        margin-top: 6px;
    }

        .navbar.main-navbar .dropdown-menu .dropdown-item {
            border-radius: var(--oui-radius-sm);
            padding: 7px 10px;
            font-size: 14px;
        }

            .navbar.main-navbar .dropdown-menu .dropdown-item:hover {
                background-color: var(--oui-bg-hover);
            }

/* Now that the bar is pinned, anything the browser scrolls to the top of the
   viewport — an in-page anchor, scrollIntoView, WebForms' SetFocus after a
   failed validation — would land underneath it. 48px of bar plus a little
   breathing room. */
html {
    scroll-padding-top: 56px;
}

/* The bar is 19px shorter than the template assumed. The layout-3 pages set
   their own 100px offset inline, so that one has to be forced. */
body.sidebar-mini .main-content {
    padding-top: 64px;
}

body.layout-3 .main-content {
    padding-top: 64px !important;
}

/* --- Top menu (layout-3 only) ------------------------------------------- */
/* GlobalSystem's navigation lives in the title bar. The template sized these
   links for a 70px bar and let them wrap onto a second line that spilled out
   of it; here they become the same 32px targets as every other control in the
   bar, on one line. */
.navbar.main-navbar {
    flex-wrap: nowrap;
}

/* 20px uppercase on 3px tracking spent 294px of the bar on the wordmark. */
.navbar .navbar-brand {
    flex: 0 0 auto;
    padding: 0 !important;
    margin-right: 16px;
    font-size: 14px !important;
    font-weight: 600;
    letter-spacing: .2px !important;
    text-transform: none !important;
    color: #fff !important;
    white-space: nowrap;
}

.nav-collapse {
    min-width: 0;
    align-items: center;
}

#MainNavbar {
    flex-wrap: nowrap;
    align-items: center;
    gap: 2px;
    /* A long menu on a narrow window scrolls sideways rather than wrapping
       out of the bar. The scrollbar is hidden because it would sit across
       the brand fill.

       This makes the bar a clipping box, so the dropdowns below can only
       escape it as `position: fixed` — optegrity-ui.js pins them. Removing
       that script, or this overflow, means fixing the other end too. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -ms-overflow-style: none;
}

    #MainNavbar::-webkit-scrollbar {
        width: 0;
        height: 0;
    }

    #MainNavbar > .nav-item {
        flex: 0 0 auto;
    }

    #MainNavbar > .nav-item > .nav-link {
        height: 32px;
        padding: 0 10px !important;
        white-space: nowrap;
    }

    /* The header emits a trailing empty link as a spacer. */
    #MainNavbar .nav-link:empty {
        display: none;
    }

    /* fa-sort-down draws low in its em box, so centring the flex line still
       leaves the caret sitting under the label. This is the one icon in the
       bar that trails its text, so it also opts out of the 6px lead-in gap —
       that gap belongs in front of a label, not behind one, and here it would
       just make the button's right padding wider than its left. */
    #MainNavbar > .nav-item > .nav-link.has-dropdown i {
        position: relative;
        top: -3px;
        font-size: 12px !important;
        margin-right: 0 !important;
    }

    /* Current page, matched by optegrity-ui.js — the rail marks selection in
       every other app and this bar had no way to show it at all. */
    #MainNavbar > .nav-item > .nav-link.active {
        background-color: rgba(255,255,255,.24);
        color: #fff !important;
        font-weight: 600;
    }

/* These flyouts hang off the brand-coloured bar, so they must undo the white
   text the bar's own links inherit. */
#MainNavbar .dropdown-menu {
    background-color: var(--oui-bg) !important;
}

    #MainNavbar .dropdown-menu .nav-item .nav-link {
        display: flex;
        justify-content: flex-start;
        height: 32px;
        min-height: 32px;
        margin: 0 !important;
        padding: 0 10px !important;
        border-radius: var(--oui-radius-sm);
        font-size: 14px;
        color: var(--oui-fg-2) !important;
        white-space: nowrap;
    }

        #MainNavbar .dropdown-menu .nav-item .nav-link:hover,
        #MainNavbar .dropdown-menu .nav-item .nav-link:focus {
            background-color: var(--oui-bg-hover) !important;
            color: var(--oui-fg) !important;
        }

/* Below 1024px the template drops the whole menu into a 200px panel under a
   hamburger. It is the same list of links, so it gets the same flyout card
   the rail's menus use rather than the template's square white slab. */
@media (max-width: 1024px) {
    .nav-collapse #MainNavbar {
        top: 42px;
        /* The template pinned this at 200px, which cut "Announcements
           Management" off mid-word. */
        width: max-content;
        min-width: 200px;
        max-width: 90vw;
        padding: 4px;
        background-color: var(--oui-bg);
        border: 1px solid var(--oui-stroke-2);
        border-radius: var(--oui-radius-lg);
        box-shadow: var(--oui-shadow-16) !important;
        overflow-x: hidden;
    }

        .nav-collapse #MainNavbar > .nav-item {
            width: 100%;
        }

        /* Out of the brand-coloured bar, these links need the flyout's ink
           rather than the bar's white. */
        .nav-collapse #MainNavbar .nav-item .nav-link,
        .nav-collapse #MainNavbar > .nav-item > .nav-link {
            display: flex;
            justify-content: flex-start;
            width: 100%;
            height: 32px;
            border-radius: var(--oui-radius-sm) !important;
            background-color: transparent !important;
            color: var(--oui-fg-2) !important;
        }

            .nav-collapse #MainNavbar .nav-item .nav-link:hover,
            .nav-collapse #MainNavbar .nav-item:focus > .nav-link,
            .nav-collapse #MainNavbar > .nav-item > .nav-link.active {
                background-color: var(--oui-bg-hover) !important;
                color: var(--oui-fg) !important;
            }

        /* The caret nudge is for a horizontal bar; in the panel it just
           floats the glyph off the line. */
        .nav-collapse #MainNavbar > .nav-item > .nav-link.has-dropdown i {
            top: 0;
            margin-left: auto !important;
        }
}

/* --- Left rail ---------------------------------------------------------- */
/* In mini mode the visible rail surface is painted by :after, not by the
   element itself. */
/* The title bar now spans the full width and is opaque, so the rail starts
   below it rather than running underneath and losing its brand block. */
body.sidebar-mini .main-sidebar:after {
    top: 48px;
    height: calc(100% - 48px);
    background-color: var(--oui-bg) !important;
    border-right: 1px solid var(--oui-stroke-2);
    border-radius: 0 !important;
    box-shadow: none !important;
    backdrop-filter: none !important;
}

/* The rail has to scroll: a long menu on a short screen otherwise runs off
   the bottom of the screen with no way to reach the last few items. The
   template blocked that with `overflow: initial !important` so the hover
   flyouts could spill past the 65px rail — but that traded a permanent bug
   for a cosmetic one.

   Scrolling wins; the flyouts are handled instead by optegrity-ui.js, which
   re-anchors them as `position: fixed` so they escape the scroller. Starting
   the box below the title bar (rather than padding the wrapper) means items
   scrolled out of view are clipped by the rail itself and can never slide
   underneath the bar. */
body.sidebar-mini .main-sidebar {
    position: fixed !important;
    top: 48px;
    height: calc(100% - 48px);
    /* style.css left this on `auto`, which put the rail in the same painting
       pass as the page's positioned cards — and since .main-content comes
       later in the document, the cards won. Flyouts opened underneath them.
       891 sits just above the navbar and well below modals (1050) and the
       advanced-search drawer (9998). */
    z-index: 891;
    overflow-x: hidden !important;
    overflow-y: auto !important;
    /* Reaching the end of the rail should not start scrolling the page. */
    overscroll-behavior: contain;
    /* The rail is only 65px wide, so a scrollbar would squeeze the labels
       into wrapping. Teams hides it here too; the wheel, touch and keyboard
       focus all still scroll. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

    body.sidebar-mini .main-sidebar::-webkit-scrollbar {
        width: 0;
        height: 0;
    }

/* scripts.js lays a niceScroll rail over the sidebar. It floats instead of
   taking layout width, so it gives back the affordance the hidden native
   scrollbar costs us — it just has to stop looking like the template's dark
   slab pressed against the rail's edge. (The same rail markup is used by the
   dashboard's #top-5-scroll widget, which wants the identical treatment.) */
body > .nicescroll-rails > .nicescroll-cursors {
    width: 4px !important;
    margin-right: 3px;
    background-color: var(--oui-scroll-thumb) !important;
    border: 0 !important;
    border-radius: 999px !important;
}

body.sidebar-mini #sidebar-wrapper {
    padding-top: 0;
    /* Nothing below the last item should collapse against the screen edge. */
    padding-bottom: 12px;
}

/* Below 1024px the rail expands to the 250px sidebar, which runs off the
   bottom of a short screen just the same. Its submenus expand in place rather
   than flying out, so there is nothing to escape and plain overflow does it. */
body:not(.sidebar-mini) .main-sidebar {
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
}

body.sidebar-mini .main-sidebar .sidebar-brand,
body.sidebar-mini .main-sidebar .sidebar-brand-sm {
    height: 48px;
    line-height: 48px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--oui-stroke-2);
}

    body.sidebar-mini .main-sidebar .sidebar-brand a {
        font-size: 11px;
        font-weight: 700;
        letter-spacing: .6px;
        color: var(--oui-brand) !important;
    }

/* Items sit inset so each one reads as its own rounded target. */
body.sidebar-mini .main-sidebar .sidebar-menu > li {
    padding: 0 6px;
    margin-bottom: 2px;
}

body.sidebar-mini .main-sidebar .sidebar-menu > li > a {
    position: relative;
    display: flex !important;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    height: 52px;
    padding: 0 2px !important;
    border-radius: var(--oui-radius-sm) !important;
    color: var(--oui-fg-2) !important;
    transition: background-color var(--oui-duration) var(--oui-ease), color var(--oui-duration) var(--oui-ease);
}

    body.sidebar-mini .main-sidebar .sidebar-menu > li > a .fas,
    body.sidebar-mini .main-sidebar .sidebar-menu > li > a .far,
    body.sidebar-mini .main-sidebar .sidebar-menu > li > a .fal,
    body.sidebar-mini .main-sidebar .sidebar-menu > li > a .fab,
    body.sidebar-mini .main-sidebar .sidebar-menu > li > a .ion {
        font-size: 18px !important;
        width: auto;
        margin: 0 !important;
        line-height: 1;
    }

    body.sidebar-mini .SidebarTitle {
        font-size: 10px !important;
        font-weight: 400;
        line-height: 12px;
        letter-spacing: 0;
        margin: 0 !important;
        text-align: center;
        color: inherit;
    }

/* style.css paints every sidebar link #a0bcea on hover with !important. */
body.sidebar-mini .main-sidebar .sidebar-menu li a:hover,
body.sidebar-mini .main-sidebar .sidebar-menu > li > a:hover {
    background-color: var(--oui-bg-hover) !important;
    color: var(--oui-fg) !important;
    padding-top: 0 !important;
}

/* Selection: brand tint plus a leading accent bar, not a saturated fill. */
body.sidebar-mini .main-sidebar .sidebar-menu > li.active > a,
body.sidebar-mini .main-sidebar .sidebar-menu li.active a {
    background-color: var(--oui-brand-tint) !important;
    color: var(--oui-brand) !important;
    font-weight: 600;
    box-shadow: none !important;
}

    body.sidebar-mini .main-sidebar .sidebar-menu > li.active > a:before {
        content: "";
        position: absolute;
        left: -6px;
        top: 10px;
        bottom: 10px;
        width: 3px;
        border-radius: 0 2px 2px 0;
        background: var(--oui-brand);
    }

/* Section dividers */
body.sidebar-mini .main-sidebar .sidebar-menu hr,
body.sidebar-mini #sidebar-wrapper hr {
    margin: 6px 10px !important;
    border: 0;
    border-top: 1px solid var(--oui-stroke-2);
}

/* --- Rail flyouts -------------------------------------------------------- */
/* Fixed, not absolute: the rail is a scroll container now, and an absolutely
   positioned flyout inside it would be clipped at 65px. A fixed box escapes
   that. optegrity-ui.js sets `top` to line the flyout up with its item and to
   keep it on screen; without it the browser falls back to the item's static
   position, which is close enough that the menu is still usable. */
body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu {
    position: fixed;
    left: 69px;
    top: auto;
    z-index: 892;
    padding: 4px;
    background-color: var(--oui-bg) !important;
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg) !important;
    box-shadow: var(--oui-shadow-16) !important;
    backdrop-filter: none !important;
}

    /* The flyout is only shown while its item is hovered, and it sits 4px off
       the rail — so moving the pointer towards it crossed 4px of nothing, lost
       the hover and closed the menu before you could reach it. This bridges
       the gap with a transparent strip that still counts as being inside the
       item. */
    body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu:before {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        left: -8px;
        width: 8px;
    }

    body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu li a {
        height: 32px;
        padding: 0 10px !important;
        border-radius: var(--oui-radius-sm);
        font-size: 14px;
        color: var(--oui-fg-2) !important;
        display: flex;
        align-items: center;
    }

    /* The template filled these brand blue on hover; a neutral wash reads
       calmer and matches the flyouts everywhere else in the system. */
    body.sidebar-mini .main-sidebar .sidebar-menu li:hover > ul.dropdown-menu li:hover > a,
    body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu li > a:hover,
    body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu li > a:focus {
        background-color: var(--oui-bg-hover) !important;
        color: var(--oui-fg) !important;
        border-radius: var(--oui-radius-sm) !important;
    }

    body.sidebar-mini .main-sidebar .sidebar-menu > li ul.dropdown-menu li.active > a {
        background-color: var(--oui-brand-tint) !important;
        color: var(--oui-brand) !important;
        font-weight: 600;
    }

    body.sidebar-mini .main-sidebar .sidebar-menu .dropdown-title {
        padding: 6px 10px 4px !important;
        font-size: 11px;
        font-weight: 600;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: var(--oui-fg-3);
    }

/* ==========================================================================
   12. App tiles — GlobalSystem's application suite
   ==========================================================================
   The launcher grid on GlobalSystem's dashboard. It is built from the
   template's `.article` component rather than `.card`, because each tile is
   an image with a caption — but it is still a card, so it takes the system's
   card geometry: 8px radius, the hairline border, and the shadow ramp.

   The tile's whole look used to be inline styles in Dashboard.aspx.cs, which
   is why it drifted: a 10px radius applied to two corners at a time on top of
   the component's 3px, a hand-rolled `0 10px 10px rgba(0,0,0,.21) !important`
   drop shadow, and a 25px heading on a 24px line box. Those are gone; the
   markup now carries only the image URL. */

.app-tile {
    cursor: pointer;
}

/* The row is a flex container, so a stretched tile makes every card in a row
   the same height however long its description runs. */
.app-tile > .article {
    display: flex;
    flex-direction: column;
    height: calc(100% - 24px);
    margin-bottom: 24px;
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    /* One radius on the article, with the overflow clipping the image to it —
       rather than the corner-by-corner radii the two children used to set. */
    border-radius: var(--oui-radius-lg);
    overflow: hidden;
    box-shadow: var(--oui-shadow-2);
    transition: box-shadow var(--oui-duration) var(--oui-ease),
                border-color var(--oui-duration) var(--oui-ease),
                transform var(--oui-duration) var(--oui-ease);
}

/* Teams answers a hoverable card with elevation, not motion. */
.app-tile:hover > .article {
    border-color: var(--oui-brand-muted);
    box-shadow: var(--oui-shadow-8);
    transform: translateY(-2px);
}

.app-tile:active > .article {
    box-shadow: var(--oui-shadow-4);
    transform: translateY(0);
}

.app-tile > .article > .article-header {
    flex: 0 0 auto;
    height: 150px;
    border-radius: 0;
}

    /* components.css parks this on z-index -1, which only works while nothing
       in the tile creates a stacking context — and the hover transform does
       exactly that, which would drop the image behind the card's own white
       background mid-hover. */
    .app-tile > .article .article-image {
        z-index: 0;
        background-size: cover;
        background-position: center;
        background-repeat: no-repeat;
    }

    .app-tile > .article .article-title {
        padding: 12px 16px;
        text-align: center;
        /* The template's scrim ramps to .65 black over the full height, which
           greys the middle of the image. Keep it low and short. */
        background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,.55) 70%, rgba(0,0,0,.72) 100%);
    }

        .app-tile > .article .article-title h2 {
            margin: 0;
            font-size: 18px;
            font-weight: 600;
            line-height: 24px;
            letter-spacing: 0;
            color: #fff;
        }

.app-tile > .article > .article-details {
    /* Takes the slack, so short and long descriptions end at the same edge. */
    flex: 1 1 auto;
    padding: 14px 16px;
    font-size: 14px;
    line-height: 20px;
    color: var(--oui-fg-2);
}

    .app-tile > .article > .article-details p {
        margin: 0;
        color: inherit;
    }

/* Applications the account does not have. The old treatment dropped the whole
   column to 40% opacity, which took the description with it; draining the
   colour out of the image says "not yours" without making the words hard to
   read. */
.app-tile-locked {
    cursor: help;
}

    .app-tile-locked > .article {
        box-shadow: none;
        background-color: var(--oui-bg-subtle);
    }

    .app-tile-locked > .article .article-image {
        filter: grayscale(1);
        opacity: .55;
    }

    .app-tile-locked > .article .article-title h2 {
        color: rgba(255,255,255,.85);
    }

    .app-tile-locked > .article > .article-details {
        color: var(--oui-fg-3);
    }

    /* Nothing happens when you click it, so it should not rise to meet you. */
    .app-tile-locked:hover > .article {
        border-color: var(--oui-stroke-2);
        box-shadow: var(--oui-shadow-2);
        transform: none;
    }

@media (max-width: 1024px) {
    .app-tile > .article > .article-header {
        height: 150px;
    }
}

/* ==========================================================================
   13. Loading
   ==========================================================================
   Three loading states, one vocabulary.

     .preloader / #loading      a blocking wait — the boot screen, and every
                                DisplayLoading() call (form submits, saves)
     #loading > .spinner        the same, on GlobalSystem's eleven pages that
                                carry the older bounce-dot markup
     .oui-loading-inline        a NON-blocking wait, for a region of a page
                                that is filling itself in — the dashboard grid

   What was here before: a solid white sheet over the whole viewport
   (`#loading` in utils.css is `position: fixed; inset: 0; background: #fff;
   z-index: 5555`) carrying a 15vw ring of three differently-coloured spinners
   with a 10vw 3D logo inside it and 2.5vw bold Azure text above. It said
   "the page is broken" rather than "the page is coming".

   Teams' answer to a wait is: keep the app visible, dim it slightly, and put
   a small flyout in the middle of it. That is what this is — a translucent
   blurred scrim and a 8px-radius card with the shadow ramp, the brand mark
   riding in a Fluent spinner, and an indeterminate bar that keeps moving so
   a long wait still reads as progress rather than as a hang.

   The scrim starts BELOW the title bar and to the RIGHT of the rail. That is
   deliberate and it is the point of the redesign: a slow page must never trap
   anyone on it. Navigation stays lit and clickable throughout, so you can
   always leave. Everything the wait actually protects — the form, its buttons,
   the grid — is inside the scrimmed area, so a submit still cannot be fired
   twice.

   GlobalSystem's bounce markup had no CSS at all in any app: `.spinner` and
   `.bounce1/2/3` are styled nowhere, so those eleven pages showed a blank
   white screen for the whole of the wait. They get the dot-pulse the markup
   was always asking for. */

/* --- Motion ------------------------------------------------------------- */
@keyframes oui-spin {
    to {
        transform: rotate(360deg);
    }
}

/* The mark inside the ring turns the other way at the same rate, so it stays
   upright while the ring goes round it. */
@keyframes oui-spin-reverse {
    to {
        transform: rotate(-360deg);
    }
}

@keyframes oui-loading-in {
    from {
        opacity: 0;
    }
}

@keyframes oui-loading-rise {
    from {
        opacity: 0;
        transform: translateY(8px) scale(.98);
    }
}

/* Indeterminate: a brand-coloured pulse crossing a tinted track. */
@keyframes oui-loading-bar {
    from {
        background-position: 150% 0;
    }

    to {
        background-position: -50% 0;
    }
}

@keyframes oui-shimmer {
    from {
        background-position: 150% 0;
    }

    to {
        background-position: -50% 0;
    }
}

@keyframes oui-dot-pulse {
    0%, 80%, 100% {
        opacity: .25;
        transform: scale(.7);
    }

    40% {
        opacity: 1;
        transform: scale(1);
    }
}

/* --- The blocking scrim ------------------------------------------------- */
/* !important throughout: `#loading` in utils.css is an id, and the plugin and
   the dashboards both set geometry inline. Neither can be reached from here
   without it. */
.preloader,
#loading {
    position: fixed !important;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: auto !important;
    height: auto !important;
    min-height: 0 !important;
    padding: 24px;
    /* Dimmed, not painted over: seeing the page you are waiting on is what
       makes the wait feel short. */
    background: rgba(255,255,255,.72) !important;
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
    opacity: 1 !important;
    /* Top of the stack. The filter drawer is 9999 over a 9998 scrim, and the
       body-level select menus it opens are lifted to 10050 (advanced-search.css)
       — a wait that renders underneath the panel or the dropdown that started
       it is no wait at all. */
    z-index: 10060 !important;
    animation: oui-loading-in .12s var(--oui-ease) both;
}

/* Leave the app shell out of the scrim. The rail and the top bar keep their
   own z-index (891 / 890) and simply are not covered, so they stay readable
   and clickable for the whole wait. */
body.sidebar-mini .preloader,
body.sidebar-mini #loading {
    top: 48px;
    left: 65px;
}

body.layout-3 .preloader,
body.layout-3 #loading {
    top: 48px;
}

/* Below 1024px the rail is off-canvas, so there is no 65px column to spare. */
@media (max-width: 1024px) {
    body.sidebar-mini .preloader,
    body.sidebar-mini #loading {
        left: 0;
    }
}

/* --- The card ----------------------------------------------------------- */
/* The template absolutely positioned this at a vh offset and let its contents
   overflow it; here it is an ordinary flyout that the scrim centres. */
.preloader-container,
#loading > .spinner {
    position: static !important;
    top: auto !important;
    left: auto !important;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    width: auto !important;
    max-width: 320px;
    padding: 24px 32px;
    text-align: center;
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
    animation: oui-loading-rise .16s var(--oui-ease) both;
}

/* Source order is percent, text, animation — the reverse of how it reads. */
.preloader-animation {
    order: 1;
}

.preloader-text {
    order: 2;
}

.preloader-percent {
    order: 3;
}

.preloader-container:after {
    order: 4;
}

/* --- The spinner -------------------------------------------------------- */
.preloader-animation {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    border: 3px solid var(--oui-brand-tint);
    /* One arc on a tinted track — the Fluent spinner. The template drew three
       rings in three unrelated colours turning at 30s, 1.5s and 1s. */
    border-top-color: var(--oui-brand);
    border-radius: 50%;
    animation: oui-spin .9s linear infinite;
}

    /* The second and third rings. */
    .preloader-animation:before,
    .preloader-animation:after {
        display: none;
    }

    /* Sized inline at 10vw — 192px on a 1920 screen, which is why the old
       loader filled the page. */
    .preloader-animation img {
        width: 28px !important;
        height: auto !important;
        padding: 0 !important;
        margin: 0 !important;
        animation: oui-spin-reverse .9s linear infinite;
    }

/* --- Label and value ---------------------------------------------------- */
.preloader-text,
.preloader-percent {
    position: static !important;
    top: auto !important;
    left: auto !important;
    width: auto !important;
}

.preloader-text {
    font-size: 15px !important;
    font-weight: 600 !important;
    line-height: 20px;
    /* 2.5vw bold Azure — 48px on a 1920 screen, and 3.56:1 on white. */
    color: var(--oui-fg) !important;
}

.preloader-percent {
    font-size: 13px !important;
    font-weight: 400 !important;
    line-height: 18px;
    color: var(--oui-fg-2) !important;
}

/* --- Indeterminate bar -------------------------------------------------- */
/* A spinner alone stops reading as progress after a few seconds. This keeps
   crossing, which is what Teams does under a long operation. */
.preloader-container:after {
    content: "";
    width: 168px;
    height: 3px;
    border-radius: 999px;
    background-image: linear-gradient(90deg, var(--oui-brand-tint) 0%, var(--oui-brand-tint) 38%, var(--oui-brand) 50%, var(--oui-brand-tint) 62%, var(--oui-brand-tint) 100%);
    background-size: 300% 100%;
    animation: oui-loading-bar 1.5s var(--oui-ease) infinite;
}

/* --- GlobalSystem's bounce-dot markup ----------------------------------- */
/* `<div id="loading"><div class="spinner"><div class="bounce1">…` — styled
   nowhere in any app, so these pages showed the white sheet and nothing else.
   The card rules above give .spinner the flyout; these give it its dots. */
#loading > .spinner {
    flex-direction: row;
    gap: 8px;
    padding: 20px 24px;
}

    #loading > .spinner > div {
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background-color: var(--oui-brand);
        animation: oui-dot-pulse 1.2s var(--oui-ease) infinite both;
    }

    #loading > .spinner > .bounce2 {
        animation-delay: .16s;
    }

    #loading > .spinner > .bounce3 {
        animation-delay: .32s;
    }

/* --- The non-blocking inline wait --------------------------------------- */
/* For a region that is filling itself in while the rest of the page is
   already usable — the dashboard grid, which can take a while and used to be
   covered by the blocking overlay above.

   It is an ordinary in-flow block with `pointer-events: none`, so there is
   nothing here that can intercept a click: the rail, the top bar, the page's
   own tabs and its config form all stay live, and you can leave for another
   page without waiting for the grid to arrive. */
.oui-loading-inline {
    position: static;
    width: 100%;
    padding: 8px 0 24px;
    pointer-events: none;
    animation: oui-loading-in .16s var(--oui-ease) both;
}

.oui-loading-inline-head {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
}

.oui-spinner {
    flex: 0 0 auto;
    display: block;
    width: 20px;
    height: 20px;
    border: 2px solid var(--oui-brand-tint);
    border-top-color: var(--oui-brand);
    border-radius: 50%;
    animation: oui-spin .9s linear infinite;
}

.oui-loading-inline-title {
    font-size: 15px;
    font-weight: 600;
    line-height: 20px;
    color: var(--oui-fg);
}

/* Says out loud that the wait is not a lock — the complaint this whole
   section exists to answer. */
.oui-loading-inline-hint {
    margin: 6px 0 20px;
    text-align: center;
    font-size: 13px;
    line-height: 18px;
    color: var(--oui-fg-3);
}

/* --- Skeleton ----------------------------------------------------------- */
/* Placeholders in the shape of what is coming beat a spinner over an empty
   canvas: the page stops jumping when the real tiles land, and the wait reads
   as "filling in" rather than "nothing here". */
.oui-skeleton-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 16px;
}

.oui-skeleton-tile {
    position: relative;
    height: 190px;
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-2);
    overflow: hidden;
}

    /* A title line and a body block, both sweeping. */
    .oui-skeleton-tile:before,
    .oui-skeleton-tile:after {
        content: "";
        position: absolute;
        left: 16px;
        border-radius: var(--oui-radius-sm);
        background-image: linear-gradient(90deg, var(--oui-bg-hover) 0%, var(--oui-bg-hover) 30%, var(--oui-bg-subtle) 50%, var(--oui-bg-hover) 70%, var(--oui-bg-hover) 100%);
        background-size: 300% 100%;
        animation: oui-shimmer 1.6s linear infinite;
    }

    .oui-skeleton-tile:before {
        top: 16px;
        width: 45%;
        height: 12px;
    }

    .oui-skeleton-tile:after {
        top: 44px;
        right: 16px;
        bottom: 16px;
    }

    /* Offsetting the sweeps stops six tiles pulsing as one slab. */
    .oui-skeleton-tile:nth-child(2):before,
    .oui-skeleton-tile:nth-child(2):after {
        animation-delay: .12s;
    }

    .oui-skeleton-tile:nth-child(3):before,
    .oui-skeleton-tile:nth-child(3):after {
        animation-delay: .24s;
    }

    .oui-skeleton-tile:nth-child(4):before,
    .oui-skeleton-tile:nth-child(4):after {
        animation-delay: .36s;
    }

    .oui-skeleton-tile:nth-child(5):before,
    .oui-skeleton-tile:nth-child(5):after {
        animation-delay: .48s;
    }

    .oui-skeleton-tile:nth-child(6):before,
    .oui-skeleton-tile:nth-child(6):after {
        animation-delay: .6s;
    }

/* --- Reduced motion ----------------------------------------------------- */
/* The spinner earns its motion — it is the only thing saying the app is still
   working — so it slows rather than stops. Everything decorative stops. */
@media (prefers-reduced-motion: reduce) {
    .preloader-animation,
    .preloader-animation img,
    .oui-spinner {
        animation-duration: 2.4s;
    }

    .preloader,
    #loading,
    .preloader-container,
    #loading > .spinner,
    .oui-loading-inline,
    .preloader-container:after,
    .oui-skeleton-tile:before,
    .oui-skeleton-tile:after {
        animation: none;
    }

    #loading > .spinner > div {
        opacity: 1;
        transform: none;
        animation: none;
    }
}

/* A wait is not part of the document. */
@media print {
    .preloader,
    #loading,
    .oui-loading-inline {
        display: none !important;
    }
}


/* ==========================================================================
   14. Toasts — iziToast

   iziToast ships a 2016 look: a grey translucent slab, Lato, a black PNG
   glyph and a full-width coloured bar down the left. This section rebuilds it
   as a Fluent 2 / Teams notification — a white card on the page's elevation
   ramp, a hairline border, a leading accent bar in the status colour and a
   tinted icon chip rather than a saturated fill.

   Loaded after iziToast.css, so these rules win. The library adds
   .iziToast-color-{blue,green,orange,red,yellow} and .ico-{info,success,
   warning,error,question} per type; both are keyed off here, so the theme
   follows whichever helper the calling code uses and there is nothing to pass
   at the call site.

   ---------------------------------------------------------------------------
   TOAST COLOUR STANDARD — like the button table, the colour says what
   happened, never what looks nice. The question is only ever "what became of
   the thing the user just asked for".

     success   Green.  The action completed and data changed. Saved, Added,
               Updated, Deleted, Approved, Rejected, Imported, Copied.
               A recorded rejection is a completed action: the user asked to
               reject and the system did it, so it is green, not red.

     info      Brand blue. Neutral notice, nothing committed. Work in
               progress ("Preparing your Excel file..."), or a statement of
               fact the user did not ask to be told.

     warning   Amber. It went through, but with something worth noticing —
               partial import, rows skipped, a limit reached.

     error     Red. The action failed or was refused. Validation, access
               denied, server error. This is the ONLY red toast.

     question  Blue with a distinct glyph. A prompt that needs an answer
               before anything happens. Pair with buttons, not a timeout.

   The trap this replaces: info was used as the success colour in ~230 call
   sites, which left blue meaning both "saved" and "just so you know".
   ---------------------------------------------------------------------------
   ========================================================================== */

/* The stack. iziToast positions the wrapper itself; only the gutter and the
   gap between stacked toasts are ours. */
.iziToast-wrapper {
    padding: 12px;
}

.iziToast-capsule {
    margin-bottom: 8px;
}

/* --- the card ---------------------------------------------------------- */
.iziToast {
    /* The library sets a translucent grey and its own web font. */
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-8);
    font-family: var(--oui-font);
    font-size: 13px;
    /* The wrapper is the full width of the window and the library lets the
       toast fill it, so an eight-word message used to stretch across the
       whole screen. A notification is a card, not a banner. */
    max-width: 400px;
    /* Room on the left for the accent bar plus the icon chip. */
    padding: 12px 44px 12px 0;
    min-height: 0;
    overflow: hidden;
    transition: box-shadow var(--oui-duration) var(--oui-ease),
                transform var(--oui-duration) var(--oui-ease);
}

/* A toast is dismissible and pauses on hover, so it should answer the
   pointer. Lift rather than tint: the surface carries status colour already. */
.iziToast:hover {
    box-shadow: var(--oui-shadow-16);
    transform: translateY(-1px);
}

/* The leading accent bar. Fluent marks status with a bar at the edge instead
   of colouring the whole surface — same move as the selected rail item. */
.iziToast:before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: var(--oui-fg-3);
    border-radius: var(--oui-radius-lg) 0 0 var(--oui-radius-lg);
}

/* iziToast draws its own left bar via ::after on .iziToast-color-*; ours
   replaces it. */
.iziToast:after {
    display: none !important;
}

/* --- body -------------------------------------------------------------- */
.iziToast > .iziToast-body {
    padding: 0 0 0 40px;
    margin: 0 0 0 12px;
    min-height: 32px;
}

.iziToast > .iziToast-body .iziToast-texts {
    margin: 0;
}

.iziToast > .iziToast-body .iziToast-title {
    display: block;
    float: none;
    color: var(--oui-fg);
    font-size: 13px;
    font-weight: 600;
    line-height: 18px;
    margin: 0;
}

/* Not every caller has a title. Left as-is it reserves a blank first line. */
.iziToast > .iziToast-body .iziToast-title:empty {
    display: none;
}

.iziToast > .iziToast-body .iziToast-message {
    display: block;
    float: none;
    color: var(--oui-fg-2);
    font-size: 13px;
    line-height: 18px;
    margin: 0;
}

/* --- icon chip ---------------------------------------------------------
   The library's glyphs are black PNGs. Font Awesome is already on every page,
   so the glyph is drawn as type: crisp at any zoom, and it takes its colour
   from a token instead of being baked into an image. */
.iziToast > .iziToast-body .iziToast-icon {
    top: 50%;
    left: 0;
    width: 28px;
    height: 28px;
    margin-top: -14px;
    border-radius: 50%;
    background-image: none !important;
    background-color: var(--oui-bg-hover);
    color: var(--oui-fg-2);
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    line-height: 28px;
    text-align: center;
    display: block;
}

.iziToast > .iziToast-body .iziToast-icon.ico-success:before { content: "\f00c"; }
.iziToast > .iziToast-body .iziToast-icon.ico-info:before    { content: "\f129"; }
.iziToast > .iziToast-body .iziToast-icon.ico-warning:before { content: "\f071"; }
.iziToast > .iziToast-body .iziToast-icon.ico-error:before   { content: "\f00d"; }
.iziToast > .iziToast-body .iziToast-icon.ico-question:before { content: "\f128"; }

/* --- close ------------------------------------------------------------- */
.iziToast > .iziToast-close {
    width: 28px;
    height: 28px;
    top: 50%;
    right: 8px;
    margin-top: -14px;
    border-radius: var(--oui-radius-sm);
    opacity: 1;
    background-image: none;
    color: var(--oui-fg-3);
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 12px;
    line-height: 28px;
    text-align: center;
    transition: background-color var(--oui-duration) var(--oui-ease),
                color var(--oui-duration) var(--oui-ease);
}

.iziToast > .iziToast-close:before {
    content: "\f00d";
}

.iziToast > .iziToast-close:hover {
    background-color: var(--oui-bg-hover);
    color: var(--oui-fg);
}

.iziToast > .iziToast-close:active {
    background-color: var(--oui-bg-pressed);
}

/* --- progress bar ------------------------------------------------------
   The remaining time, not decoration: a hairline at the foot of the card in
   the status colour. */
.iziToast > .iziToast-progressbar {
    background: transparent;
}

.iziToast > .iziToast-progressbar > div {
    height: 2px;
    background: var(--oui-fg-3);
    border-radius: 0;
}

/* --- buttons -----------------------------------------------------------
   A question toast carries actions. Match .btn rather than inventing a
   third button look. */
.iziToast > .iziToast-body .iziToast-buttons {
    margin-top: 8px;
}

.iziToast > .iziToast-body .iziToast-buttons > a,
.iziToast > .iziToast-body .iziToast-buttons > button,
.iziToast > .iziToast-body .iziToast-buttons > input:not([type=checkbox]):not([type=radio]) {
    min-height: 28px;
    padding: 4px 12px;
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
    background: var(--oui-bg);
    color: var(--oui-fg);
    font-size: 13px;
    font-weight: 600;
    transition: background-color var(--oui-duration) var(--oui-ease);
}

.iziToast > .iziToast-body .iziToast-buttons > a:hover,
.iziToast > .iziToast-body .iziToast-buttons > button:hover,
.iziToast > .iziToast-body .iziToast-buttons > input:not([type=checkbox]):not([type=radio]):hover {
    background: var(--oui-bg-hover);
}

/* --- per-type colour ---------------------------------------------------
   One block per status: the accent bar, the icon chip and the progress bar
   all take the same colour, and nothing else does. */
.iziToast.iziToast-color-green:before,
.iziToast.iziToast-color-green > .iziToast-progressbar > div {
    background: var(--oui-success);
}

.iziToast.iziToast-color-green > .iziToast-body .iziToast-icon {
    background-color: var(--oui-success-tint);
    color: var(--oui-success);
}

.iziToast.iziToast-color-blue:before,
.iziToast.iziToast-color-blue > .iziToast-progressbar > div,
.iziToast.iziToast-color-yellow:before,
.iziToast.iziToast-color-yellow > .iziToast-progressbar > div {
    background: var(--oui-brand);
}

.iziToast.iziToast-color-blue > .iziToast-body .iziToast-icon,
.iziToast.iziToast-color-yellow > .iziToast-body .iziToast-icon {
    background-color: var(--oui-brand-tint);
    color: var(--oui-brand);
}

.iziToast.iziToast-color-orange:before,
.iziToast.iziToast-color-orange > .iziToast-progressbar > div {
    background: var(--oui-warning);
}

.iziToast.iziToast-color-orange > .iziToast-body .iziToast-icon {
    background-color: var(--oui-warning-tint);
    color: var(--oui-warning);
}

.iziToast.iziToast-color-red:before,
.iziToast.iziToast-color-red > .iziToast-progressbar > div {
    background: var(--oui-danger);
}

.iziToast.iziToast-color-red > .iziToast-body .iziToast-icon {
    background-color: var(--oui-danger-tint);
    color: var(--oui-danger);
}

/* The library paints a pastel background AND a matching border on these, which
   ringed the card in its status colour on top of the accent bar. The card
   stays white with a hairline; status is the bar and the chip, said once. */
.iziToast.iziToast-color-green,
.iziToast.iziToast-color-blue,
.iziToast.iziToast-color-orange,
.iziToast.iziToast-color-red,
.iziToast.iziToast-color-yellow {
    background: var(--oui-bg);
    border-color: var(--oui-stroke);
}

/* On a phone the card is the screen, so let it fill the gutter. */
@media (max-width: 480px) {
    .iziToast {
        max-width: 100%;
    }
}

/* --- reduced motion ---------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .iziToast {
        transition: none;
    }

    .iziToast:hover {
        transform: none;
    }
}

/* ==========================================================================
   15. Template Management — GlobalSystem's template catalogue and importer

   One page uses this block: GlobalSystem's Template Management. It is here
   rather than in a page-level <style> for the same reason the app tiles in
   section 12 are — the colour coding it carries is a rule, not a decoration,
   and a rule belongs where the rest of the rules live.

   The rule: a column the user MUST fill in is brand blue, a column they may
   leave empty is grey, and a column whose value has to already exist in the
   database wears the azure accent. Those three colours mean the same thing
   here, in the generated workbook, and in the error report that comes back.
   Change one and change all three.
   ========================================================================== */

/* --- the picker -------------------------------------------------------- */

.tm-search {
    margin-bottom: 12px;
}

.tm-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 12px;
}

/* A segmented filter, not a button row: these switch a view and commit
   nothing, so they are quiet until the one in force lights up. */
.tm-tab {
    border: 1px solid var(--oui-stroke);
    background: var(--oui-bg);
    color: var(--oui-fg-2);
    border-radius: var(--oui-radius-sm);
    font-size: 12px;
    line-height: 1;
    padding: 6px 10px;
    cursor: pointer;
    transition: background var(--oui-duration) var(--oui-ease);
}

.tm-tab:hover {
    background: var(--oui-bg-hover);
}

.tm-tab.active {
    background: var(--oui-brand);
    border-color: var(--oui-brand);
    color: var(--oui-brand-fg);
}

.tm-list {
    max-height: 620px;
    overflow-y: auto;
    margin: 0 -4px;
    padding: 0 4px;
}

.tm-group + .tm-group {
    margin-top: 14px;
}

.tm-group-name {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--oui-fg-3);
    margin-bottom: 6px;
}

.tm-group-count {
    background: var(--oui-stroke-2);
    color: var(--oui-fg-2);
    border-radius: 10px;
    padding: 1px 7px;
    font-size: 11px;
}

/* Teams-style selection: a tint and a leading accent bar rather than a fill. */
.tm-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    text-align: left;
    background: var(--oui-bg);
    border: 1px solid transparent;
    border-left: 3px solid transparent;
    border-radius: var(--oui-radius-sm);
    padding: 8px 10px;
    cursor: pointer;
    transition: background var(--oui-duration) var(--oui-ease);
}

.tm-item:hover {
    background: var(--oui-bg-hover);
}

.tm-item.active {
    background: var(--oui-brand-tint);
    border-color: var(--oui-brand-muted);
    border-left-color: var(--oui-brand);
}

.tm-item-name {
    font-size: 14px;
    color: var(--oui-fg);
}

.tm-item.active .tm-item-name {
    font-weight: 600;
    color: var(--oui-brand);
}

.tm-item-meta {
    font-size: 11px;
    color: var(--oui-fg-3);
    white-space: nowrap;
}

.tm-empty {
    padding: 16px 4px;
    font-size: 13px;
    color: var(--oui-fg-3);
}

/* --- nothing picked yet ------------------------------------------------ */

.tm-blank {
    text-align: center;
    padding: 48px 24px;
    color: var(--oui-fg-2);
}

.tm-blank i {
    font-size: 34px;
    color: var(--oui-brand-muted);
}

.tm-blank-title {
    margin-top: 14px;
    font-size: 17px;
    font-weight: 600;
    color: var(--oui-fg);
}

.tm-blank-text {
    margin: 8px auto 0;
    max-width: 560px;
    font-size: 13px;
    line-height: 1.55;
}

/* --- the template, explained ------------------------------------------- */

.tm-detail-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.tm-detail-eyebrow {
    font-size: 11px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--oui-fg-3);
}

.tm-detail-title {
    font-size: 22px;
    font-weight: 600;
    color: var(--oui-fg);
    margin: 2px 0 0;
}

.tm-detail-lede {
    margin: 10px 0 0;
    font-size: 13px;
    line-height: 1.6;
    color: var(--oui-fg-2);
    max-width: 76ch;
}

.tm-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 16px 0 4px;
}

.tm-fact {
    display: flex;
    align-items: baseline;
    gap: 6px;
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
    padding: 6px 10px;
}

.tm-fact-value {
    font-size: 15px;
    font-weight: 600;
    color: var(--oui-brand);
}

.tm-fact-label {
    font-size: 12px;
    color: var(--oui-fg-2);
}

.tm-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin: 14px 0 6px;
    font-size: 12px;
    color: var(--oui-fg-2);
}

.tm-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.tm-swatch {
    width: 12px;
    height: 12px;
    border-radius: 2px;
    border: 1px solid var(--oui-stroke);
    display: inline-block;
}

.tm-swatch-required {
    background: var(--oui-brand);
    border-color: var(--oui-brand);
}

.tm-swatch-optional {
    background: var(--oui-bg-canvas);
}

.tm-swatch-list {
    background: var(--oui-brand-tint);
    border-color: var(--oui-brand-accent);
}

/* A wide table must scroll inside its own box; the page never scrolls
   sideways. */
.tm-table-wrap {
    overflow-x: auto;
    margin-top: 8px;
}

/* Fixed layout on purpose. Left to size themselves these two tables are as
   wide as their longest sentence, which pushed the last column out of the card
   and made every row need a sideways scroll to be read. */
.tm-columns,
.tm-issue-table {
    font-size: 13px;
    margin-bottom: 0;
    table-layout: fixed;
    min-width: 700px;
}

.tm-columns th:nth-child(1) { width: 9%; }
.tm-columns th:nth-child(2) { width: 23%; }
.tm-columns th:nth-child(3) { width: 12%; }
.tm-columns th:nth-child(4) { width: 38%; }
.tm-columns th:nth-child(5) { width: 18%; }

.tm-issue-table { min-width: 760px; }

.tm-issue-table th:nth-child(1) { width: 10%; }
.tm-issue-table th:nth-child(2) { width: 14%; }
.tm-issue-table th:nth-child(3) { width: 14%; }
.tm-issue-table th:nth-child(4) { width: 31%; }
.tm-issue-table th:nth-child(5) { width: 31%; }

.tm-columns th,
.tm-issue-table th {
    font-size: 11px;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--oui-fg-3);
    border-top: none;
    border-bottom: 1px solid var(--oui-stroke);
}

.tm-columns td,
.tm-issue-table td {
    vertical-align: top;
    border-top: 1px solid var(--oui-stroke-2);
}

/* The template's table rules keep cells on one line and cut what will not fit
   with an ellipsis. These two tables are prose - a truncated "Cell A9 is empty,
   and Site Name is re..." is exactly the message the user needed - so they wrap
   instead. */
.tm-columns th,
.tm-columns td,
.tm-issue-table th,
.tm-issue-table td {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* Required rows carry the same leading bar the selected template does. */
.tm-columns tbody tr td:first-child {
    border-left: 3px solid transparent;
}

.tm-row-required td:first-child {
    border-left-color: var(--oui-brand);
}

.tm-row-required {
    background: var(--oui-brand-tint);
}

/* Two-class selectors, because the wrapping rule above is one and a cell
   reference broken over two lines ("B\n7") is not a cell reference. */
.tm-columns td.tm-cell-ref,
.tm-columns th.tm-cell-ref,
.tm-issue-table td.tm-cell-ref,
.tm-issue-table th.tm-cell-ref {
    font-family: monospace;
    color: var(--oui-fg-3);
    white-space: nowrap;
}

.tm-cell-kind {
    color: var(--oui-fg-2);
}

.tm-columns code,
.tm-cell-example code {
    word-break: break-word;
}

.tm-cell-example code {
    background: var(--oui-bg-canvas);
    color: var(--oui-fg);
    border-radius: var(--oui-radius-sm);
    padding: 2px 6px;
}

.tm-col-name {
    font-weight: 600;
    color: var(--oui-fg);
    margin-right: 6px;
}

.tm-col-note {
    display: block;
    color: var(--oui-fg);
}

.tm-col-rule {
    display: block;
    margin-top: 2px;
    color: var(--oui-fg-3);
    font-size: 12px;
}

.tm-flag {
    display: inline-block;
    font-size: 10px;
    letter-spacing: .03em;
    text-transform: uppercase;
    border-radius: 10px;
    padding: 1px 8px;
    margin-right: 4px;
    white-space: nowrap;
}

.tm-flag-required {
    background: var(--oui-brand);
    color: var(--oui-brand-fg);
}

.tm-flag-optional {
    background: var(--oui-bg-canvas);
    color: var(--oui-fg-2);
}

.tm-flag-quiet {
    border: 1px solid var(--oui-stroke);
    color: var(--oui-fg-3);
}

/* --- live values, destinations, notes ---------------------------------- */

.tm-section {
    margin-top: 22px;
    padding-top: 16px;
    border-top: 1px solid var(--oui-stroke-2);
}

.tm-section-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--oui-fg);
    margin-bottom: 10px;
}

.tm-values + .tm-values {
    margin-top: 10px;
}

.tm-values-name {
    font-size: 12px;
    color: var(--oui-fg-2);
    margin-bottom: 4px;
}

.tm-values-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.tm-chip {
    font-size: 12px;
    background: var(--oui-brand-tint);
    color: var(--oui-brand);
    border: 1px solid var(--oui-brand-muted);
    border-radius: var(--oui-radius-sm);
    padding: 2px 8px;
}

.tm-chip-more {
    background: var(--oui-bg);
    color: var(--oui-fg-3);
    border-color: var(--oui-stroke);
}

.tm-chip-empty {
    background: var(--oui-danger-tint);
    color: var(--oui-danger);
    border-color: var(--oui-danger);
}

.tm-lands {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.tm-land {
    display: flex;
    align-items: baseline;
    gap: 10px;
    font-size: 12px;
    color: var(--oui-fg-2);
}

/* A companion is a row the import may create without being asked to, so it is
   marked rather than listed alongside the ones the user chose. */
.tm-land-companion code {
    background: var(--oui-warning-tint);
    color: var(--oui-warning);
}

.tm-land code {
    background: var(--oui-bg-canvas);
    color: var(--oui-fg);
    border-radius: var(--oui-radius-sm);
    padding: 2px 6px;
    white-space: nowrap;
}

.tm-notes {
    margin-top: 18px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.tm-note {
    display: flex;
    gap: 8px;
    font-size: 12px;
    line-height: 1.55;
    color: var(--oui-fg-2);
    background: var(--oui-brand-tint);
    border-left: 3px solid var(--oui-brand-accent);
    border-radius: var(--oui-radius-sm);
    padding: 8px 10px;
}

.tm-note i {
    color: var(--oui-brand);
    margin-top: 2px;
}

/* --- the upload card --------------------------------------------------- */

.tm-drop {
    border: 1px dashed var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    background: var(--oui-bg-subtle);
    text-align: center;
    padding: 18px 12px;
    cursor: pointer;
    transition: background var(--oui-duration) var(--oui-ease);
}

.tm-drop:hover,
.tm-drop-over {
    background: var(--oui-brand-tint);
    border-color: var(--oui-brand);
}

.tm-drop i {
    font-size: 20px;
    color: var(--oui-brand);
}

.tm-drop-text {
    font-size: 12px;
    color: var(--oui-fg-2);
    margin-top: 6px;
}

.tm-drop-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--oui-fg);
    margin-top: 4px;
    word-break: break-all;
}

.tm-drop-filled {
    border-style: solid;
    border-color: var(--oui-brand-muted);
    background: var(--oui-brand-tint);
}

.tm-upload-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    margin-top: 4px;
}

.tm-hint {
    display: block;
    font-size: 12px;
    color: var(--oui-fg-3);
    margin-top: 4px;
}

.tm-upload-note {
    margin-top: 10px;
    line-height: 1.55;
    max-width: 90ch;
}

/* --- what came back ---------------------------------------------------- */

.tm-result {
    margin-top: 16px;
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    overflow: hidden;
}

.tm-result-head {
    display: flex;
    gap: 10px;
    padding: 12px 14px;
}

.tm-result-head i {
    font-size: 18px;
    margin-top: 2px;
}

.tm-result-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--oui-fg);
}

.tm-result-message {
    font-size: 13px;
    line-height: 1.55;
    color: var(--oui-fg-2);
    margin-top: 2px;
}

.tm-result-good .tm-result-head {
    background: var(--oui-success-tint);
}

.tm-result-good .tm-result-head i {
    color: var(--oui-success);
}

.tm-result-mixed .tm-result-head {
    background: var(--oui-warning-tint);
}

.tm-result-mixed .tm-result-head i {
    color: var(--oui-warning);
}

.tm-result-bad .tm-result-head {
    background: var(--oui-danger-tint);
}

.tm-result-bad .tm-result-head i {
    color: var(--oui-danger);
}

.tm-issues {
    border-top: 1px solid var(--oui-stroke);
    padding: 12px 14px;
}

.tm-issues-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--oui-fg);
}

.tm-issues-count {
    display: inline-block;
    font-size: 11px;
    border-radius: 10px;
    padding: 1px 8px;
    margin-left: 4px;
    background: var(--oui-bg-canvas);
    color: var(--oui-fg-2);
}

.tm-issues-error .tm-issues-count {
    background: var(--oui-danger-tint);
    color: var(--oui-danger);
}

.tm-issues-warning .tm-issues-count {
    background: var(--oui-warning-tint);
    color: var(--oui-warning);
}

.tm-issue-table code {
    background: var(--oui-bg-canvas);
    color: var(--oui-fg);
    border-radius: var(--oui-radius-sm);
    padding: 2px 6px;
    word-break: break-all;
}

.tm-empty-value {
    font-style: italic;
    color: var(--oui-fg-3);
}

.tm-issues-more {
    font-size: 12px;
    color: var(--oui-fg-2);
    margin-top: 8px;
}

/* --- the Add by Excel dialog ------------------------------------------- */

/* The dialog is the picker's detail panel and its upload card, stacked. The
   guide can be long, so the body scrolls rather than the page behind it. */
.tm-modal .modal-body {
    max-height: 70vh;
    overflow-y: auto;
}

.tm-modal .tm-detail-head {
    display: none; /* the dialog title already names the template */
}

.tm-modal-loading {
    padding: 18px 2px;
    font-size: 13px;
    color: var(--oui-fg-3);
}

/* The guide sits under the file box, so the rule is on top of it rather than
   under it. */
.tm-modal-guide {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--oui-stroke-2);
}

.tm-modal-result:empty {
    display: none;
}

/* The two fixed mass-upload workbooks, under the generated catalogue. They are
   plain links, so they need what a button already had. */
.tm-extras {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--oui-stroke-2);
}

a.tm-item,
a.tm-item:hover,
a.tm-item:focus {
    text-decoration: none;
}

/* On a narrow screen the picker sits above the detail, so it should not eat
   the screen before the user has scrolled to what they picked. */
@media (max-width: 991px) {
    .tm-list {
        max-height: 320px;
    }
}

/* ==========================================================================
   16. Drop-downs — bootstrap-select
   ==========================================================================
   Every <select> a user picks from in this suite is a bootstrap-select
   (CLAUDE.md section 3c). There are ~600 of them, so this is the single most
   repeated control in the product and it was the last one still wearing the
   Stisla template's look: a 10px pill, a washed #fdfdff91 fill, a purple
   #6777ef hover on every menu row and a purple focus ring — none of which are
   in the Optegrity palette.

   This section restyles it as a Fluent 2 combobox: a flat white field with a
   hairline border, a brand-coloured 2px bottom stroke while it is open, an
   elevated 8px flyout, and rows that tint rather than fill. It sits on top of
   the vendor stylesheet (assets/libraries/bootstrap-select/css) rather than
   editing it — that file has drifted per app, and optegrity-ui.css loads after
   it in every head.html, so overriding here reaches all of them at once. The
   vendor rules it is fighting carry !important, which is why several of these
   do too.

   HEIGHT: the field is 42px, matching .form-control in style.css, not the 32px
   a Fluent control would normally be. A drop-down almost always sits beside a
   text box in the same .form-group, and a control that is 10px shorter than
   the input next to it reads as broken. When .form-control is modernised, this
   comes down with it.
   ========================================================================== */

/* --- The closed field ---------------------------------------------------- */
.bootstrap-select > .dropdown-toggle {
    min-height: 42px;
    /* Room on the right for the chevron, which is positioned, not laid out. */
    padding: 8px 32px 8px 12px;
    font-family: var(--oui-font);
    font-size: 14px;
    /* .btn makes labels 600; this is a field's value, not a button's label. */
    font-weight: 400;
    line-height: 20px;
    text-align: left;
    color: var(--oui-fg) !important;
    background-color: var(--oui-bg) !important;
    border: 1px solid var(--oui-stroke) !important;
    border-radius: var(--oui-radius-sm);
    box-shadow: none;
    transition: border-color var(--oui-duration) var(--oui-ease), background-color var(--oui-duration) var(--oui-ease), box-shadow var(--oui-duration) var(--oui-ease);
}

    /* Hover darkens the stroke and leaves the surface alone — a field invites
       typing, so it must not look like a button lighting up. */
    .bootstrap-select > .dropdown-toggle:hover {
        background-color: var(--oui-bg) !important;
        border-color: var(--oui-fg-3) !important;
    }

    /* Fluent's signature: the whole border stays hairline and a 2px brand
       stroke appears along the bottom edge. Drawn as an inset shadow so it
       follows the 4px radius and costs no layout. */
    .bootstrap-select > .dropdown-toggle:focus,
    .bootstrap-select > .dropdown-toggle:active,
    .bootstrap-select.show > .dropdown-toggle {
        background-color: var(--oui-bg) !important;
        border-color: var(--oui-stroke) !important;
        box-shadow: inset 0 -2px 0 0 var(--oui-brand) !important;
        outline: none !important;
    }

    /* Keyboard users still get the double stroke .btn:focus-visible gives
       every other control; the underline above is the mouse-click state. */
    .bootstrap-select > .dropdown-toggle:focus-visible {
        outline: 2px solid var(--oui-fg) !important;
        outline-offset: 1px;
    }

/* Nothing chosen yet: the same muted grey a placeholder uses everywhere. */
.bootstrap-select > .dropdown-toggle.bs-placeholder,
.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,
.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,
.bootstrap-select > .dropdown-toggle.bs-placeholder:active {
    color: var(--oui-fg-3) !important;
}

/* Disabled reads as inert rather than as a button that has gone flat. */
.bootstrap-select.disabled > .dropdown-toggle,
.bootstrap-select > .dropdown-toggle.disabled,
.bootstrap-select > .dropdown-toggle:disabled {
    background-color: var(--oui-bg-subtle) !important;
    border-color: var(--oui-stroke-2) !important;
    color: var(--oui-fg-disabled) !important;
    box-shadow: none !important;
    opacity: 1;
}

/* Validation, from either the Bootstrap 4 or the Bootstrap 3 convention. */
.bootstrap-select.is-invalid > .dropdown-toggle,
.has-error .bootstrap-select > .dropdown-toggle,
.error .bootstrap-select > .dropdown-toggle,
.was-validated .bootstrap-select .selectpicker:invalid + .dropdown-toggle {
    border-color: var(--oui-danger) !important;
}

.bootstrap-select.is-valid > .dropdown-toggle,
.was-validated .bootstrap-select .selectpicker:valid + .dropdown-toggle {
    border-color: var(--oui-success) !important;
}

/* --- The chevron ---------------------------------------------------------
   The plugin emits a <span class="caret">, a Bootstrap 3 class that Bootstrap
   4 does not style at all, so what is actually on screen is Bootstrap's own
   ::after triangle. Hide the leftover span and bend the triangle into a
   chevron with two borders and a rotation — a solid caret is the one piece of
   Bootstrap 3 that dates the whole control. */
.bootstrap-select > .dropdown-toggle .caret {
    display: none !important;
}

.bootstrap-select > .dropdown-toggle::after {
    position: absolute;
    top: 50%;
    right: 13px;
    width: 7px;
    height: 7px;
    margin: -5px 0 0 0;
    border: solid var(--oui-fg-2);
    border-width: 0 0 1.5px 1.5px;
    border-radius: 1px;
    transform: rotate(-45deg);
    transition: transform var(--oui-duration) var(--oui-ease), margin-top var(--oui-duration) var(--oui-ease);
    content: "";
}

.bootstrap-select.show > .dropdown-toggle::after {
    margin-top: -2px;
    transform: rotate(135deg);
}

.bootstrap-select.disabled > .dropdown-toggle::after,
.bootstrap-select > .dropdown-toggle.disabled::after {
    border-color: var(--oui-fg-disabled);
}

/* --- The clear button ----------------------------------------------------
   A Fluent combobox is clearable; bootstrap-select is not. The plugin gives a
   multiple select a Deselect All inside the flyout — two clicks and a scroll —
   and a single select nothing at all, so undoing a choice meant reloading the
   page or hunting for the blank row. optegrity-ui.js adds this button to every
   drop-down in the suite (CLAUDE.md section 3c); nothing here is per page.

   It is a sibling of the plugin's toggle, not a child — a button inside a
   button is invalid markup — and the wrapper is `.dropdown .bootstrap-select`,
   which Bootstrap has already made `position: relative`, so it can be placed
   against the field without touching the vendor sheet. It sits left of the
   chevron, which is drawn at right: 13px and 7px wide.

   On screen only while something is chosen: an x against an empty field is a
   control that does nothing, and 42px of field is not wide enough to spend on
   one. .oui-has-value is written by the script, off the <select>'s own
   selection rather than off .bs-placeholder, so a list whose blank row is
   labelled "All" is still read as empty. */
.bootstrap-select > .oui-select-clear {
    position: absolute;
    top: 50%;
    right: 28px;
    z-index: 1;
    display: none;
    width: 20px;
    height: 20px;
    margin-top: -10px;
    padding: 0;
    color: var(--oui-fg-3);
    background-color: transparent;
    border: 0;
    border-radius: var(--oui-radius-sm);
    cursor: pointer;
    transition: color var(--oui-duration) var(--oui-ease), background-color var(--oui-duration) var(--oui-ease);
}

.bootstrap-select.oui-has-value > .oui-select-clear {
    display: block;
}

    /* Nothing to clear on a field the user cannot change. */
    .bootstrap-select.disabled > .oui-select-clear,
    .bootstrap-select.oui-has-value.disabled > .oui-select-clear {
        display: none;
    }

    .bootstrap-select > .oui-select-clear:hover {
        color: var(--oui-fg);
        background-color: var(--oui-bg-hover);
    }

    .bootstrap-select > .oui-select-clear:active {
        color: var(--oui-fg);
        background-color: var(--oui-bg-pressed);
    }

    .bootstrap-select > .oui-select-clear:focus {
        outline: none;
    }

    /* The button is in the tab order once it is on screen, so it needs the
       same double stroke every other control gives a keyboard user. */
    .bootstrap-select > .oui-select-clear:focus-visible {
        color: var(--oui-fg);
        outline: 2px solid var(--oui-fg);
        outline-offset: 1px;
    }

    /* The x, drawn rather than fetched — same reasoning as the search box's
       magnifier below: a data-URI would have to carry a literal colour and an
       icon font may not have loaded by the time the field first paints. */
    .bootstrap-select > .oui-select-clear::before,
    .bootstrap-select > .oui-select-clear::after {
        position: absolute;
        top: 9px;
        left: 5px;
        width: 10px;
        height: 1.5px;
        background-color: currentColor;
        border-radius: 1px;
        content: "";
    }

    .bootstrap-select > .oui-select-clear::before {
        transform: rotate(45deg);
    }

    .bootstrap-select > .oui-select-clear::after {
        transform: rotate(-45deg);
    }

/* The value must not run under the button: the field's own padding-right only
   clears the chevron. Widened only while the button is showing, so an empty
   field keeps every pixel it has for its placeholder. */
.bootstrap-select.oui-has-value > .dropdown-toggle {
    padding-right: 52px;
}

/* --- The flyout ----------------------------------------------------------
   `> .dropdown-menu` on purpose: the plugin nests a second .dropdown-menu
   (the .inner <ul>) inside this one, and it must stay a bare list. */
.bootstrap-select > .dropdown-menu {
    margin-top: 4px;
    padding: 4px;
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
}

    /* Fades in only. NEVER animate `transform` here, however small: Bootstrap 4
       positions a dropdown with Popper, which writes the placement into the
       element's inline `transform` — and a running CSS animation outranks an
       inline style for the property it animates. A one-line translateY put the
       menu back at its untransformed origin for the length of the animation.
       With data-container='body' that origin is `.bs-container`, which the
       vendor sheet pins to top:0 left:0, so the flyout appeared in the corner
       of the window and then snapped into place when the animation ended.
       Elevation is what says this is a flyout; it does not need to move. */
    .bootstrap-select > .dropdown-menu.show {
        animation: oui-select-open .12s var(--oui-ease);
    }

.bootstrap-select.dropup > .dropdown-menu {
    margin-top: 0;
    margin-bottom: 4px;
}

@keyframes oui-select-open {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@media (prefers-reduced-motion: reduce) {
    .bootstrap-select > .dropdown-menu.show {
        animation: none;
    }
}

/* --- The search box ------------------------------------------------------
   data-live-search='true' belongs on every drop-down in the suite, so this
   input is on screen as often as the field itself. */
.bootstrap-select .bs-searchbox {
    position: relative;
    padding: 4px 4px 8px;
}

    .bootstrap-select .bs-searchbox .form-control {
        height: 34px;
        /* Left padding clears the magnifier drawn below. */
        padding: 6px 10px 6px 32px;
        font-size: 14px;
        line-height: 20px;
        color: var(--oui-fg);
        background-color: var(--oui-bg);
        border: 1px solid var(--oui-stroke);
        border-radius: var(--oui-radius-sm);
        box-shadow: none;
    }

        /* !important because style.css blanks every .form-control:focus shadow
           with one of its own. */
        .bootstrap-select .bs-searchbox .form-control:focus {
            background-color: var(--oui-bg);
            border-color: var(--oui-stroke);
            box-shadow: inset 0 -2px 0 0 var(--oui-brand) !important;
        }

        .bootstrap-select .bs-searchbox .form-control::placeholder {
            color: var(--oui-fg-3);
        }

    /* The magnifier, drawn rather than fetched: a data-URI would have to carry
       a literal colour, and an icon font is not guaranteed to have loaded by
       the time the flyout opens. ::before is the lens, ::after the handle. */
    .bootstrap-select .bs-searchbox::before {
        position: absolute;
        top: 15px;
        left: 12px;
        width: 9px;
        height: 9px;
        border: 1.5px solid var(--oui-fg-3);
        border-radius: 50%;
        pointer-events: none;
        content: "";
    }

    .bootstrap-select .bs-searchbox::after {
        position: absolute;
        top: 25px;
        left: 22px;
        width: 6px;
        height: 1.5px;
        background-color: var(--oui-fg-3);
        border-radius: 1px;
        transform: rotate(45deg);
        transform-origin: left center;
        pointer-events: none;
        content: "";
    }

/* --- Select All / Deselect All (multiple only) --------------------------- */
.bootstrap-select .bs-actionsbox,
.bootstrap-select .bs-searchbox + .bs-actionsbox {
    padding: 0 4px 8px;
}

.bootstrap-select .bs-actionsbox .btn-group {
    display: flex;
    width: 100%;
    gap: 8px;
}

    /* A .btn-group would normally weld these two together; in a flyout they
       read better as two separate utilities. */
    .bootstrap-select .bs-actionsbox .btn-group button {
        flex: 1 1 0;
        width: auto;
        min-height: 30px;
        margin: 0 !important;
        padding: 4px 10px;
        font-size: 13px;
        font-weight: 600;
        color: var(--oui-fg) !important;
        background-color: var(--oui-bg) !important;
        border: 1px solid var(--oui-stroke) !important;
        border-radius: var(--oui-radius-sm) !important;
    }

        .bootstrap-select .bs-actionsbox .btn-group button:hover,
        .bootstrap-select .bs-actionsbox .btn-group button:focus {
            background-color: var(--oui-bg-hover) !important;
        }

        .bootstrap-select .bs-actionsbox .btn-group button:active {
            background-color: var(--oui-bg-pressed) !important;
        }

/* The mobile "Close" button gets the same treatment. */
.bootstrap-select .bs-donebutton {
    padding: 0 4px 8px;
}

    .bootstrap-select .bs-donebutton .btn-group button {
        width: 100%;
        min-height: 30px;
        font-size: 13px;
        font-weight: 600;
        color: var(--oui-fg) !important;
        background-color: var(--oui-bg) !important;
        border: 1px solid var(--oui-stroke) !important;
        border-radius: var(--oui-radius-sm) !important;
    }

/* --- The rows ------------------------------------------------------------
   The vendor sheet pads the <li> and paints the hover on it; move both onto
   the <a> so the tint is a rounded row inside the flyout's padding rather than
   a full-bleed band. */
.bootstrap-select .dropdown-menu li {
    padding: 0;
}

    .bootstrap-select .dropdown-menu li:hover,
    .bootstrap-select .dropdown-menu li:active,
    .bootstrap-select .dropdown-menu li.selected,
    .bootstrap-select .dropdown-menu .selected {
        background-color: transparent;
    }

    .bootstrap-select .dropdown-menu li a {
        position: relative;
        margin: 1px 0;
        padding: 7px 12px;
        font-size: 14px;
        font-weight: 400;
        line-height: 20px;
        color: var(--oui-fg) !important;
        background-color: transparent;
        border-radius: var(--oui-radius-sm);
        transition: background-color var(--oui-duration) var(--oui-ease);
    }

        /* Hover and the keyboard cursor are the same state: a neutral tint.
           .active is what bootstrap-select marks the arrow-key row with. */
        .bootstrap-select .dropdown-menu li a:hover,
        .bootstrap-select .dropdown-menu li a:focus,
        .bootstrap-select .dropdown-menu li a.active,
        .bootstrap-select .dropdown-menu li.active > a {
            color: var(--oui-fg) !important;
            background-color: var(--oui-bg-hover) !important;
            outline: none;
        }

        /* Chosen: brand tint plus a leading accent bar, the same way the rest
           of the system marks a selection. It has to out-specify the hover
           rules above, because a row can be selected and hovered at once. */
        .bootstrap-select .dropdown-menu li a.selected,
        .bootstrap-select .dropdown-menu li.selected > a,
        .bootstrap-select .dropdown-menu li a.selected:hover,
        .bootstrap-select .dropdown-menu li.selected > a:hover,
        .bootstrap-select .dropdown-menu li a.selected.active,
        .bootstrap-select .dropdown-menu li.selected > a.active {
            color: var(--oui-fg) !important;
            background-color: var(--oui-brand-tint) !important;
            font-weight: 600;
        }

            .bootstrap-select .dropdown-menu li a.selected:hover,
            .bootstrap-select .dropdown-menu li.selected > a:hover,
            .bootstrap-select .dropdown-menu li a.selected.active,
            .bootstrap-select .dropdown-menu li.selected > a.active {
                background-color: var(--oui-brand-tint-hover) !important;
            }

            .bootstrap-select .dropdown-menu li a.selected::before,
            .bootstrap-select .dropdown-menu li.selected > a::before {
                position: absolute;
                top: 6px;
                bottom: 6px;
                left: 0;
                width: 3px;
                background-color: var(--oui-brand);
                border-radius: 0 2px 2px 0;
                content: "";
            }

        /* An option inside an <optgroup>. The vendor indents it 2.25em, which
           leaves a hole between the selection bar and the text. */
        .bootstrap-select .dropdown-menu li a.opt {
            padding-left: 24px;
        }

        .bootstrap-select .dropdown-menu li.disabled a,
        .bootstrap-select .dropdown-menu li a.disabled {
            color: var(--oui-fg-disabled) !important;
            background-color: transparent !important;
        }

            .bootstrap-select .dropdown-menu li.disabled a::before {
                display: none;
            }

    /* Subtext. The vendor turns this white on the active row, which was for a
       saturated fill; ours is a tint, so it stays grey. */
    .bootstrap-select .dropdown-menu li small,
    .bootstrap-select .dropdown-menu li.active small,
    .bootstrap-select .dropdown-menu li a.active small {
        padding-left: 6px;
        font-size: 12px;
        color: var(--oui-fg-3) !important;
    }

    /* The tick on a multiple select — the plugin draws it from two borders,
       so it takes the colour it inherits. */
    .bootstrap-select .dropdown-menu li a span.check-mark {
        color: var(--oui-brand);
    }

.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark {
    top: 50%;
    right: 12px;
    margin-top: -8px;
}

/* Optgroup headings. */
.bootstrap-select .dropdown-menu .dropdown-header {
    padding: 8px 12px 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--oui-fg-2);
}

.bootstrap-select .dropdown-menu .divider,
.bootstrap-select .dropdown-menu .dropdown-divider {
    height: 1px;
    margin: 4px 8px;
    background-color: var(--oui-stroke-2);
    border: 0;
}

/* Nothing matched what was typed. The vendor draws a grey slab; a quiet line
   of text is enough, and it must be able to wrap — search terms are long. */
.bootstrap-select .no-results {
    margin: 0;
    padding: 10px 12px;
    background: transparent;
    border: 0;
    font-size: 13px;
    color: var(--oui-fg-3);
    text-align: center;
    white-space: normal;
}

/* The maxOptions ceiling — a notice, not a failure, so it is the warning
   tint rather than the danger one. */
.bootstrap-select .notify {
    width: auto;
    margin: 0 4px;
    padding: 6px 10px;
    background: var(--oui-warning-tint);
    border: 1px solid var(--oui-warning);
    border-radius: var(--oui-radius-sm);
    box-shadow: none;
    font-size: 12px;
    color: var(--oui-warning);
    opacity: 1;
}

/* ==========================================================================
   17. Form fields — inputs, textareas, addons
   ==========================================================================
   Section 16 gave the drop-down the system's control shape. This gives it to
   the field standing next to it, which is the only reason this section exists:
   a 4px white combobox beside a 10px tinted text box does not read as one
   form, it reads as one of them being broken.

   Three template layers had to be undone, and they disagree with each other:
   Bootstrap ships #ced4da at 10px in this build, style.css repaints it
   #dddedf on a #fdfdff91 wash, and both put a blue glow on focus. What is
   left is the Fluent 2 field — flat white, one hairline, 4px corners, and a
   2px Denim stroke along the bottom edge when it has focus.

   WHAT THIS DELIBERATELY DOES NOT CHANGE: the 42px height, the 10px/15px
   padding and the 14px type, all of which come from style.css. Only the shape
   and the colour move. Heights ripple through toolbars, input groups and
   every card header in the suite, and none of that is what makes the field
   look dated.
   ========================================================================== */

.form-control,
.custom-select,
.custom-file-label {
    color: var(--oui-fg);
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
    box-shadow: none;
    transition: border-color var(--oui-duration) var(--oui-ease), box-shadow var(--oui-duration) var(--oui-ease);
}

    /* Same hover as the drop-down: the stroke darkens, the surface does not
       move. A field is not a button lighting up. */
    .form-control:hover,
    .custom-select:hover {
        border-color: var(--oui-fg-3);
    }

    /* style.css blanks every .form-control:focus box-shadow with an
       !important of its own, so the underline has to carry one too. */
    .form-control:focus,
    .custom-select:focus,
    .custom-file-input:focus ~ .custom-file-label {
        color: var(--oui-fg);
        background-color: var(--oui-bg);
        border-color: var(--oui-stroke) !important;
        box-shadow: inset 0 -2px 0 0 var(--oui-brand) !important;
        outline: none;
    }

    .form-control::placeholder {
        color: var(--oui-fg-3);
        opacity: 1;
    }

    /* Read-only still has to be read: the surface goes inert, the text does
       not fade out. Disabled is the one that recedes. */
    .form-control[readonly] {
        background-color: var(--oui-bg-subtle);
        color: var(--oui-fg-2);
        border-color: var(--oui-stroke-2);
    }

    .form-control:disabled,
    .custom-select:disabled {
        background-color: var(--oui-bg-subtle);
        color: var(--oui-fg-disabled);
        border-color: var(--oui-stroke-2);
        opacity: 1;
        cursor: not-allowed;
    }

/* An addon is furniture attached to the field, not a second field. */
.input-group-text {
    color: var(--oui-fg-2);
    background-color: var(--oui-bg-subtle);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
}

.custom-file-label::after {
    color: var(--oui-fg);
    background-color: var(--oui-bg-hover);
    border-left: 1px solid var(--oui-stroke);
    border-radius: 0 var(--oui-radius-sm) var(--oui-radius-sm) 0;
}

/* Validation, matching what section 16 does to a drop-down. Bootstrap also
   paints an icon into the background; the tint is enough and the icon
   collides with the fields that already carry one. */
.form-control.is-invalid,
.was-validated .form-control:invalid,
.has-error .form-control,
.error .form-control {
    border-color: var(--oui-danger);
    background-image: none;
}

    .form-control.is-invalid:focus,
    .was-validated .form-control:invalid:focus {
        border-color: var(--oui-danger) !important;
        box-shadow: inset 0 -2px 0 0 var(--oui-danger) !important;
    }

.form-control.is-valid,
.was-validated .form-control:valid {
    border-color: var(--oui-success);
    background-image: none;
}

/* ==========================================================================
   18. The suite background
   --------------------------------------------------------------------------
   One background for every page of every app, drawn as SVG so it stays crisp
   at any size: soft glass panels over a pale canvas, with the brand's quiet
   peach / cyan / lilac glows. It replaces each app's MainBackground*.jpg from
   style.css — this file loads after style.css, so this rule wins.

   Cards and content surfaces keep their own solid backgrounds; only the
   canvas between them shows the image. Print keeps a plain white page.
   ========================================================================== */

body {
    background: #f2f3f9 url('../img/background/optegrity-bg.svg?rnd=1') no-repeat center center fixed; /* design-system-ok — the fallback canvas under the requested suite background */
    background-size: cover;
}

@media print {
    body {
        background: #fff !important;
    }
}

/* ==========================================================================
   19. The Welcome page
   --------------------------------------------------------------------------
   /Welcome is the suite's front door: shown on a first login (before any app
   has been opened) and to users with no application access. Everything here
   uses the system's tokens; the page itself carries no inline styling.
   ========================================================================== */

.welcome-hero {
    max-width: 760px;
    margin: 24px auto 8px;
    text-align: center;
}

    .welcome-hero h1 {
        font-size: 34px;
        font-weight: 700;
        color: var(--oui-fg);
        letter-spacing: -0.5px;
    }

        .welcome-hero h1 .welcome-wave {
            display: inline-block;
            transform-origin: 70% 70%;
            animation: welcome-wave 1.6s ease 0.4s 1;
        }

    .welcome-hero p {
        font-size: 16px;
        color: var(--oui-fg-2);
        margin: 10px 0 0;
    }

@keyframes welcome-wave {
    0% { transform: rotate(0); }
    20% { transform: rotate(16deg); }
    40% { transform: rotate(-8deg); }
    60% { transform: rotate(12deg); }
    80% { transform: rotate(-4deg); }
    100% { transform: rotate(0); }
}

/* --- the getting-started checklist: three steps, a progress track, and a
       little celebration when all three are done ---------------------------- */
.welcome-steps {
    max-width: 980px;
    margin: 26px auto;
}

.welcome-progress {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 6px 18px;
}

.welcome-progress-track {
    flex: 1;
    height: 8px;
    border-radius: 4px;
    background: var(--oui-stroke-2);
    overflow: hidden;
}

.welcome-progress-fill {
    height: 100%;
    width: 0;
    border-radius: 4px;
    background: linear-gradient(90deg, var(--oui-brand), var(--oui-brand-accent));
    transition: width .45s var(--oui-ease);
}

.welcome-progress-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--oui-fg-2);
    white-space: nowrap;
}

.welcome-step {
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-2);
    margin-bottom: 12px;
    overflow: hidden;
    transition: box-shadow var(--oui-duration) var(--oui-ease), transform var(--oui-duration) var(--oui-ease);
}

    .welcome-step:hover {
        box-shadow: var(--oui-shadow-8);
    }

.welcome-step-head {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    cursor: pointer;
    user-select: none;
}

.welcome-step-badge {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    flex: 0 0 34px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    color: var(--oui-brand);
    background: var(--oui-brand-tint);
    border: 1px solid var(--oui-brand-muted);
    transition: background var(--oui-duration) var(--oui-ease);
}

.welcome-step.done .welcome-step-badge {
    background: var(--oui-success-tint);
    border-color: var(--oui-success);
    color: var(--oui-success);
}

.welcome-step-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--oui-fg);
    flex: 1;
}

.welcome-step-state {
    font-size: 12px;
    color: var(--oui-fg-3);
}

.welcome-step.done .welcome-step-state {
    color: var(--oui-success);
    font-weight: 600;
}

.welcome-step-body {
    display: none;
    padding: 2px 18px 16px 66px;
    color: var(--oui-fg-2);
    font-size: 14px;
    line-height: 1.55;
}

.welcome-step.open .welcome-step-body {
    display: block;
}

.welcome-step-body .welcome-hint {
    display: inline-block;
    background: var(--oui-brand-tint);
    border-left: 3px solid var(--oui-brand);
    border-radius: var(--oui-radius-sm);
    padding: 6px 10px;
    margin-top: 8px;
}

.welcome-done-note {
    display: none;
    text-align: center;
    font-size: 15px;
    font-weight: 600;
    color: var(--oui-success);
    margin: 6px 0 2px;
}

.welcome-steps.all-done .welcome-done-note {
    display: block;
    animation: welcome-pop .4s var(--oui-ease);
}

@keyframes welcome-pop {
    0% { transform: scale(.85); opacity: 0; }
    100% { transform: scale(1); opacity: 1; }
}

/* --- the app cards ------------------------------------------------------- */
.welcome-apps {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
    max-width: 980px;
    margin: 10px auto 40px;
}

.welcome-app {
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-2);
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    transition: transform var(--oui-duration) var(--oui-ease), box-shadow var(--oui-duration) var(--oui-ease);
}

    .welcome-app:hover {
        transform: translateY(-3px);
        box-shadow: var(--oui-shadow-16);
    }

.welcome-app-icon {
    width: 52px;
    height: 52px;
    border-radius: var(--oui-radius-lg);
    background-size: cover;
    background-position: center;
    border: 1px solid var(--oui-stroke-2);
}

.welcome-app h3 {
    font-size: 17px;
    font-weight: 700;
    color: var(--oui-fg);
    margin: 4px 0 0;
}

.welcome-app p {
    font-size: 13px;
    color: var(--oui-fg-2);
    flex: 1;
    margin: 0;
}

/* --- no access ----------------------------------------------------------- */
.welcome-noaccess {
    max-width: 640px;
    margin: 30px auto 60px;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-4);
    padding: 34px 30px;
    text-align: center;
}

    .welcome-noaccess .welcome-noaccess-icon {
        font-size: 44px;
        color: var(--oui-brand-muted);
        margin-bottom: 12px;
    }

    .welcome-noaccess h3 {
        font-size: 20px;
        font-weight: 700;
        color: var(--oui-fg);
    }

    .welcome-noaccess p {
        color: var(--oui-fg-2);
        font-size: 14px;
        margin-top: 8px;
    }

/* ==========================================================================
   20. Sign-in pages
   --------------------------------------------------------------------------
   /Default and /ForgetPassword: a Fluent 2 glass card centred on the suite
   background from section 18. The form controls themselves are section 17;
   this section is only the door frame around them.
   ========================================================================== */

.oui-auth .oui-auth-wrap {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.oui-auth-card {
    width: 100%;
    max-width: 430px;
    background: rgba(255, 255, 255, 0.82);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
    padding: 34px 34px 26px;
    animation: oui-auth-enter .5s var(--oui-ease);
}

@keyframes oui-auth-enter {
    0% { transform: translateY(14px); opacity: 0; }
    100% { transform: translateY(0); opacity: 1; }
}

.oui-auth-logos {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 18px;
}

.oui-auth-logo {
    max-width: 46%;
    max-height: 52px;
    object-fit: contain;
    transition: transform var(--oui-duration) var(--oui-ease);
}

    .oui-auth-logo:hover {
        transform: scale(1.04);
    }

.oui-auth-keyicon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--oui-brand-tint);
    border: 1px solid var(--oui-brand-muted);
    color: var(--oui-brand);
    font-size: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 14px;
}

.oui-auth-title {
    font-size: 24px;
    font-weight: 700;
    color: var(--oui-fg);
    margin: 0;
}

.oui-auth-emoji {
    display: inline-block;
}

.oui-auth-sub {
    font-size: 14px;
    color: var(--oui-fg-2);
    margin: 8px 0 22px;
}

.oui-auth-form .form-group {
    margin-bottom: 18px;
}

    .oui-auth-form .form-group label {
        font-weight: 600;
        color: var(--oui-fg);
        margin-bottom: 6px;
    }

.oui-auth-password {
    position: relative;
}

.oui-auth-eye {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 42px;
    border: none;
    background: transparent;
    color: var(--oui-fg-3);
    cursor: pointer;
}

    .oui-auth-eye:hover {
        color: var(--oui-fg);
    }

.oui-auth-caps {
    display: none;
    margin-top: 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--oui-warning);
}

.oui-auth-submit {
    width: 100%;
    margin-top: 4px;
}

.oui-auth-links {
    text-align: center;
    margin-top: 14px;
    font-size: 13px;
}

    .oui-auth-links a {
        color: var(--oui-brand);
        font-weight: 600;
    }

.oui-auth-tip {
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--oui-stroke-2);
    font-size: 12.5px;
    color: var(--oui-fg-3);
    text-align: center;
}

    .oui-auth-tip i {
        color: var(--oui-warning);
        margin-right: 4px;
    }

    .oui-auth-tip span {
        transition: opacity .3s var(--oui-ease);
    }

/* ==========================================================================
   21. The suite navbar
   --------------------------------------------------------------------------
   The one top menu AppNav renders on every page: app links as quiet pills,
   the active app as a filled pill with an indicator, and a Settings flyout
   that is sized by its content and positioned under its toggle everywhere -
   style.css's `.dropdown-menu.show { width: 100% }` made it either narrower
   than its labels or as wide as the page, depending on the ancestor chain.
   ========================================================================== */

.main-navbar .navbar-nav > li.dropdown {
    position: relative;
}

.main-navbar .oui-appnav-link {
    color: #fff !important;
    border-radius: var(--oui-radius-sm);
    padding: 8px 14px !important;
    margin: 0 1px;
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .main-navbar .oui-appnav-link:hover {
        background-color: rgba(255, 255, 255, 0.14);
    }

.main-navbar .oui-appnav-active {
    background-color: rgba(255, 255, 255, 0.18);
    font-weight: 600;
    text-decoration: none !important;
    position: relative;
}

    .main-navbar .oui-appnav-active::after {
        content: '';
        position: absolute;
        left: 14px;
        right: 14px;
        bottom: 4px;
        height: 2px;
        border-radius: 1px;
        background: #fff;
    }

.oui-settings-menu {
    position: absolute;
    top: 100%;
    left: 0;
    width: max-content !important;
    min-width: 250px;
    max-width: 340px;
    padding: 6px 0;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
}

    .oui-settings-menu.show {
        width: max-content !important;
    }

    .oui-settings-menu .dropdown-item {
        white-space: nowrap;
        padding: 9px 18px;
    }


/* --------------------------------------------------------------------------
   21b. Quick Search - the navbar's search flyout
   --------------------------------------------------------------------------
   One control, in every app's navbar, drawn as a Fluent 2 command palette: a
   flat white surface at --oui-radius-lg on --oui-shadow-16, a search field in
   the same language as section 17, grouped rows that tint on hover and carry a
   leading brand accent bar when they are the keyboard's current row.

   The panel is `position: fixed` and placed by optegrity-ui.js - the navbar is
   a scroll container, so an absolute panel would be clipped out of existence
   (the same trap as the rail flyouts in section 11). Never give it a `top` or
   `left` here: the script owns both.
   -------------------------------------------------------------------------- */

.oui-qs {
    position: relative;
}

.main-navbar .oui-qs-toggle {
    color: #fff !important;
    border-radius: var(--oui-radius-sm);
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .main-navbar .oui-qs-toggle:hover,
    .main-navbar .oui-qs-toggle[aria-expanded='true'] {
        background-color: rgba(255, 255, 255, 0.18);
    }

    .main-navbar .oui-qs-toggle i {
        font-size: 20px;
    }

.oui-qs-panel {
    position: fixed;
    z-index: 9990;
    display: none;
    width: 440px;
    max-width: calc(100vw - 16px);
    flex-direction: column;
    overflow: hidden;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
}

    .oui-qs-panel.oui-qs-panel-open {
        display: flex;
    }

.oui-qs-field {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    padding: 10px 12px;
    border-bottom: 1px solid var(--oui-stroke-2);
}

.oui-qs-field-icon {
    position: absolute;
    left: 24px;
    font-size: 13px;
    color: var(--oui-fg-3);
    pointer-events: none;
}

.oui-qs-input {
    width: 100%;
    height: 38px;
    padding: 0 34px 0 34px;
    font-size: 14px;
    color: var(--oui-fg);
    background: var(--oui-bg-canvas);
    border: 1px solid transparent;
    border-radius: var(--oui-radius-sm);
    outline: none;
}

    .oui-qs-input::placeholder {
        color: var(--oui-fg-3);
    }

    .oui-qs-input:focus {
        background: var(--oui-bg);
        border-color: var(--oui-stroke);
        border-bottom: 2px solid var(--oui-brand);
    }

.oui-qs-clear {
    position: absolute;
    right: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    font-size: 11px;
    color: var(--oui-fg-3);
    background: transparent;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}

    .oui-qs-clear:hover {
        color: var(--oui-fg);
        background: var(--oui-bg-hover);
    }

.oui-qs-results {
    flex: 1 1 auto;
    min-height: 0;
    padding: 6px 0;
    overflow-y: auto;
}

.oui-qs-group {
    padding: 10px 16px 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--oui-fg-3);
}

.oui-qs-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 16px;
    color: var(--oui-fg);
    text-decoration: none !important;
    cursor: pointer;
}

    .oui-qs-row:hover,
    .oui-qs-row-active {
        color: var(--oui-fg);
        background: var(--oui-brand-tint);
    }

        .oui-qs-row-active::before {
            content: '';
            position: absolute;
            top: 6px;
            bottom: 6px;
            left: 0;
            width: 3px;
            border-radius: 0 2px 2px 0;
            background: var(--oui-brand);
        }

.oui-qs-row-icon {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    font-size: 12px;
    color: var(--oui-brand);
    background: var(--oui-brand-tint);
    border-radius: var(--oui-radius-sm);
}

.oui-qs-row-active .oui-qs-row-icon {
    color: #fff;
    background: var(--oui-brand);
}

.oui-qs-row-text {
    flex: 1 1 auto;
    min-width: 0;
}

.oui-qs-row-title {
    display: block;
    font-size: 13px;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

    .oui-qs-row-title mark {
        padding: 0;
        color: var(--oui-brand);
        font-weight: 600;
        background: transparent;
    }

.oui-qs-row-sub {
    display: block;
    font-size: 11px;
    color: var(--oui-fg-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.oui-qs-row-go {
    flex: 0 0 auto;
    font-size: 11px;
    color: var(--oui-fg-3);
    opacity: 0;
}

.oui-qs-row-active .oui-qs-row-go {
    opacity: 1;
}

.oui-qs-recent {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 16px;
    font-size: 13px;
    color: var(--oui-fg-2);
    text-align: left;
    background: transparent;
    border: 0;
    cursor: pointer;
}

    .oui-qs-recent:hover {
        color: var(--oui-fg);
        background: var(--oui-bg-hover);
    }

    .oui-qs-recent i {
        font-size: 11px;
        color: var(--oui-fg-3);
    }

.oui-qs-empty {
    padding: 26px 20px 30px;
    text-align: center;
}

    .oui-qs-empty i {
        font-size: 22px;
        color: var(--oui-brand-muted);
    }

    .oui-qs-empty p {
        margin: 10px 0 2px;
        font-size: 14px;
        font-weight: 600;
        color: var(--oui-fg);
    }

    .oui-qs-empty span {
        font-size: 12px;
        color: var(--oui-fg-2);
    }

.oui-qs-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 14px;
    font-size: 12px;
    color: var(--oui-fg-2);
}

.oui-qs-spinner {
    display: inline-block;
    width: 13px;
    height: 13px;
    border: 2px solid var(--oui-stroke);
    border-top-color: var(--oui-brand);
    border-radius: 50%;
    animation: oui-qs-spin .7s linear infinite;
}

@keyframes oui-qs-spin {
    to {
        transform: rotate(360deg);
    }
}

.oui-qs-foot {
    flex: 0 0 auto;
    display: flex;
    gap: 16px;
    padding: 8px 16px;
    font-size: 11px;
    color: var(--oui-fg-3);
    background: var(--oui-bg-canvas);
    border-top: 1px solid var(--oui-stroke-2);
}

    .oui-qs-foot kbd {
        display: inline-block;
        min-width: 18px;
        margin-right: 3px;
        padding: 1px 4px;
        font-family: inherit;
        font-size: 10px;
        color: var(--oui-fg-2);
        text-align: center;
        background: var(--oui-bg);
        border: 1px solid var(--oui-stroke);
        border-radius: 3px;
    }

/* --------------------------------------------------------------------------
   21c. The OpRobot launcher, and the other plain navbar icons
   -------------------------------------------------------------------------- */

.main-navbar .oui-navbar-icon {
    color: #fff !important;
    border-radius: var(--oui-radius-sm);
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .main-navbar .oui-navbar-icon:hover {
        background-color: rgba(255, 255, 255, 0.18);
    }

    .main-navbar .oui-navbar-icon i {
        font-size: 20px;
    }

.main-navbar .oui-chat-launch {
    position: relative;
    color: #fff !important;
    border-radius: var(--oui-radius-sm);
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .main-navbar .oui-chat-launch:hover {
        background-color: rgba(255, 255, 255, 0.18);
    }

    .main-navbar .oui-chat-launch i {
        font-size: 20px;
    }

.oui-chat-launch-dot {
    position: absolute;
    top: 10px;
    right: 10px;
    width: 8px;
    height: 8px;
    border: 2px solid var(--oui-brand);
    border-radius: 50%;
    background: var(--oui-success-accent);
}

/* ==========================================================================
   22. The profile page
   --------------------------------------------------------------------------
   /UserProfile: a profile hero in the suite's glass language above the edit
   card. The old Stisla profile-widget (banner + floating portrait) is gone.
   ========================================================================== */

.oui-profile-hero {
    display: flex;
    align-items: flex-start;
    gap: 22px;
    background: rgba(255, 255, 255, 0.78);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border: 1px solid rgba(255, 255, 255, 0.9);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-4);
    padding: 24px;
    margin-bottom: 20px;
}

.oui-profile-avatar {
    width: 84px;
    height: 84px;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid var(--oui-bg);
    box-shadow: 0 0 0 2px var(--oui-brand-muted);
    flex: 0 0 84px;
}

.oui-profile-summary {
    flex: 1;
    min-width: 0;
}

.oui-profile-name {
    font-size: 22px;
    font-weight: 700;
    color: var(--oui-fg);
}

.oui-profile-role {
    display: inline-block;
    vertical-align: middle;
    margin-left: 8px;
    padding: 3px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--oui-brand);
    background: var(--oui-brand-tint);
    border: 1px solid var(--oui-brand-muted);
    border-radius: 999px;
}

.oui-profile-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    margin-top: 8px;
    font-size: 13px;
    color: var(--oui-fg-2);
}

    .oui-profile-meta i {
        color: var(--oui-fg-3);
        margin-right: 5px;
    }

.oui-profile-bio {
    margin-top: 10px;
    font-size: 14px;
    color: var(--oui-fg-2);
}

.oui-profile-social {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

    .oui-profile-social a {
        width: 32px;
        height: 32px;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        color: var(--oui-brand);
        background: var(--oui-brand-tint);
        border: 1px solid var(--oui-brand-muted);
        transition: background-color var(--oui-duration) var(--oui-ease);
    }

        .oui-profile-social a:hover {
            background-color: var(--oui-brand-muted);
        }

@media (max-width: 575.98px) {
    .oui-profile-hero {
        flex-direction: column;
        align-items: center;
        text-align: center;
    }

    .oui-profile-meta {
        justify-content: center;
    }
}

/* ==========================================================================
   23. Grid cell menu — right-click Copy on an ag-Grid cell
   ==========================================================================
   Every table of records in the suite is an ag-Grid (CLAUDE.md section 3g),
   and right-clicking a cell now opens this to copy the value out of it. The
   gesture used to be a double-click with no menu and nothing on screen to say
   so; optegrity-ui.js explains why that moved.

   It is the same flyout as everything else that opens over the page — the
   drop-down's menu in section 16, the rail's in section 11: white surface, a
   hairline border, 8px corners, shadow-16 for the elevation, and rows that
   tint on hover rather than fill. Nothing here is new furniture; the only
   reason it is its own section is that it has no Bootstrap component to hang
   off, being built by script.

   ABOVE EVERYTHING: it is opened from a cell, and a grid can sit inside a
   modal (1050) or behind the filter drawer (9998), with bootstrap-select's
   detached menus at 10050 and the loading overlay at 10060. This is the last
   thing opened, so it goes on top of all of them rather than tying with the
   overlay and settling it on document order. */
.oui-cell-menu {
    position: fixed;
    z-index: 10070;
    min-width: 160px;
    padding: 4px;
    background-color: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-16);
    font-family: var(--oui-font);
}

/* A row, not a button: .btn's border, weight and min-height would make this
   look like a toolbar stacked on the page. */
.oui-cell-menu-item {
    display: block;
    width: 100%;
    padding: 7px 10px;
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
    text-align: left;
    color: var(--oui-fg);
    background: none;
    border: 0;
    border-radius: var(--oui-radius-sm);
    cursor: pointer;
}

    .oui-cell-menu-item:hover,
    .oui-cell-menu-item:focus {
        background-color: var(--oui-bg-hover);
        color: var(--oui-fg);
        outline: none;
    }

/* An ag-Grid popup editor (AgSearchSelect, AgMultiSelect, any isPopup()
   editor) has to clear everything the page can stack over a grid: a
   Bootstrap modal (1050) and the filter drawer (9998). The z-index belongs
   on the CHILD: AG Grid's `.ag-popup` wrapper is position:static, where
   z-index does nothing at all -- it is `.ag-popup-child` that is absolutely
   positioned. (Where the popup lands is decided by popupParent, which
   TableCodeGenerator points at the containing modal.) */
.ag-popup-child,
.ag-custom-component-popup {
    z-index: 10060;
}

/* ==========================================================================
   24. Print previews — the report sheet and its print rules
   --------------------------------------------------------------------------
   One language for every "Print Preview" page: on screen the report is a
   white sheet centred on the canvas, like a document preview; on paper the
   sheet becomes the page. Pages compose it from these pieces and add NO
   styling of their own:

     .oui-print-toolbar          the screen-only action bar (Print, Back)
     .oui-print-sheet            the sheet itself
     .oui-print-head             title block: eyebrow, title, sub, logo/status
     .oui-print-status--*        status pill: success / warning / danger / info
     .oui-print-section          one section, with its accent-bar title
     .oui-kv / .oui-kv-item      label-over-value grid, two per row
     .oui-print-table            hairline data table, tinted header row
     .oui-print-gallery          attached-picture strip
     .oui-print-qr               a gallery holding a QR code: thumbnail sized
     .oui-print-watermark        the rotated "Copy" ghost

   PAPER SIZE IS THE ONE THING A PAGE STILL DECLARES. `@page` is a document
   rule -- CSS cannot scope it to an element -- so a report that must print
   landscape or on A3 (a wide week grid, an A3 kaizen sheet) carries exactly
   one line in its .aspx <head>, and nothing else:

       <style media="print">@page { size: A3; }</style>

   That is the only stylesheet a print page may own. Portrait letter/A4 is
   the browser default and needs no declaration.
   ========================================================================== */

.oui-print-toolbar {
    display: flex;
    justify-content: center;
    gap: 8px;
    max-width: 960px;
    margin: 16px auto;
}

.oui-print-sheet {
    max-width: 960px;
    /* A skill or training matrix is 55-60 columns wide. On screen it scrolls
       inside the sheet rather than pushing the whole page sideways; the print
       half of this section drops the constraint so nothing is clipped on
       paper (those two reports declare A3 landscape for the same reason). */
    overflow-x: auto;
    margin: 0 auto 32px;
    padding: 32px 40px;
    text-align: left;
    color: var(--oui-fg);
    font-family: var(--oui-font);
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke-2);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-8);
}

.oui-print-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    padding-bottom: 16px;
    margin-bottom: 4px;
    border-bottom: 2px solid var(--oui-brand);
}

.oui-print-eyebrow {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--oui-brand);
}

.oui-print-title {
    margin: 2px 0 0;
    font-size: 24px;
    font-weight: 700;
    line-height: 32px;
    color: var(--oui-fg);
}

.oui-print-sub {
    margin-top: 4px;
    font-size: 13px;
    color: var(--oui-fg-2);
}

.oui-print-head-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
    flex-shrink: 0;
}

    .oui-print-head-side img {
        max-height: 40px;
        max-width: 180px;
    }

.oui-print-status {
    display: inline-flex;
    align-items: center;
    padding: 3px 12px;
    font-size: 12px;
    font-weight: 600;
    line-height: 16px;
    white-space: nowrap;
    border: 1px solid;
    border-radius: 999px;
}

.oui-print-status--success { color: var(--oui-success); }
.oui-print-status--warning { color: var(--oui-warning); }
.oui-print-status--danger  { color: var(--oui-danger); }
.oui-print-status--info    { color: var(--oui-brand); }

.oui-print-section {
    margin-top: 20px;
}

/* A section that must start its own sheet. Use it only where the old page
   carried page-break-before: always -- an inline page-break is otherwise
   never written at a call site. */
.oui-print-section--break {
    page-break-before: always;
    break-before: page;
}

.oui-print-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
    padding-left: 10px;
    font-size: 14px;
    font-weight: 600;
    color: var(--oui-fg);
    border-left: 3px solid var(--oui-brand);
}

.oui-kv {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 12px 24px;
}

.oui-kv--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.oui-kv-item--wide {
    grid-column: 1 / -1;
}

.oui-kv-label {
    margin-bottom: 2px;
    font-size: 12px;
    color: var(--oui-fg-3);
}

.oui-kv-value {
    font-size: 14px;
    color: var(--oui-fg);
    overflow-wrap: break-word;
}

.oui-print-table {
    width: 100%;
    font-size: 13px;
    border-collapse: collapse;
}

    .oui-print-table th {
        padding: 8px 10px;
        text-align: left;
        font-size: 12px;
        font-weight: 600;
        color: var(--oui-fg);
        background: var(--oui-brand-tint);
        border: 1px solid var(--oui-stroke-2);
    }

    .oui-print-table td {
        padding: 8px 10px;
        vertical-align: top;
        color: var(--oui-fg-2);
        border: 1px solid var(--oui-stroke-2);
    }

    /* A grouping row - an area score, a subtotal - reads as a band. */
    .oui-print-table tr.oui-print-table-group td {
        font-weight: 600;
        color: var(--oui-fg);
        background: var(--oui-bg-canvas);
    }

    /* Row colour that CARRIES MEANING, not decoration: the shop-floor reports
       colour a line by what it is (a shortage, an open defect) and the reader
       scans for it. Only these two exist - a page never writes its own. */
    .oui-print-table tr.oui-print-row-danger td {
        color: var(--oui-danger);
    }

    .oui-print-table tr.oui-print-row-info td {
        color: var(--oui-brand);
    }

.oui-print-gallery {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

    .oui-print-gallery img {
        max-height: 220px;
        max-width: 100%;
        border: 1px solid var(--oui-stroke-2);
        border-radius: var(--oui-radius-sm);
    }

/* A QR code is scanned, not read, so it takes a thumbnail rather than the
   220px an attached picture gets. On paper a 96px code still scans from a
   phone at arm's length, and the sheet keeps the space for the report. */
.oui-print-gallery.oui-print-qr img {
    max-height: 96px;
    max-width: 96px;
    padding: 4px;
}

.oui-print-empty {
    font-size: 13px;
    color: var(--oui-fg-3);
}

.oui-print-watermark {
    position: fixed;
    bottom: 50%;
    right: 50%;
    z-index: 99;
    transform: rotate(45deg);
    font-size: 64px;
    color: var(--oui-fg-3);
    opacity: .25;
    pointer-events: none;
}

@media print {
    body {
        background: #fff !important;
    }

    .main-navbar,
    .main-sidebar,
    .navbar-bg,
    .oui-print-toolbar {
        display: none !important;
    }

    .oui-print-sheet {
        max-width: none;
        /* visible, not auto: a scroll container clips on paper */
        overflow-x: visible;
        margin: 0;
        padding: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
    }

    /* Chrome drops backgrounds when printing unless told otherwise; the
       tinted table header and the accent bars are part of the report. */
    .oui-print-sheet,
    .oui-print-table th,
    .oui-print-table tr.oui-print-table-group td,
    .oui-print-status,
    .oui-print-section-title {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    .oui-print-section,
    .oui-kv-item,
    .oui-print-table tr,
    .oui-print-gallery img {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    .oui-print-watermark {
        font-size: 100px;
    }
}

/* ==========================================================================
   25. OpRobot - the support conversation page
   --------------------------------------------------------------------------
   /OpRobot, a shared GlobalSystem page reached from the same navbar button in
   all six apps. Teams' own chat shape: a rail of conversations on the left, one
   conversation on the right, both inside a single flat surface at
   --oui-radius-lg on --oui-shadow-8. Bubbles are the only place in the suite
   where a filled brand surface carries text, which is what makes a chat read as
   a chat.

   The page owns no colour of its own: every value below is an --oui-* token.
   Its behaviour is assets/libraries/Ticket/Ticket.js.
   ========================================================================== */

.oui-chat {
    display: flex;
    align-items: stretch;
    height: calc(100vh - 190px);
    min-height: 520px;
    overflow: hidden;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    box-shadow: var(--oui-shadow-8);
}

/* --- the rail ------------------------------------------------------------ */

.oui-chat-rail {
    display: flex;
    flex: 0 0 320px;
    flex-direction: column;
    min-width: 0;
    border-right: 1px solid var(--oui-stroke-2);
    background: var(--oui-bg);
}

.oui-chat-rail-head {
    flex: 0 0 auto;
    padding: 16px;
    border-bottom: 1px solid var(--oui-stroke-2);
}

.oui-chat-rail-title {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 12px;
}

    .oui-chat-rail-title h2 {
        margin: 0;
        font-size: 16px;
        font-weight: 600;
        color: var(--oui-fg);
    }

.oui-chat-rail-count {
    font-size: 11px;
    color: var(--oui-fg-3);
}

.oui-chat-filters {
    flex: 0 0 auto;
    display: flex;
    gap: 6px;
    padding: 12px 16px 0;
}

.oui-chat-filter {
    padding: 4px 12px;
    font-size: 12px;
    color: var(--oui-fg-2);
    background: var(--oui-bg-canvas);
    border: 1px solid transparent;
    border-radius: 999px;
    cursor: pointer;
}

    .oui-chat-filter:hover {
        background: var(--oui-bg-hover);
    }

.oui-chat-filter-on {
    color: var(--oui-brand);
    font-weight: 600;
    background: var(--oui-brand-tint);
    border-color: var(--oui-brand-muted);
}

.oui-chat-rail-search {
    position: relative;
    flex: 0 0 auto;
    padding: 12px 16px;
}

    .oui-chat-rail-search i {
        position: absolute;
        top: 50%;
        left: 27px;
        font-size: 12px;
        color: var(--oui-fg-3);
        transform: translateY(-50%);
    }

    .oui-chat-rail-search input {
        width: 100%;
        height: 34px;
        padding: 0 10px 0 30px;
        font-size: 13px;
        color: var(--oui-fg);
        background: var(--oui-bg-canvas);
        border: 1px solid transparent;
        border-radius: var(--oui-radius-sm);
        outline: none;
    }

        .oui-chat-rail-search input:focus {
            background: var(--oui-bg);
            border-color: var(--oui-stroke);
            border-bottom: 2px solid var(--oui-brand);
        }

.oui-chat-list {
    flex: 1 1 auto;
    min-height: 0;
    padding: 0 8px 8px;
    overflow-y: auto;
}

.oui-chat-sentinel {
    flex: 0 0 auto;
    height: 1px;
}

.oui-chat-item {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    text-align: left;
    background: transparent;
    border: 0;
    border-radius: var(--oui-radius-sm);
    cursor: pointer;
}

    .oui-chat-item:hover {
        background: var(--oui-bg-hover);
    }

.oui-chat-item-on {
    background: var(--oui-brand-tint);
}

    .oui-chat-item-on::before {
        content: '';
        position: absolute;
        top: 8px;
        bottom: 8px;
        left: 0;
        width: 3px;
        border-radius: 0 2px 2px 0;
        background: var(--oui-brand);
    }

.oui-chat-item-dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    margin-top: 6px;
    border-radius: 50%;
    background: var(--oui-fg-3);
}

.oui-chat-item-dot--open {
    background: var(--oui-warning);
}

.oui-chat-item-dot--busy {
    background: var(--oui-brand-accent);
}

.oui-chat-item-dot--done {
    background: var(--oui-success-accent);
}

.oui-chat-item-body {
    flex: 1 1 auto;
    min-width: 0;
}

.oui-chat-item-title {
    display: block;
    font-size: 13px;
    line-height: 1.35;
    color: var(--oui-fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.oui-chat-item-meta {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    color: var(--oui-fg-3);
}

/* --- status pill --------------------------------------------------------- */

.oui-chat-pill {
    display: inline-block;
    flex: 0 0 auto;
    align-self: flex-start;
    padding: 2px 8px;
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
    border-radius: 999px;
}

.oui-chat-pill--open {
    color: var(--oui-warning);
    background: var(--oui-warning-tint);
}

.oui-chat-pill--busy {
    color: var(--oui-brand);
    background: var(--oui-brand-tint);
}

.oui-chat-pill--done {
    color: var(--oui-success);
    background: var(--oui-success-tint);
}

/* --- the right-hand panels ----------------------------------------------- */

.oui-chat-main {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
    background: var(--oui-bg-canvas);
}

.oui-chat-panel {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
}

    .oui-chat-panel[hidden] {
        display: none;
    }

/* --- home ---------------------------------------------------------------- */

#OpRobotHomePanel {
    padding: 28px 32px;
    overflow-y: auto;
}

.oui-chat-hero {
    display: flex;
    align-items: flex-start;
    gap: 18px;
    margin-bottom: 24px;
}

.oui-chat-hero-avatar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 54px;
    height: 54px;
    font-size: 22px;
    color: #fff;
    background: var(--oui-brand);
    border-radius: var(--oui-radius-lg);
}

.oui-chat-hero-text h1 {
    margin: 0 0 6px;
    font-size: 22px;
    font-weight: 600;
    color: var(--oui-fg);
}

.oui-chat-hero-text p {
    max-width: 640px;
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--oui-fg-2);
}

.oui-chat-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 18px;
}

.oui-chat-stat {
    flex: 1 1 130px;
    padding: 14px 16px;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
}

.oui-chat-stat-level {
    flex: 2 1 260px;
}

.oui-chat-stat-value {
    display: block;
    font-size: 22px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--oui-brand);
}

.oui-chat-stat-label {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--oui-fg-3);
}

.oui-chat-stat-hint {
    display: block;
    margin-top: 4px;
    font-size: 11px;
    color: var(--oui-fg-2);
}

.oui-chat-levelbar {
    height: 6px;
    margin-top: 10px;
    overflow: hidden;
    background: var(--oui-stroke-2);
    border-radius: 3px;
}

    .oui-chat-levelbar span {
        display: block;
        width: 0;
        height: 100%;
        background: var(--oui-brand);
        border-radius: 3px;
        transition: width .4s var(--oui-ease);
    }

.oui-chat-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 26px;
}

.oui-chat-badge {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    font-size: 12px;
    color: var(--oui-fg-3);
    background: var(--oui-bg);
    border: 1px dashed var(--oui-stroke);
    border-radius: 999px;
}

    .oui-chat-badge i {
        font-size: 12px;
    }

.oui-chat-badge-on {
    color: var(--oui-brand);
    font-weight: 600;
    background: var(--oui-brand-tint);
    border-style: solid;
    border-color: var(--oui-brand-muted);
}

.oui-chat-startcards {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
}

.oui-chat-startcard {
    display: flex;
    flex: 1 1 300px;
    align-items: center;
    gap: 14px;
    padding: 18px;
    text-align: left;
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg);
    cursor: pointer;
    transition: box-shadow var(--oui-duration) var(--oui-ease), border-color var(--oui-duration) var(--oui-ease);
}

    .oui-chat-startcard:hover {
        border-color: var(--oui-brand-muted);
        box-shadow: var(--oui-shadow-8);
    }

    .oui-chat-startcard > i {
        color: var(--oui-fg-3);
    }

.oui-chat-startcard-icon {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    font-size: 17px;
    border-radius: var(--oui-radius-sm);
}

.oui-chat-startcard-icon--problem {
    color: var(--oui-danger);
    background: var(--oui-danger-tint);
}

.oui-chat-startcard-icon--idea {
    color: var(--oui-warning);
    background: var(--oui-warning-tint);
}

.oui-chat-startcard-text {
    flex: 1 1 auto;
    min-width: 0;
}

    .oui-chat-startcard-text strong {
        display: block;
        font-size: 14px;
        font-weight: 600;
        color: var(--oui-fg);
    }

    .oui-chat-startcard-text em {
        display: block;
        margin-top: 2px;
        font-size: 12px;
        font-style: normal;
        color: var(--oui-fg-2);
    }

/* --- a conversation, new or old ------------------------------------------ */

.oui-chat-head {
    flex: 0 0 auto;
    position: relative;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 20px;
    background: var(--oui-bg);
    border-bottom: 1px solid var(--oui-stroke-2);
}

.oui-chat-head-text {
    flex: 1 1 auto;
    min-width: 0;
}

    .oui-chat-head-text h2 {
        margin: 0;
        font-size: 15px;
        font-weight: 600;
        color: var(--oui-fg);
    }

    /* Direct child only: the meta line holds a status pill, and a `display:
       block` inherited by that pill stretched it across the whole header. */
    .oui-chat-head-text > span {
        display: block;
        margin-top: 2px;
        font-size: 11px;
        color: var(--oui-fg-3);
    }

.oui-chat-progress {
    position: absolute;
    right: 0;
    bottom: -1px;
    left: 0;
    height: 3px;
    background: var(--oui-stroke-2);
}

    .oui-chat-progress span {
        display: block;
        width: 0;
        height: 100%;
        background: var(--oui-brand);
        transition: width .35s var(--oui-ease);
    }

.oui-chat-transcript {
    flex: 1 1 auto;
    min-height: 0;
    padding: 22px 24px;
    overflow-y: auto;
}

.oui-chat-bubble {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    margin-bottom: 14px;
}

.oui-chat-bubble--me {
    flex-direction: row-reverse;
}

.oui-chat-bubble-avatar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    font-size: 13px;
    color: #fff;
    background: var(--oui-brand);
    border-radius: 50%;
}

.oui-chat-bubble-body {
    max-width: 72%;
    padding: 10px 14px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--oui-fg);
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-lg) var(--oui-radius-lg) var(--oui-radius-lg) 2px;
    word-break: break-word;
}

.oui-chat-bubble--me .oui-chat-bubble-body {
    color: #fff;
    background: var(--oui-brand);
    border-color: var(--oui-brand);
    border-radius: var(--oui-radius-lg) var(--oui-radius-lg) 2px var(--oui-radius-lg);
}

.oui-chat-bubble-when {
    display: block;
    margin-top: 5px;
    font-size: 10px;
    opacity: .7;
}

.oui-chat-update,
.oui-chat-done {
    display: block;
    margin-bottom: 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--oui-brand);
}

.oui-chat-done {
    color: var(--oui-success);
}

.oui-chat-reward {
    display: inline-block;
    margin-top: 10px;
    padding: 3px 10px;
    font-size: 11px;
    font-weight: 600;
    color: var(--oui-success);
    background: var(--oui-success-tint);
    border-radius: 999px;
}

/* --- the composer -------------------------------------------------------- */

.oui-chat-composer {
    flex: 0 0 auto;
    padding: 12px 20px 16px;
    background: var(--oui-bg);
    border-top: 1px solid var(--oui-stroke-2);
}

.oui-chat-composer-choices {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

    .oui-chat-composer-choices:not(:empty) {
        margin-bottom: 12px;
    }

.oui-chat-choice {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    font-size: 13px;
    color: var(--oui-brand);
    background: var(--oui-bg);
    border: 1px solid var(--oui-brand-muted);
    border-radius: 999px;
    cursor: pointer;
    transition: background-color var(--oui-duration) var(--oui-ease);
}

    .oui-chat-choice:hover {
        background: var(--oui-brand-tint);
    }

.oui-chat-composer-row {
    display: flex;
    align-items: flex-end;
    gap: 8px;
}

.oui-chat-input {
    flex: 1 1 auto;
    min-height: 42px;
    max-height: 140px;
    padding: 10px 15px;
    font-size: 13px;
    line-height: 1.4;
    color: var(--oui-fg);
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
    outline: none;
    resize: vertical;
}

    .oui-chat-input:focus {
        border-bottom: 2px solid var(--oui-brand);
    }

    .oui-chat-input:disabled {
        color: var(--oui-fg-disabled);
        background: var(--oui-bg-canvas);
    }

.oui-chat-attach {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    margin: 0;
    color: var(--oui-fg-2);
    background: var(--oui-bg);
    border: 1px solid var(--oui-stroke);
    border-radius: var(--oui-radius-sm);
    cursor: pointer;
}

    .oui-chat-attach:hover {
        color: var(--oui-brand);
        background: var(--oui-bg-hover);
    }

    .oui-chat-attach input {
        display: none;
    }

.oui-chat-send {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    padding: 0;
}

    /* A step that is answered by picking a chip disables both of these; they
       have to read as unavailable rather than merely do nothing. */
    .oui-chat-send:disabled,
    .oui-chat-input:disabled ~ .oui-chat-attach {
        opacity: .45;
        cursor: default;
    }

.oui-chat-attach-name {
    margin-top: 6px;
    font-size: 11px;
    color: var(--oui-fg-2);
}

/* --- empty, loading and small screens ------------------------------------ */

.oui-chat-none,
.oui-chat-loading {
    padding: 34px 18px;
    font-size: 12px;
    text-align: center;
    color: var(--oui-fg-2);
}

    .oui-chat-none i {
        font-size: 22px;
        color: var(--oui-brand-muted);
    }

    .oui-chat-none p {
        margin: 10px 0 2px;
        font-size: 14px;
        font-weight: 600;
        color: var(--oui-fg);
    }

    .oui-chat-none span {
        display: block;
        max-width: 240px;
        margin: 0 auto;
        font-size: 12px;
    }

.oui-chat-spinner {
    display: inline-block;
    width: 13px;
    height: 13px;
    vertical-align: -2px;
    border: 2px solid var(--oui-stroke);
    border-top-color: var(--oui-brand);
    border-radius: 50%;
    animation: oui-qs-spin .7s linear infinite;
}

@media (max-width: 991px) {
    .oui-chat {
        height: auto;
        flex-direction: column;
    }

    .oui-chat-rail {
        flex: 0 0 auto;
        border-right: 0;
        border-bottom: 1px solid var(--oui-stroke-2);
    }

    .oui-chat-list {
        max-height: 300px;
    }

    .oui-chat-main {
        min-height: 460px;
    }

    .oui-chat-bubble-body {
        max-width: 88%;
    }
}
