/* Intentional product-level overrides on top of the UniFi-style UI Kit. */

/* Product typography: the kit's Inter stack (kit token, webfonts bundled). */
:root {
    --bs-body-font-family: var(--uf-font);
}
body {
    font-family: var(--uf-font);
    font-optical-sizing: auto;
    font-variation-settings: "GRAD" 0;
}

/* Authentication surface. */
.authentication-bg {
    background-color: var(--uf-auth-bg);
    background-image: linear-gradient(135deg, var(--uf-auth-bg) 0%, var(--uf-bg-subtle) 100%);
}
.authentication-bg .card {
    border: 0;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.06);
}

#workos-google-login-form {
    width: 100%;
}

/*
 * Google Identity control rendered with the portal's outline button
 * (btn btn-outline-secondary) so it shares the theme's typography, sizing,
 * radius, and interaction states with every other action on the page. Only
 * the official multi-color "G" mark keeps Google's brand colors; its sizing
 * and label spacing live here.
 */
.google-g {
    display: inline-flex;
    width: 1.125rem;
    height: 1.125rem;
    margin-right: 0.625rem;
}

.google-g svg {
    width: 100%;
    height: 100%;
    display: block;
}

.chart-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    flex: 0 0 2.5rem;
    border-radius: 0.75rem;
    font-size: 1.25rem;
}

.vmm-list-card__identity {
    min-width: 0;
}

/* Five compact-row concepts. The kit's stock list-group, card-body, badge,
   button, border, background, spacing, and typography utilities provide the
   shared visual language; only the layouts without kit equivalents live here. */
.device-list > .list-group-item {
    min-height: 5.5rem;
    /* List rows are standalone cards (they carry shadow-sm + rounded-3), so
       they need the card surface. `.list-group-item` defaults to
       `--bs-list-group-bg` (= `--bs-bg-body` = `--uf-bg`), which makes the
       rows read as one flat block against the page and drops the elevation
       the shadow implies. Use the same surface as `.card` so the group cards
       match the processing-overview and worker-status cards. `--bs-card-bg`
       is declared on `.card` itself, so the underlying token is used here. */
    background-color: var(--uf-surface);
}

/* Spaced-card lists (dashboard/groups `list-group gap-3`): Bootstrap zeroes
   the top border of every item after the first to merge flush list rows
   (`.list-group-item + .list-group-item { border-top-width: 0 }`). With
   `gap-3` the items are standalone cards, so each one needs all four
   borders — without this the 2nd+ card (e.g. dashboard "Test") loses its
   top edge. */
.device-list > .list-group-item + .list-group-item {
    border-top-width: var(--bs-list-group-border-width);
}

.list-metric-layout {
    display: grid;
    grid-template-columns: minmax(13rem, 1fr) auto auto;
    align-items: stretch;
}

.list-metric-identity {
    align-self: center;
    min-width: 0;
}

/* Group rows (dashboard + groups list): size to content instead of the
   generic 5.5rem device-list min-height, and inset the identity tile to the
   same 18px gutter the stat cards use so the icons line up. */
.group-list > .list-group-item {
    min-height: 0;
}

.group-list .list-metric-identity {
    padding-left: 1.125rem;
}

.list-metric-strip {
    display: grid;
    grid-template-columns: repeat(3, minmax(6rem, 1fr));
}

.list-metric-strip > div {
    display: flex;
    flex-direction: column-reverse;
    justify-content: center;
    min-width: 6rem;
    padding: 0.85rem 1rem;
    text-align: center;
}

.list-metric-strip dt {
    color: var(--uf-text-2);
    font-size: 0.6875rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.list-metric-strip dd {
    margin: 0 0 0.15rem;
    font-size: 1rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.list-metric-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

@media (max-width: 767.98px) {
    .list-metric-layout {
        grid-template-columns: 1fr auto;
    }

    .list-metric-strip {
        grid-column: 1 / -1;
        grid-row: 2;
        border-left: 0 !important;
        border-top: 1px solid var(--uf-border-light);
    }

    .list-metric-actions {
        grid-column: 2;
        grid-row: 1;
    }
}

@media (max-width: 575.98px) {
    .device-list .card-body {
        gap: 0.75rem !important;
    }

    .list-metric-strip {
        grid-template-columns: repeat(3, 1fr);
    }

    .list-metric-strip > div {
        min-width: 0;
        padding: 0.7rem 0.4rem;
    }

    .list-metric-strip dd {
        font-size: 0.875rem;
    }

}

/* List/grid view toggle (groups, dashboard groups, workers).
 *
 * List stays the default: the existing Design 3 metric-strip rows (groups)
 * and the workers table. Grid reuses the same kit pieces (card, soft
 * identity tile, metric strip, dots/badges, btn-default) in a responsive
 * Bootstrap row; the rules below only adapt the strip to a stacked card
 * and keep long names truncated inside grid columns. */
.group-grid-card .list-metric-strip {
    border-top: 1px solid var(--uf-border-light);
}

.group-grid-card .list-metric-strip > div {
    min-width: 0;
    padding: 0.7rem 0.5rem;
}

.group-grid-card .vmm-list-card__identity,
.worker-grid-card .worker-grid-identity {
    min-width: 0;
}

@media (max-width: 575.98px) {
    .group-grid-card .list-metric-strip dd {
        font-size: 0.875rem;
    }
}

/*
 * Restore the `hidden` attribute against Bootstrap display utilities
 * (portal bug found in the first real browser pass, 10 September 2026).
 *
 * Some themes define `[hidden] { display: none !important }` in their reboot
 * section, and every display utility (`.d-flex` … ) later in the stylesheet
 * with the same !important tier wins by source order — so any element
 * combining `hidden` with a `d-*` utility would stay visible. This file loads
 * after the theme, so this rule restores the attribute everywhere. Known affected element: the job page's late-result banner
 * (#job-result-late), which was permanently visible.
 */
[hidden] {
    display: none !important;
}

/*
 * Worker allocation picker (group detail "Manage workers" modal).
 * Locked rows — the worker belongs to another process group or is
 * reserved by an active attempt — mute only the NAME; the reason tag
 * ("Assigned to <group>" / "Busy") renders at full contrast so the
 * blocker reads at a glance. The narrow leading column hosts the tick
 * boxes.
 */
.worker-picker-col-select {
    width: 2.75rem;
}

.worker-picker-col-select .check {
    margin: 0 auto;
    display: block;
}

.worker-picker-row-locked .worker-picker-name {
    color: var(--bs-fg-2, #707070);
}

.worker-picker-tag {
    font-weight: 500;
    white-space: nowrap;
}

#group-workers-error {
    white-space: pre-wrap;
}

/*
 * Group detail filter bars (queue + Results cards). The markup uses the
 * Bootstrap grid (`row g-3`: search `col-12`, Status/Type `col-6` + `col-6`,
 * date/time `col-12` on phones), so the mobile layout comes from the grid
 * alone. These fixed widths apply from md up so both cards keep defined,
 * identical, coherent sizes on tablets/desktops instead of stretching with
 * the grid or depending on the native time-input width.
 */
@media (min-width: 768px) {
    .vpp-f-search {
        width: 18rem;
        max-width: 100%;
    }

    .vpp-f-select {
        width: 11rem;
    }

    /* Date range + start/end time group (date col-6, times col-3 each):
     * one block that sits on the same row as Search/Status/Type. */
    .vpp-f-daterange {
        width: 36rem;
        max-width: 100%;
    }
}

/* Inside the date/time group the time inputs fill their grid column
 * instead of the fixed 7rem used by the older stacked layout. */
.vpp-f-daterange .vpp-filter-time-input {
    width: 100%;
}

/* Range input + its two time inputs stack: the range spans full width,
 * the times share one row below (each flexible, same 7rem basis as the
 * former single-date rows). */
.vpp-filter-range-input {
    width: 100%;
    min-width: 0;
}

.vpp-filter-time-input {
    flex: 0 1 auto;
    width: 7rem;
    min-width: 0;
}

/*
 * Single-row date range + start/end time (used by every filter bar:
 * job Lifecycle, worker history/command/telemetry, group processed,
 * audit, API logs). Date flexes, times keep compact fixed widths so
 * all three stay on ONE line at every breakpoint, phones included.
 * Each field stacks its label above its input.
 */
.vpp-dt-row {
    display: flex;
    gap: 0.5rem;
    flex-wrap: nowrap;
    align-items: flex-start;
}

.vpp-dt-date {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.vpp-dt-time {
    flex: 0 0 6.5rem;
    width: 6.5rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.vpp-dt-row input {
    width: 100%;
    min-width: 0;
}

.vpp-dt-date input {
    text-overflow: ellipsis;
}

@media (max-width: 767.98px) {
    .vpp-dt-time {
        flex-basis: 5.5rem;
        width: 5.5rem;
    }
}

/*
 * Group detail header (process-group top buttons): the shell uses the kit
 * `.uf-page-head`, whose xs breakpoint already stacks `.ms-auto` full-width
 * below the title. The actions container wraps so "Manage access / Manage
 * workers / Edit" never overflow horizontally; on phones the buttons share
 * the row equally (two per row, wrapping) instead of being cut off.
 */
#group-detail-page #group-detail-head {
    flex-wrap: wrap;
    align-items: flex-start;
    /* Subtitle row (Group ID) sits directly under the title:
     * keep only a 4px rhythm gap instead of the kit's 16px. */
    margin-bottom: 4px;
}

#group-detail-page #group-detail-head > div:first-child {
    min-width: 0;
    flex: 1 1 0;
}

#group-detail-page #group-detail-head .group-actions {
    flex-shrink: 0;
}

#group-detail-page #group-detail-head .group-actions .btn {
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    #group-detail-page #group-detail-head .group-actions {
        width: 100%;
        margin-left: 0 !important;
    }
    #group-detail-page #group-detail-head .group-actions .btn {
        flex: 1 1 140px;
        justify-content: center;
    }
}

/* Group ID row: the UUID must break anywhere so it never
 * overflows on narrow phones. */
#group-detail-page #group-id-row {
    min-width: 0;
}

#group-detail-page #group-id-row code {
    min-width: 0;
    overflow-wrap: anywhere;
}

/*
 * Group detail Error column (queue + processed tables): inline code plus a
 * clamped message so the Failed badge always ships with a visible reason.
 * The column keeps a bounded width so long backend messages wrap/clamp
 * instead of stretching the table; the full text stays in the cell's
 * title/aria-label and on the job-detail page.
 */
.vpp-error-cell {
    max-width: 22rem;
    min-width: 10rem;
}

.vpp-error-cell .min-w-0 {
    min-width: 0;
}

.vpp-error-msg {
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/*
 * File-name cells (group queue + processed tables, worker job history,
 * storage file table, job artifacts table): bound the width so
 * long file names (e.g. 100+ char camera exports) truncate with an
 * ellipsis instead of stretching the table and breaking the layout.
 * A common name like AKNV_S07_20260909_090000-090100.mp4 (~35 chars)
 * still fits without truncation on desktop; longer names clamp and the
 * full text stays in the element's title tooltip. The client-reference
 * subline truncates the same way.
 */
.vpp-file-cell {
    max-width: 26rem;
    min-width: 10rem;
}

.vpp-file-link,
.vpp-file-ref {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

.vpp-file-ref {
    display: block;
}

@media (max-width: 575.98px) {
    .vpp-file-cell {
        max-width: 12rem;
        min-width: 0;
    }
}

/*
 * Audit log table: fixed layout with defined column widths so narrow
 * columns (timestamp, user, action, resource type) always fit their
 * content on one line while the smaller resource-id truncates and the
 * content (JSON) column takes the remainder. Timestamp/user/ids/action
 * stay on one line with ellipsis; content wraps anywhere instead of
 * stretching.
 */
.audit-table {
    table-layout: fixed;
    width: 100%;
    min-width: 80rem;
}

.audit-col-time { width: 15rem; }
.audit-col-user { width: 14rem; }
.audit-col-action { width: 16rem; }
.audit-col-restype { width: 11rem; }
.audit-col-resid { width: 7rem; }
.audit-col-content { width: auto; }

.audit-table th,
.audit-table td {
    vertical-align: middle;
}

.audit-cell-nowrap {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

.audit-cell-nowrap .uf-badge {
    display: inline-block;
    vertical-align: middle;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.audit-cell-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.audit-cell-time {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.audit-cell-time .audit-time-absolute {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.audit-cell-content {
    min-width: 0;
}

.audit-cell-content code {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
    text-overflow: ellipsis;
    overflow-wrap: anywhere;
    word-break: break-word;
    white-space: pre-wrap;
    font-size: 0.75rem;
}

/* Clickable audit rows open the event-details modal. */
.audit-row {
    cursor: pointer;
}

.audit-row:hover {
    background-color: var(--bs-bg-1);
}

.audit-row:focus-visible {
    outline: 2px solid var(--bs-primary-bg);
    outline-offset: -2px;
}

.audit-detail-content {
    max-height: 24rem;
    overflow: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: 0.8rem;
}

/*
 * Stream detection table: fixed layout with defined column widths so the
 * Artifacts column stays a minimal icon-only gutter stuck to the right —
 * Time keeps one line for the wall-clock timestamp, Names takes the
 * remainder. The file name lives in the button title/aria-label and in
 * the preview modal title.
 */
.stream-events-table {
    table-layout: fixed;
    width: 100%;
}

.stream-col-time { width: 12rem; }
.stream-col-names { width: auto; }
.stream-col-artifacts { width: 3.25rem; }

.stream-events-table th:last-child,
.stream-events-table td:last-child {
    text-align: right;
}

.stream-artifact-btn {
    padding-left: 0.4rem;
    padding-right: 0.4rem;
    vertical-align: middle;
}

.stream-artifact-btn + .stream-artifact-btn {
    margin-left: 0.25rem;
}

/* Markdown docs (api-docs, worker-docs) on the Bootstrap 6 .prose class.
 *
 * The references are authored as Markdown, rendered client-side, and styled
 * by the kit's theme-aware .prose rules — headings, lists, tables,
 * blockquotes and links need no per-element classes and follow the portal
 * light/dark theme (this replaces the retired always-light
 * .docs-light-scope isolation plus the third-party github-markdown theme).
 * The rules below only fill the gaps reboot leaves: code fences get a
 * subtle theme-aware surface so they read as blocks, and long lines scroll
 * instead of stretching the card. Tables keep their classless markup (prose
 * styles table:not([class])); the page scripts wrap them in
 * .table-responsive for narrow screens. */
.prose pre {
    padding: 1rem 1.25rem;
    border: 1px solid var(--uf-border);
    border-radius: 0.5rem;
    background-color: var(--uf-bg-subtle);
}

/* Compact soft toasts (see static/js/toast.js). The header tint comes from
   the kit's .toast-success / .toast-danger / … soft rules in unifi-ui.css;
   here the toast is only made smaller and calmer: narrower than Bootstrap's
   350px default, smaller type, tighter header padding and a soft shadow so
   it sits quietly in the bottom-right corner in both themes. */
.toast-compact {
    width: auto;
    max-width: 20rem;
    font-size: 0.8125rem;
    border-radius: 0.625rem;
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.12);
}
.toast-compact .toast-header {
    padding: 0.45rem 0.7rem;
    font-size: 0.75rem;
    font-weight: 600;
}
.toast-compact .toast-header .fs-lg {
    font-size: 1rem;
}
.toast-compact .toast-body {
    padding: 0.55rem 0.7rem;
}

/* Toast container in the top layer (see static/js/toast.js): the container
   is a manual popover so toasts paint above native <dialog> overlays
   (Dialog/Drawer via showModal) and their ::backdrops, which ignore z-index.
   The UA popover stylesheet centers with inset:0/margin:auto and adds a
   bordered box — reset it so the Bootstrap position utilities (bottom-0 /
   top-0 + end-0) keep pinning the corner, and never show a backdrop.
   WORKAROUND — retire with the toast.js popover promotion once the kit
   top-layers toasts itself; see the comment there. */
#toast-container[popover] {
    margin: 0;
    border: 0;
    background: transparent;
    overflow: visible;
}
/* UA popover styles center with inset:0/margin:auto. Pin the corner
   explicitly for each placement: unlayered author rules beat the layered
   position utilities (.bottom-0/.top-0/.end-0), so every side is set here
   instead of relying on those utilities in the popover case. (Without
   Popover API support toast.js drops the attribute and the utilities apply
   normally.) */
#toast-container[popover].bottom-0 {
    top: auto;
    bottom: 0;
    left: auto;
    right: 0;
}
#toast-container[popover].top-0 {
    top: 0;
    bottom: auto;
    left: auto;
    right: 0;
}
#toast-container::backdrop {
    display: none;
    background: transparent;
}

/*
 * Job detail page head on narrow screens: the title can be a long
 * unbroken stream URL or file path, and streams carry up to seven
 * action buttons — without wrapping, both push the page sideways.
 * The title block may shrink (min-width: 0) and breaks anywhere;
 * the action row wraps between buttons, each keeping icon + label
 * on one line. No-op on wide screens (everything fits one line).
 */
#job-detail-page .uf-page-head > div:first-child {
    min-width: 0;
    /* Zero basis (not content-sized): the file name must never squeeze the
     * action buttons off the row. The title takes whatever is left after
     * the buttons' natural width and clamps to three lines; the buttons
     * stay top-right (tablet/desktop). Phones stack via the query below. */
    flex: 1 1 0;
}
#job-detail-page #job-detail-heading {
    min-width: 0;
    overflow-wrap: anywhere;
    /* Long file names clamp to three lines with an ellipsis instead of
     * pushing the action buttons or stretching the page head; the full
     * name stays in the heading's title tooltip and in the Details row.
     * Doubled ID wins over the kit's `.uf-page-title` display rule, and
     * the legacy box model is required: `line-clamp` alone does not
     * engage in this browser (verified: block/flow-root render all
     * lines, -webkit-box clamps to 3). */
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    line-clamp: 3;
}
/* Details "File" value: keep the full name but break long unbroken
 * strings anywhere so the facts column never overflows. The !important
 * wins over Bootstrap's `.text-break` (which sets break-word with
 * !important and would otherwise keep mid-extension splits). */
#job-meta-file {
    overflow-wrap: anywhere !important;
}
/* Delete/terminate confirmations quote the file name in the modal body:
 * break long unbroken names anywhere so the modal never overflows. */
#job-confirm-message {
    overflow-wrap: anywhere;
}
#job-detail-page .uf-page-head {
    flex-wrap: wrap;
    /* Buttons pin to the top row (first title line), never vertically
     * centered against a multi-line file name. */
    align-items: flex-start;
}
/* Tablet/desktop: never wrap the buttons below the title — the title
 * above shrinks (zero-basis flex) while the actions keep their natural
 * size at the top right. */
@media (min-width: 768px) {
    #job-detail-page .uf-page-head {
        flex-wrap: nowrap;
    }
}
#job-detail-page .uf-page-head .ms-auto {
    flex-wrap: wrap;
    flex-shrink: 0;
}
@media (max-width: 767.98px) {
    #job-detail-page .uf-page-head > div:first-child {
        max-width: 100%;
        flex-basis: 100%;
    }
}
#job-detail-page .uf-page-head .ms-auto .btn {
    white-space: nowrap;
}

/*
 * Tables on narrow phones: cell content (file names, URLs, IDs) must not
 * wrap — wrapped fragments like "det / ect / ion / s.j / so / n" are
 * unreadable. Every data table already lives in a horizontally scrollable
 * `.table-responsive` wrapper, so keep cells on one line and let wide tables
 * scroll instead. No-op on wider screens.
 */
@media (max-width: 575.98px) {
    .table-responsive > .table th,
    .table-responsive > .table td {
        white-space: nowrap;
    }
}

/*
 * Dialogs whose header/body/footer are wrapped in a <form> (worker, group
 * and other create/edit modals need the footer submit inside the form):
 * the Bootstrap dialog is a flex column that expects .dialog-header /
 * .dialog-body / .dialog-footer as DIRECT children (see the
 * dialog-scrollable reference markup). The intermediate <form> is the
 * actual flex item, so without this the body never gets a height
 * constraint — .dialog-scrollable's overflow-y:auto has nothing to
 * scroll, tall content spills past the rounded border and the footer is
 * pushed out of view. Make the form a pass-through flex column instead:
 * header/footer keep their shrink-0 behavior, the body shrinks (its
 * non-visible overflow already zeroes the automatic minimum) and scrolls.
 */
.dialog > form {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    max-height: 100%;
}

/*
 * Modal footers on narrow phones: footers with a leading destructive action
 * (Delete worker / Delete group + Cancel + Save) exceed the ~310px content
 * width at 360px, so the primary button wraps onto a second row and lands
 * centered, detached from Cancel. Stack the footer vertically on xs screens
 * so every action is full-width in DOM order (destructive first, primary
 * last) with a comfortable touch target. No-op on wider screens.
 */
@media (max-width: 575.98px) {
    .dialog-footer {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
    }
    .dialog-footer > * {
        margin: 0;
    }
    .dialog-footer .me-auto {
        margin-right: 0 !important;
        width: 100%;
    }
    .dialog-footer .me-auto > span {
        display: flex;
        width: 100%;
    }
    .dialog-footer .btn {
        width: 100%;
    }
}

/*
 * Job header action row on phones: the title takes the full row and the
 * actions get their own full row below it, sharing the width equally (like
 * four col-3: star, pin, delete, share) instead of keeping their minimized
 * square look. Extra stream controls wrap onto further equal-width rows.
 * Tablet and up keep the desktop look (buttons top-right, title shrinks).
 */
@media (max-width: 767.98px) {
    #job-detail-page .uf-page-head .ms-auto.job-actions {
        width: 100%;
        margin-left: 0 !important;
    }
    #job-detail-page .uf-page-head .ms-auto.job-actions .btn {
        flex: 1 1 0;
        width: auto;
        max-width: none;
        min-width: fit-content;
        justify-content: center;
    }
}

/* Offline-license status banner (licensed builds): the first in-flow element
   takes up its normal space on arrival, then remains reachable at the
   viewport edge after the user scrolls past it. Signal red with white text
   and action link. The icon rail is fixed-positioned (kit), so while the
   banner is present the rail — and the sticky filter panels hanging off the
   top bar — are offset below it by the measured banner height (set on
   :root by the inline script in base.html; 0 when the banner is absent). */
#license-status-banner {
    --bs-alert-color: #ffffff;
    --bs-alert-bg: #c62828;
    --bs-alert-border-color: #c62828;
    --bs-alert-link-color: #ffffff;
    position: sticky;
    top: 0;
    z-index: 1040;
    background-color: #c62828;
    color: #ffffff;
}

body.has-license-banner .uf-topbar {
    top: var(--license-banner-h, 0px);
}

body.has-license-banner .uf-rail {
    top: calc(var(--uf-topbar-h) + var(--license-banner-h, 0px));
}

body.has-license-banner .uf-panel {
    top: calc(var(--uf-topbar-h) + var(--license-banner-h, 0px) + 16px);
    max-height: calc(100vh - var(--uf-topbar-h) - var(--license-banner-h, 0px) - 32px);
}

#license-status-banner .alert-link,
#license-status-banner .alert-link:hover {
    color: #ffffff;
    text-decoration: underline;
}

/* Offline-license lock shell (licensed builds): locked non-root users get a
   full-page shell — base.html skips the icon rail markup, so the page area
   drops the rail offset here (mirrors the kit's below-desktop-breakpoint
   rule). Navigation survives in the top-bar user menu and the mobile
   drawer; sidebar.js is a no-op without its toggle button. */
body.license-locked .uf-shell {
    margin-left: 0;
}

/*
 * Worker detail fact list: identifiers are shown in full (never truncated
 * with an ellipsis) and wrap anywhere so long UUIDs/URLs stay inside the
 * column on narrow phones. Generous row spacing + relaxed line-height keep
 * the dense mono values readable.
 */
.vpp-id-full,
#worker-hero-job.vpp-chip-wrap {
    overflow-wrap: anywhere;
    word-break: break-word;
}
#worker-hero-job.vpp-chip-wrap {
    max-width: 100%;
    white-space: normal;
    text-align: left;
}
#worker-facts,
#worker-provision {
    row-gap: 0.65rem;
}
#worker-facts > dt,
#worker-facts > dd,
#worker-provision > dt,
#worker-provision > dd {
    margin-bottom: 0;
    line-height: 1.6;
}
#worker-signals-attempt {
    margin-top: 0.15rem;
}

/*
 * Worker detail copy rows: the value wraps (min-width: 0) while the copy
 * button stays a compact inline action next to the first text line.
 * The button itself is the kit's `.btn-icon btn-xs` (24px square, see
 * portal-theme-override.css) so it matches the fact-list line box and
 * does not stand taller than the text; the old custom `.vpp-copy-inline`
 * box (and the earlier `.btn-icon` at a fixed 2.25rem) both read as
 * misaligned against the mono value.
 */
.vpp-copy-row {
    display: flex;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 0.5rem;
    min-width: 0;
}
.vpp-copy-row .vpp-copy-text {
    flex: 0 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* Group settings use the Application Config subnav within a scrollable
   dialog; the navigation and Save/Cancel footer stay reachable. */
.dialog.group-settings-dialog {
    width: min(1080px, calc(100vw - 2rem));
    max-width: min(1080px, calc(100vw - 2rem));
}
.group-settings-dialog .dialog-body.group-settings-layout {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    padding: 0;
    overflow: hidden;
    min-height: 0;
}
.group-settings-dialog .group-settings-sidebar {
    width: auto;
    min-width: 0;
    padding: 16px;
    border-radius: 0;
    border-right: 1px solid var(--uf-border, var(--bs-border-color));
    overflow-y: auto;
}
.group-settings-content {
    min-width: 0;
    min-height: 0;
    overflow-y: auto;
    padding: 24px;
    overscroll-behavior: contain;
}
.group-settings-section + .group-settings-section {
    padding-top: 24px;
    margin-top: 24px;
    border-top: 1px solid var(--uf-border, var(--bs-border-color));
}

/* CodeMirror 5 JSON editors (PortalUtils.enhanceJsonEditor): the library's
   own stylesheets own the token colors — this only bridges box metrics and
   the kit border to the editor frame. */
.CodeMirror {
    height: auto;
    min-width: 0;
    max-width: 100%;
    border: 1px solid var(--bs-border-color, #e5e7eb);
    border-radius: var(--bs-border-radius, 0.5rem);
    font-size: 0.8125rem;
    line-height: 1.5;
}
.CodeMirror-scroll { min-height: 10rem; max-height: 28rem; }
.CodeMirror-focused { border-color: var(--bs-primary, #0d6efd); }
.group-settings-nav .uf-subnav-item { white-space: normal; }
.group-settings-mobile-nav { display: none; }
.group-settings-dialog .uf-choice-row { flex-wrap: wrap; }
.group-settings-dialog .uf-choice { flex: 1 1 150px; min-width: 0; }
@media (max-width: 767.98px) {
    .group-settings-dialog .dialog-body.group-settings-layout {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr);
    }
    .group-settings-dialog .group-settings-sidebar { border-right: 0; padding: 12px 16px; }
    .group-settings-nav { display: none; }
    .group-settings-mobile-nav { display: block; }
    .group-settings-content { padding: 16px; }
}
