html, body {
    margin: 0;
    padding: 0;
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-md);
    line-height: var(--bz-lh);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

*, *::before, *::after { box-sizing: border-box; }

a {
    text-decoration: none;
}

a:hover { text-decoration: underline; }

button {
    font-family: inherit;
    font-size: inherit;
    color: inherit;
}

/* Consistent keyboard focus ring across all interactive elements. */
:focus-visible {
    outline: 2px solid var(--bz-brand-primary);
    outline-offset: 2px;
    border-radius: var(--bz-radius-sm);
}

/* Subtle scrollbars. WebKit/Chromium/Edge get a custom track+thumb;
   Firefox falls back to its native thin scrollbar via scrollbar-width.
   Other engines keep their platform default — acceptable progressive
   enhancement, no JS-based scrollbar polyfill needed. */
html { scrollbar-width: thin; scrollbar-color: var(--bz-color-border) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--bz-color-border);
    border-radius: var(--bz-radius-pill);
    border: 2px solid var(--bz-color-bg);
}
::-webkit-scrollbar-thumb:hover { background: var(--bz-color-border-strong); }

/* --- Auth pages (shared between /login and /host/login) ------------------
   Global rather than scoped because both pages render a matching split
   layout and share the branded background image. Prefixed with `auth-`
   so they never collide with component-scoped classes. */
.auth-page {
    min-height: 100vh;
    width: 100%;
}

/* Split-panel branded background. The image is a CSS custom property so a resolved tenant can
   override it per-session (Login.razor sets --bz-login-image / --bz-login-image-dark inline) without
   a reload; unset it falls back to the product default illustration. A subtle top-to-bottom scrim is
   layered above the image (first background layer) so the panel's heading/hero text stays readable
   over any artwork. */
.auth-branded-bg {
    background-image:
        linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.28) 55%, rgba(255, 255, 255, 0.55) 100%),
        var(--bz-login-image, url('/branding/login-default.svg'));
}

html.dark .auth-branded-bg {
    background-image:
        linear-gradient(180deg, rgba(15, 23, 42, 0.1) 0%, rgba(15, 23, 42, 0.45) 55%, rgba(15, 23, 42, 0.7) 100%),
        var(--bz-login-image-dark, var(--bz-login-image, url('/branding/login-default-dark.svg')));
}

/* Frosted "glass" panel behind the hero text. The scrim above only darkens the bottom of the panel,
   but the hero text sits at the TOP — so over a poorly-contrasting uploaded image it can wash out.
   This translucent, backdrop-blurred card sits directly behind the text and guarantees legibility
   over ANY artwork: a light card (dark text) in light mode, a dark card (light text) in dark mode.
   Hugs its content (align-self:start) so it reads as a card, not a full-width band. */
.auth-hero-content {
    align-self: flex-start;
    max-inline-size: 34rem;
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-4);
    padding: var(--bz-space-5) var(--bz-space-6);
    border-radius: var(--bz-radius-lg, 14px);
    background: rgba(255, 255, 255, 0.6);
    -webkit-backdrop-filter: blur(12px) saturate(130%);
    backdrop-filter: blur(12px) saturate(130%);
    border: 1px solid rgba(255, 255, 255, 0.45);
    box-shadow: 0 10px 34px rgba(15, 23, 42, 0.14);
    text-shadow: 0 1px 2px rgba(255, 255, 255, 0.5);
}

html.dark .auth-hero-content {
    background: rgba(15, 23, 42, 0.5);
    border-color: rgba(255, 255, 255, 0.12);
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.5);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
}

/* Browsers without backdrop-filter (rare) fall back to a more opaque panel so text stays readable. */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .auth-hero-content { background: rgba(255, 255, 255, 0.85); }
    html.dark .auth-hero-content { background: rgba(15, 23, 42, 0.8); }
}

/* Theme-aware brand logo pair. When a tenant has both a light and dark logo, the shell renders both
   <img>s and CSS shows the one matching the active theme. Default (no theme class yet) = light. */
.bz-dark-only { display: none; }
html.dark .bz-light-only { display: none; }
html.dark .bz-dark-only { display: revert; }

/* ── Browser autofill — preserve dark mode colours ────────────────────
   Chromium/Safari forcibly paint a light-yellow/white overlay on
   autofilled inputs via :-webkit-autofill.  The only reliable cross-
   browser override is box-shadow inset (background-color is ignored
   by the UA stylesheet).  We re-apply Metronic's --card / --foreground
   tokens so the input matches the surrounding card in both modes. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
    -webkit-box-shadow: 0 0 0 1000px var(--card, #fff) inset !important;
    -webkit-text-fill-color: var(--foreground, #09090b) !important;
    caret-color: var(--foreground, #09090b);
    transition: background-color 0s 600000s; /* delay so Chrome never flashes */
}

#sidebar_menu {
    flex-shrink: 1;
    min-height: 0;
    overflow: hidden;
}

#sidebar_menu > .kt-scrollable-y-auto {
    max-height: 100%;
    min-height: 0;
    overflow-y: auto;
}

/* ── DevExpress Blazor — global grid + button overrides ──────────────
   Applied here (global) so every DxGrid instance across all modules
   shares the same visual language, including raw DxGrid usage in
   HostAdmin / TenantAdmin pages that don't go through ProjectionGrid.
   We map BizsOn design tokens to DevExpress CSS variables and add
   a few structural fixes for filling available width. */

/* Every DxGrid fills its parent container horizontally */
.dxbl-grid {
    width: 100%;
}

/* Map BizsOn design tokens to DevExpress Fluent grid CSS variables */
.dxbl-grid {
    --dxbl-grid-bg:                var(--bz-color-surface);
    --dxbl-grid-color:             var(--bz-color-fg);
    --dxbl-grid-border-color:      var(--bz-color-border);
    --dxbl-grid-header-bg:         var(--bz-color-surface-2);
    --dxbl-grid-header-color:      var(--bz-color-fg);
    --dxbl-grid-header-border-color: var(--bz-color-border);
    --dxbl-grid-row-hover-bg:      var(--bz-color-surface-3);
    --dxbl-grid-footer-bg:         var(--bz-color-surface-2);
    --dxbl-grid-pager-bg:          var(--bz-color-surface-2);
    --dxbl-grid-filter-row-bg:     var(--bz-color-surface);
    --dxbl-grid-group-bg:          var(--bz-color-surface-2);
    --dxbl-grid-font-size:         var(--bz-fs-md);
    --dxbl-grid-font-family:       var(--bz-font-sans);
}

/* Command cells (data rows only): row of small action buttons, vertically
   centred. Scoped to td — the command-column HEADER cell must stay a normal
   table cell so it matches the height/alignment of every other header cell. */
.dxbl-grid td.dxbl-grid-command-cell {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--bz-space-2);
    padding: 0.25rem 0.5rem !important;
    flex-wrap: wrap;
}

/* Command-column header: same metrics as other headers; centre any residual
   built-in header button (grids that intentionally keep the new-row button) */
.dxbl-grid th.dxbl-grid-command-cell {
    vertical-align: middle;
    text-align: center;
}
.dxbl-grid th.dxbl-grid-command-cell .dxbl-btn {
    width: 28px;
    height: 28px;
    min-width: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--bz-radius-sm);
}

/* Make Link-style DxButtons inside command cells look like visible
   ghost actions rather than plain hyperlinks */
.dxbl-grid-command-cell .dxbl-btn-link,
.dxbl-grid-command-cell .dxbl-btn-link:visited {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.2rem 0.55rem;
    border-radius: var(--bz-radius-sm);
    border: 1px solid var(--bz-color-border);
    background: var(--bz-color-surface);
    color: var(--bz-color-fg) !important;
    text-decoration: none !important;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    line-height: 1.4;
    cursor: pointer;
    white-space: nowrap;
    /* transform repeated here (and in the two rules below): these selectors outrank
       the shared press-feedback rule, so omitting it would silently drop the press. */
    transition: background var(--bz-duration-fast) var(--bz-ease),
                border-color var(--bz-duration-fast) var(--bz-ease),
                transform var(--bz-duration-press) var(--bz-ease);
}

.dxbl-grid-command-cell .dxbl-btn-link:hover {
    background: var(--bz-color-surface-3) !important;
    border-color: var(--bz-color-border-strong) !important;
    color: var(--bz-brand-primary) !important;
    text-decoration: none !important;
}

/* ══ Button system (task 285 — GitLab #113) ════════════════════════════════════
   One shared definition for every clickable control in the app. Buttons used to
   read as floating glyphs rather than controls: the DevExpress normalization was
   gated on .toolbar/.bz-toolbar, .bz-icon-action was a fully transparent ghost
   whose only affordance was :hover — which a phone does not have — and nothing
   anywhere enforced a finger-sized target.

   Three rules hold this section together:

   1. DevExpress metrics and colours are written as --dxbl-btn-* variables, never
      as property overrides. DevExpress paints .dxbl-btn from those variables, and
      every contextual override it ships (pager, calendar header, editor buttons,
      SizeMode.Small) sets the same variables at HIGHER specificity — so its own
      metrics survive untouched and not one !important is needed here.
   2. Every colour comes from a --bz-* token, so both themes follow automatically.
      Never add a prefers-color-scheme block to this section.
   3. Touch sizing lives in the single @media (pointer: coarse) block at the end.
      Never size or box a button per page. Coarse pointer, not a width breakpoint:
      a tablet in landscape is wide AND finger-driven. */

/* ── 1. DevExpress normalization, app-wide ────────────────────────────────── */
.dxbl-btn {
    --dxbl-btn-font-family: var(--bz-font-sans);
    --dxbl-btn-font-size: var(--bz-fs-sm);
    --dxbl-btn-font-weight: var(--bz-fw-medium);
    --dxbl-btn-line-height: 1.2;
    --dxbl-btn-border-radius: var(--bz-radius-sm);
    --dxbl-btn-padding-x: 0.8rem;
    --dxbl-btn-padding-y: 0.3rem;
    --dxbl-btn-image-spacing: var(--bz-space-2);
    /* DevExpress's own :focus-visible rule reads these three, so the ring matches
       the app's global outline. The retired forced box-shadow reset on the icon
       button removed the focus affordance altogether. */
    --dxbl-btn-focus-outline-color: var(--bz-brand-primary);
    --dxbl-btn-focus-outline-size: 2px;
    --dxbl-btn-focus-outline-offset: 2px;
    min-height: var(--bz-control-height);
    cursor: pointer;
}

/* DevExpress composites size their own embedded buttons — a combo box's drop-down
   arrow, a spin edit's steppers, pager pages, the calendar's month header. Those
   are widget internals, not app actions: the app-wide height floor above would
   stretch the control around them. */
.dxbl-text-edit .dxbl-btn,
.dxbl-pager .dxbl-btn,
.dxbl-calendar .dxbl-btn,
.dxbl-menu .dxbl-btn,
.dxbl-treeview .dxbl-btn,
.dxbl-pivot-table .dxbl-btn {
    min-height: 0;
}

.dxbl-btn:disabled,
.dxbl-btn.dxbl-disabled {
    opacity: .5;
    cursor: not-allowed;
}

/* ── Press feedback ─────────────────────────────────────────────────
   Every pressable surface confirms it heard the click. transform only, so the
   whole thing stays on the GPU, and :not(disabled) so dead controls stay dead.
   scale() also scales the label and icon inside — that is the point: the whole
   button compresses as one object rather than the border moving alone. */
:is(.dxbl-btn, .bz-icon-action, .kt-btn, .bz-chip-x,
    .bz-avatar-action, .bz-toast-close, .bz-tabs a) {
    transition: transform var(--bz-duration-press) var(--bz-ease);
}

:is(.dxbl-btn, .bz-icon-action, .kt-btn, .bz-chip-x,
    .bz-avatar-action, .bz-toast-close, .bz-tabs a):active:not(:disabled):not(.dxbl-disabled) {
    transform: scale(0.97);
}

/* A form control paired with a trailing icon button (e.g. a field + its info/help button). */
.bz-inline-field {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
}

.bz-inline-field > :first-child {
    flex: 1 1 auto;
    min-width: 0;
}

/* A plain flex row of inline items (icon + label + badges + …) — unlike .bz-toolbar it carries no
   page-level padding/margin, so it stacks cleanly when repeated as list rows (stage/gate/document
   rows, list-item actions) instead of a toolbar's spacing between them. */
.bz-inline-row {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    flex-wrap: wrap;
}

/* ── 1b. Render-style colours from the app tokens ─────────────────────────────
   DevExpress ships only its light mode here (App.razor registers Fluent with
   modes/light.min.css regardless of the app theme), so a Secondary button stayed a
   white slab on a dark page. The grid already takes its colours from --bz-* tokens
   through --dxbl-grid-*; buttons now do the same through --dxbl-btn-*, so both
   themes follow the tokens and Primary follows the tenant's brand colour. */
.dxbl-btn.dxbl-btn-secondary {
    --dxbl-btn-bg: var(--bz-color-surface);
    --dxbl-btn-color: var(--bz-color-fg);
    --dxbl-btn-border-color: var(--bz-color-border);
    --dxbl-btn-hover-bg: var(--bz-color-surface-2);
    --dxbl-btn-hover-color: var(--bz-color-fg);
    --dxbl-btn-hover-border-color: var(--bz-color-border-strong);
    --dxbl-btn-active-bg: var(--bz-color-surface-3);
    --dxbl-btn-active-color: var(--bz-color-fg);
    --dxbl-btn-active-border-color: var(--bz-color-border-strong);
}

.dxbl-btn.dxbl-btn-primary {
    --dxbl-btn-bg: var(--bz-color-accent);
    --dxbl-btn-color: var(--bz-color-accent-fg);
    --dxbl-btn-border-color: var(--bz-color-accent);
    --dxbl-btn-hover-bg: var(--bz-color-accent-hover);
    --dxbl-btn-hover-color: var(--bz-color-accent-fg);
    --dxbl-btn-hover-border-color: var(--bz-color-accent-hover);
    --dxbl-btn-active-bg: var(--bz-color-accent-hover);
    --dxbl-btn-active-color: var(--bz-color-accent-fg);
    --dxbl-btn-active-border-color: var(--bz-color-accent-hover);
}

.dxbl-btn.dxbl-btn-danger {
    --dxbl-btn-bg: var(--bz-color-danger);
    --dxbl-btn-color: #fff;
    --dxbl-btn-border-color: var(--bz-color-danger);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-danger) 85%, black);
    --dxbl-btn-hover-color: #fff;
    --dxbl-btn-hover-border-color: color-mix(in srgb, var(--bz-color-danger) 85%, black);
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-danger) 75%, black);
    --dxbl-btn-active-color: #fff;
    --dxbl-btn-active-border-color: color-mix(in srgb, var(--bz-color-danger) 75%, black);
}

.dxbl-btn.dxbl-btn-warning {
    --dxbl-btn-bg: var(--bz-color-warn);
    --dxbl-btn-color: #fff;
    --dxbl-btn-border-color: var(--bz-color-warn);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-warn) 85%, black);
    --dxbl-btn-hover-color: #fff;
    --dxbl-btn-hover-border-color: color-mix(in srgb, var(--bz-color-warn) 85%, black);
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-warn) 75%, black);
    --dxbl-btn-active-color: #fff;
    --dxbl-btn-active-border-color: color-mix(in srgb, var(--bz-color-warn) 75%, black);
}

.dxbl-btn.dxbl-btn-success {
    --dxbl-btn-bg: var(--bz-color-ok);
    --dxbl-btn-color: #fff;
    --dxbl-btn-border-color: var(--bz-color-ok);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-ok) 85%, black);
    --dxbl-btn-hover-color: #fff;
    --dxbl-btn-hover-border-color: color-mix(in srgb, var(--bz-color-ok) 85%, black);
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-ok) 75%, black);
    --dxbl-btn-active-color: #fff;
    --dxbl-btn-active-border-color: color-mix(in srgb, var(--bz-color-ok) 75%, black);
}

.dxbl-btn.dxbl-btn-link {
    --dxbl-btn-bg: transparent;
    --dxbl-btn-color: var(--bz-color-accent);
    --dxbl-btn-border-color: transparent;
    --dxbl-btn-hover-bg: transparent;
    --dxbl-btn-hover-color: var(--bz-color-accent-hover);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: transparent;
    --dxbl-btn-active-color: var(--bz-color-accent-hover);
    --dxbl-btn-active-border-color: transparent;
}

/* Pressed state of a two-state action (Watch/Unwatch, …). On a phone the page-header
   icon-collapse clips the label, so "Watch" and "Unwatch" render the same bare eye —
   the state has to live on the chip itself (task 290, GitLab #128). Accent tint and
   border, the app's own "active" language (see .bz-board-pill--active); deliberately
   not red, which means danger here. Declared AFTER the render-style blocks above so it
   out-orders .dxbl-btn-secondary at equal specificity. */
.dxbl-btn.bz-toggle-on {
    --dxbl-btn-bg: color-mix(in srgb, var(--bz-color-accent) 12%, var(--bz-color-surface));
    --dxbl-btn-color: var(--bz-color-accent);
    --dxbl-btn-border-color: var(--bz-color-accent);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-accent) 18%, var(--bz-color-surface));
    --dxbl-btn-hover-color: var(--bz-color-accent);
    --dxbl-btn-hover-border-color: var(--bz-color-accent);
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-accent) 24%, var(--bz-color-surface));
    --dxbl-btn-active-color: var(--bz-color-accent);
    --dxbl-btn-active-border-color: var(--bz-color-accent);
}

/* ── 2. Icon-only action ─────────────────────────────────────────────────────────────────────────────────
   Shared by DxButton (CssClass="bz-icon-action"), plain <button> and <a>.

   Task 285 boxed every one of them. #113 round 2 says that is right for a
   STANDALONE action and wrong everywhere else: an icon sitting INLINE in a grid
   row, a toolbar, a navigation strip or an input adornment (the login password
   eye, the header bell) turns a page into a wall of little boxes.

   So the chip is context-bound now. The shared definition is lightweight — no
   border, transparent at rest, tinted on hover/focus, SAME size and same 44px
   touch target — and only `.bz-page-actions`, the page-header action row, paints
   it back into a chip, because that is the one place an icon action really does
   stand alone with nothing around it to give it an edge. */
.bz-icon-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--bz-control-height);
    min-width: var(--bz-control-height);
    height: var(--bz-control-height);
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--bz-radius-sm);
    background: transparent;
    color: var(--bz-color-fg-muted);
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
    transition: background var(--bz-duration-fast) var(--bz-ease),
                border-color var(--bz-duration-fast) var(--bz-ease),
                color var(--bz-duration-fast) var(--bz-ease),
                transform var(--bz-duration-press) var(--bz-ease);
}

.bz-icon-action i,
.bz-icon-action .dxbl-image {
    margin: 0;
    font-size: 1.05rem;
    line-height: 1;
}

/* DxButton flavour: rest/hover/active/disabled all ride DevExpress's own variables,
   so every state stays consistent with the rest button above without fighting its
   cascade — and the .bz-page-actions block below only has to re-point the same
   three variables to turn the chip back on. */
.dxbl-btn.bz-icon-action {
    --dxbl-btn-bg: transparent;
    --dxbl-btn-border-color: transparent;
    --dxbl-btn-hover-bg: var(--bz-color-surface-2);
    --dxbl-btn-hover-color: var(--bz-color-accent);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: var(--bz-color-surface-3);
    --dxbl-btn-active-color: var(--bz-color-accent);
    --dxbl-btn-active-border-color: transparent;
}

/* Semantic variants keep their colour at rest and tint the surface on interaction.
   Keyed off the DevExpress render-style class, so a Danger DxButton is a red chip. */
.dxbl-btn.bz-icon-action.dxbl-btn-danger {
    color: var(--bz-color-danger);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-danger) 12%, transparent);
    --dxbl-btn-hover-color: var(--bz-color-danger);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-danger) 20%, transparent);
    --dxbl-btn-active-color: var(--bz-color-danger);
    --dxbl-btn-active-border-color: transparent;
}

.dxbl-btn.bz-icon-action.dxbl-btn-warning {
    color: var(--bz-color-warn);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-warn) 12%, transparent);
    --dxbl-btn-hover-color: var(--bz-color-warn);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-warn) 20%, transparent);
    --dxbl-btn-active-color: var(--bz-color-warn);
    --dxbl-btn-active-border-color: transparent;
}

.dxbl-btn.bz-icon-action.dxbl-btn-primary {
    color: var(--bz-color-accent);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-accent) 12%, transparent);
    --dxbl-btn-hover-color: var(--bz-color-accent);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-accent) 20%, transparent);
    --dxbl-btn-active-color: var(--bz-color-accent);
    --dxbl-btn-active-border-color: transparent;
}

.dxbl-btn.bz-icon-action.dxbl-btn-success {
    color: var(--bz-color-ok);
    --dxbl-btn-hover-bg: color-mix(in srgb, var(--bz-color-ok) 12%, transparent);
    --dxbl-btn-hover-color: var(--bz-color-ok);
    --dxbl-btn-hover-border-color: transparent;
    --dxbl-btn-active-bg: color-mix(in srgb, var(--bz-color-ok) 20%, transparent);
    --dxbl-btn-active-color: var(--bz-color-ok);
    --dxbl-btn-active-border-color: transparent;
}

.bz-icon-action:disabled,
.bz-icon-action.dxbl-disabled {
    opacity: .45;
    cursor: not-allowed;
}

/* Plain <button>/<a> flavour (RecordDocuments.razor, PM Files.razor, …) — same
   lightweight action, without a DevExpress cascade to route through. */
button.bz-icon-action:hover:not(:disabled),
a.bz-icon-action:hover {
    background: var(--bz-color-surface-2);
    border-color: transparent;
    color: var(--bz-color-accent);
    text-decoration: none;
}

button.bz-icon-action.kt-btn-danger,
a.bz-icon-action.kt-btn-danger {
    color: var(--bz-color-danger);
}

button.bz-icon-action.kt-btn-danger:hover:not(:disabled),
a.bz-icon-action.kt-btn-danger:hover {
    background: color-mix(in srgb, var(--bz-color-danger) 12%, transparent);
    border-color: transparent;
    color: var(--bz-color-danger);
}

/* The one chip context: the page-header action row (PageHeader.razor's
   `.bz-page-actions`). These actions stand alone — nothing around them draws an
   edge, and on a phone the header row IS the page's control strip — so here the
   border and surface come back. Everything else inherits the lightweight rest
   state above. Never widen this selector to "any toolbar": that is #113 again. */
.bz-page-actions .bz-icon-action {
    border-color: var(--bz-color-border);
    background: var(--bz-color-surface);
}

.bz-page-actions .dxbl-btn.bz-icon-action {
    --dxbl-btn-bg: var(--bz-color-surface);
    --dxbl-btn-border-color: var(--bz-color-border);
    --dxbl-btn-hover-border-color: var(--bz-color-border-strong);
    --dxbl-btn-active-border-color: var(--bz-color-border-strong);
}

.bz-page-actions button.bz-icon-action:hover:not(:disabled),
.bz-page-actions a.bz-icon-action:hover {
    border-color: var(--bz-color-border-strong);
}

/* ── 3. Text actions ──────────────────────────────────────────────────────────
   On a desktop these stay links — that is the right weight for a secondary action
   next to a real button. On a touch device a bare word is not a target, so the
   coarse-pointer block below turns them into ghost buttons. `.bz-page-back` and
   the dashboard's own text actions carry the same treatment in their own
   stylesheets (PageHeader.razor.css, dashboard.css) because they are scoped. */
.dxbl-btn.dxbl-btn-link:hover,
.kt-btn-ghost:hover,
.kt-btn-dim:hover {
    text-decoration: underline;
}

/* ── 4. Metronic classes the theme never shipped ──────────────────────────────
   Profile.razor and the support-session banner in MainLayout.razor ask for
   kt-btn-light / kt-btn-success / kt-btn-danger; Metronic defines none of them, so
   those buttons fell back to the bare .kt-btn primary fill. Defining them here
   fixes every call site at once and needs no page edits. */
.kt-btn.kt-btn-light {
    background-color: var(--bz-color-surface-2);
    color: var(--bz-color-fg);
    border: 1px solid var(--bz-color-border);
}

.kt-btn.kt-btn-light:hover {
    background-color: var(--bz-color-surface-3);
    border-color: var(--bz-color-border-strong);
    color: var(--bz-color-fg);
}

.kt-btn.kt-btn-success {
    background-color: var(--bz-color-ok);
    color: #fff;
    border: 1px solid var(--bz-color-ok);
}

.kt-btn.kt-btn-success:hover {
    background-color: color-mix(in srgb, var(--bz-color-ok) 85%, black);
    border-color: color-mix(in srgb, var(--bz-color-ok) 85%, black);
    color: #fff;
}

.kt-btn.kt-btn-danger:not(.bz-icon-action) {
    background-color: var(--bz-color-danger);
    color: #fff;
    border: 1px solid var(--bz-color-danger);
}

.kt-btn.kt-btn-danger:not(.bz-icon-action):hover {
    background-color: color-mix(in srgb, var(--bz-color-danger) 85%, black);
    border-color: color-mix(in srgb, var(--bz-color-danger) 85%, black);
    color: #fff;
}

/* ── 5. Touch targets ─────────────────────────────────────────────────────────
   The ONE place button sizes grow for a finger. Deliberately keyed on
   (pointer: coarse) rather than a width breakpoint: a landscape tablet is wide and
   still finger-driven, and a narrow desktop window is neither. The DevExpress
   composite exclusions above out-specify `.dxbl-btn` here too, so widget internals
   are left alone. */
@media (pointer: coarse) {
    .dxbl-btn {
        min-height: 44px;
        --dxbl-btn-padding-x: 1rem;
        --dxbl-btn-padding-y: 0.5rem;
    }

    .bz-icon-action {
        width: 44px;
        min-width: 44px;
        height: 44px;
    }

    .bz-icon-action i,
    .bz-icon-action .dxbl-image {
        font-size: 1.25rem;
    }

    .kt-btn { height: 44px; padding-inline: 16px; }
    .kt-btn.kt-btn-icon { width: 44px; height: 44px; }
    .kt-btn.kt-btn-sm { height: 40px; }
    .kt-btn.kt-btn-sm.kt-btn-icon { width: 40px; height: 40px; }

    /* Hit area only — these sit inline in a chip/toast/avatar row, so growing the
       box instead of the padding would move the layout around them. */
    .bz-chip-x { padding: var(--bz-space-2); }
    .bz-toast-close { padding: var(--bz-space-2); }
    .bz-avatar-action { width: 40px; height: 40px; }

    /* Grid command cells stay denser than the rest — a row of 44px buttons turns a
       10-row grid into a scroll marathon — but 28px is not a target either. */
    .dxbl-grid td.dxbl-grid-command-cell .dxbl-btn { min-height: 36px; }

    .dxbl-grid td.dxbl-grid-command-cell .bz-icon-action {
        width: 36px;
        min-width: 36px;
        height: 36px;
    }

    .bz-toolbar,
    .bz-form-actions { gap: var(--bz-space-3); }

    /* Text actions become ghost buttons: a bare word is not a tappable control. */
    .dxbl-btn.dxbl-btn-link {
        --dxbl-btn-bg: var(--bz-color-surface);
        --dxbl-btn-border-color: var(--bz-color-border);
        --dxbl-btn-hover-bg: var(--bz-color-surface-2);
        --dxbl-btn-text-decoration: none;
        --dxbl-btn-padding-x: var(--bz-space-3);
    }

    .dxbl-btn.dxbl-btn-link:hover { text-decoration: none; }

    /* Text ghosts only — an ICON-only ghost/dim button (the login password eye,
       the search-modal close, the sidebar logout) stays lightweight per #113 round 2. */
    .kt-btn-ghost:not(.kt-btn-icon),
    .kt-btn-dim:not(.kt-btn-icon) {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        padding-inline: var(--bz-space-3);
        border: 1px solid var(--bz-color-border);
        border-radius: var(--bz-radius-sm);
        background-color: var(--bz-color-surface);
        text-decoration: none;
    }

    .kt-btn-ghost:not(.kt-btn-icon):hover,
    .kt-btn-dim:not(.kt-btn-icon):hover { text-decoration: none; }

    /* GitLab #127: dropdown options were ~34px rows with tight padding on a phone. DevExpress paints
       a list-box row entirely from its own variables — --dxbl-list-box-item-padding-* for the <li>
       (list pattern: DxComboBox, DxTagBox, DxListBox) and --dxbl-list-box-item-cell-padding-* for the
       <td> (table pattern) — so widen those rather than the element, per the button-system rule. The
       .dxbl-sm / .dxbl-lg size variants out-specify the bare container in the DevExpress theme,
       hence the size-variant selectors and still no !important. Native <select> options are drawn by the OS
       and cannot be styled at all — nothing to do there. */
    .dxbl-list-box,
    .dxbl-list-box.dxbl-sm,
    .dxbl-list-box.dxbl-lg,
    .dxbl-list-box-render-container,
    .dxbl-list-box-render-container.dxbl-sm,
    .dxbl-list-box-render-container.dxbl-lg {
        --dxbl-list-box-item-padding-y: 0.7rem;
        --dxbl-list-box-item-padding-x: 0.9rem;
        --dxbl-list-box-item-cell-padding-y: 0.7rem;
        --dxbl-list-box-item-cell-padding-x: 0.9rem;
        --dxbl-list-box-font-size: var(--bz-fs-md);
    }

    /* The padding above lands just under 44px at the small size mode; this is the floor. */
    /* Verified DOM (26.1.3): a DxComboBox dropdown is .dxbl-list-box-render-container > dxbl-scroll-viewer
       > .dxbl-scroll-viewer-content > ul > li — the variables live on the render container, not on a
       .dxbl-list-box element, which only the standalone DxListBox renders. */
    :is(.dxbl-list-box, .dxbl-list-box-render-container) .dxbl-scroll-viewer-content > ul > li {
        min-height: 44px;
        display: flex;
        align-items: center;
    }

    /* GitLab #126: BzTabs now renders DevExpress's prev/next scroll buttons. They are ordinary
       .dxbl-btn instances, so the height floor above already reaches them — this only guarantees the
       horizontal half of the target, which their icon-only width does not. */
    .dxbl-tabs-scroll-btn-prev,
    .dxbl-tabs-scroll-btn-next {
        min-width: 44px;
    }
}

/* Plain <button> and <a> styled as buttons via legacy module CSS classes —
   normalize to match DxButton visual weight */
.btn-primary, a.btn-primary {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.8rem;
    background: var(--bz-color-accent);
    color: #fff;
    border: 1px solid var(--bz-color-accent);
    border-radius: var(--bz-radius-sm);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    cursor: pointer;
    text-decoration: none;
    line-height: 1.4;
}
.btn-primary:hover, a.btn-primary:hover {
    background: var(--bz-color-accent-hover);
    border-color: var(--bz-color-accent-hover);
    text-decoration: none;
    color: #fff;
}

.btn-secondary, a.btn-secondary {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.8rem;
    background: var(--bz-color-surface);
    color: var(--bz-color-fg);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-sm);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    cursor: pointer;
    text-decoration: none;
    line-height: 1.4;
}
.btn-secondary:hover, a.btn-secondary:hover {
    background: var(--bz-color-surface-3);
    border-color: var(--bz-color-border-strong);
    text-decoration: none;
    color: var(--bz-color-fg);
}

.btn-warning, a.btn-warning {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.8rem;
    background: var(--bz-color-warn);
    color: #fff;
    border: 1px solid var(--bz-color-warn);
    border-radius: var(--bz-radius-sm);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    cursor: pointer;
    text-decoration: none;
    line-height: 1.4;
}

.btn-danger, a.btn-danger {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.8rem;
    background: var(--bz-color-danger);
    color: #fff;
    border: 1px solid var(--bz-color-danger);
    border-radius: var(--bz-radius-sm);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    cursor: pointer;
    text-decoration: none;
    line-height: 1.4;
}

.btn-link, a.btn-link {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.5rem;
    background: transparent;
    color: var(--bz-color-accent);
    border: 1px solid transparent;
    border-radius: var(--bz-radius-sm);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    cursor: pointer;
    text-decoration: none;
    line-height: 1.4;
}
.btn-link:hover, a.btn-link:hover {
    background: var(--bz-color-surface-3);
    text-decoration: none;
    color: var(--bz-brand-primary);
}

.btn-icon-only, a.btn-icon-only {
    justify-content: center;
    min-width: 2rem;
    width: 2rem;
    padding-inline: 0;
}

.btn-icon-only i,
.btn-icon-only .ki-filled,
.btn-icon-only .ki-outline,
.btn-icon-only .ki-solid,
a.btn-icon-only i,
a.btn-icon-only .ki-filled,
a.btn-icon-only .ki-outline,
a.btn-icon-only .ki-solid {
    font-size: 1rem;
}

/* Toolbar layout — legacy class kept for backward compat */
.toolbar {
    display: flex;
    gap: var(--bz-space-2);
    align-items: center;
    margin-bottom: var(--bz-space-3);
    flex-wrap: wrap;
}

/* ── Page card — wraps a DxGrid (or any content) inside a surface card ── */
.bz-page-card {
    --bz-card-pad: var(--bz-space-4);
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-lg);
    box-shadow: var(--bz-shadow-sm);
    /* The card owns the inset so no content ever touches its border; the genuinely full-bleed
       children (grids, head bars) cancel it below. */
    padding: var(--bz-card-pad);
    /* overflow-x: auto (not hidden) so a grid widened by column resize scrolls within the card
       instead of escaping to the page's own scroll container and shifting the whole layout. */
    overflow-x: auto;
    overflow-y: hidden;
    margin-bottom: var(--bz-space-5);
}

/* Full-bleed children: DevExpress grids (direct or inside a wrapper element) and the padded
   head bars own the card's edges, exactly as they did before the card gained padding. */
.bz-page-card > :is(.dxbl-grid, .bz-dxgrid, *:has(> .dxbl-grid),
                    .bz-page-card-head, .bz-dash-section-head) {
    margin-left: calc(var(--bz-card-pad) * -1);
    margin-right: calc(var(--bz-card-pad) * -1);
    /* Grids carry width:100%, which resolves against the card's CONTENT box — without this the
       negative margins would shift them left and leave a gap on the right. */
    width: calc(100% + var(--bz-card-pad) * 2);
}
.bz-page-card > :is(.dxbl-grid, .bz-dxgrid, *:has(> .dxbl-grid),
                    .bz-page-card-head, .bz-dash-section-head):first-child {
    margin-top: calc(var(--bz-card-pad) * -1);
}
.bz-page-card > :is(.dxbl-grid, .bz-dxgrid, *:has(> .dxbl-grid)):last-child {
    margin-bottom: calc(var(--bz-card-pad) * -1);
}
/* Head bar keeps its divider gap to the now-inset body below it. */
.bz-page-card > :is(.bz-page-card-head, .bz-dash-section-head):not(:last-child) {
    margin-bottom: var(--bz-card-pad);
}
/* These already carried their own inset — don't double it now that the card has one. */
.bz-page-card > .bz-card-pad { padding: 0; }
.bz-page-card > .bz-filter-bar { padding-left: 0; padding-right: 0; }

.bz-page-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--bz-space-3) var(--bz-space-4);
    border-bottom: 1px solid var(--bz-color-border);
    background: var(--bz-color-surface-2);
    gap: var(--bz-space-3);
    flex-wrap: wrap;
}

.bz-page-card-title {
    font-size: var(--bz-fs-md);
    font-weight: var(--bz-fw-semibold);
    color: var(--bz-color-fg);
    margin: 0;
}

.bz-page-card-body { overflow: auto; }

/* ── Styled toolbar — replaces bare <section class="toolbar"> ───────── */
.bz-toolbar {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    padding: var(--bz-space-3) 0;
    margin-bottom: var(--bz-space-3);
    flex-wrap: wrap;
}

/* Heading toolbar row above a grid/section card (pattern B — see AGENTS.md UI standard) */
.bz-grid-toolbar {
    margin-bottom: var(--bz-space-4);
}

/* Search input with leading magnifier icon */
.bz-search-input {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-width: 220px;
    max-width: 340px;
    flex: 1 1 220px;
}

.bz-search-input i {
    position: absolute;
    left: 10px;
    font-size: 15px;
    color: var(--bz-color-fg-subtle);
    pointer-events: none;
    line-height: 1;
}

.bz-search-input input {
    width: 100%;
    padding: 0.38rem 0.75rem 0.38rem 2.1rem;
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    color: var(--bz-color-fg);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    line-height: 1.5;
    transition: border-color var(--bz-duration-fast) var(--bz-ease),
                box-shadow var(--bz-duration-fast) var(--bz-ease);
    min-height: var(--bz-control-height);
}

.bz-search-input input::placeholder { color: var(--bz-color-fg-subtle); }

.bz-search-input input:focus {
    outline: none;
    border-color: var(--bz-brand-primary);
    box-shadow: 0 0 0 3px var(--bz-color-focus-ring);
}

/* Toolbar select filter */
.bz-toolbar-select {
    padding: 0.38rem 0.7rem;
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    color: var(--bz-color-fg);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    min-height: var(--bz-control-height);
    cursor: pointer;
    transition: border-color var(--bz-duration-fast) var(--bz-ease);
}

.bz-toolbar-select:focus {
    outline: none;
    border-color: var(--bz-brand-primary);
    box-shadow: 0 0 0 3px var(--bz-color-focus-ring);
}

/* Toolbar spacer — pushes right-side actions to the end */
.bz-toolbar-spacer { flex: 1; }

/* Toolbar action group */
.bz-toolbar-actions {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    flex-shrink: 0;
}

/* ── Global status badge utilities ──────────────────────────────────── */
/* Use .bz-badge + one of the variants. Replaces per-page .badge-* */
.bz-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: var(--bz-radius-pill);
    font-size: var(--bz-fs-xs);
    font-weight: var(--bz-fw-medium);
    white-space: nowrap;
    line-height: 1.5;
}

.bz-badge-ok      { color: var(--bz-color-ok);     background: color-mix(in srgb, var(--bz-color-ok)     13%, transparent); }
.bz-badge-warn    { color: var(--bz-color-warn);    background: color-mix(in srgb, var(--bz-color-warn)   13%, transparent); }
.bz-badge-danger  { color: var(--bz-color-danger);  background: color-mix(in srgb, var(--bz-color-danger) 13%, transparent); }
.bz-badge-info    { color: var(--bz-color-info);    background: color-mix(in srgb, var(--bz-color-info)   13%, transparent); }
.bz-badge-neutral { color: var(--bz-color-fg-muted); background: var(--bz-color-surface-2); }

/* Row of badges in a grid cell */
.bz-badge-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bz-space-1);
    align-items: center;
}

/* ── Text utility helpers (referenced from multiple modules) ─────────── */
.bz-text-danger  { color: var(--bz-color-danger) !important; }
.bz-text-warn    { color: var(--bz-color-warn) !important; }
.bz-text-success { color: var(--bz-color-ok) !important; }

/* A bulleted list inside a card or alert, without the browser's default margins. */
.bz-plain-list {
    margin: var(--bz-space-2) 0 0;
    padding-inline-start: var(--bz-space-5);
}

/* `.text-muted` is NOT Bootstrap's here: the Metronic/Tailwind build emits it as
   `color: var(--muted)` — the muted *surface* color, not a text color (that is
   `--muted-foreground`). It therefore renders near-invisible, badly so in dark mode.
   ~258 usages across the web modules mean it, so alias it to the same theme-aware
   token `.bz-text-muted` uses instead of rewriting every call site. */
.text-muted,
.bz-text-muted   { color: var(--bz-color-fg-muted) !important; }
.bz-text-subtle  { color: var(--bz-color-fg-subtle) !important; }
.bz-text-accent  { color: var(--bz-color-accent) !important; }
.bz-fw-medium    { font-weight: var(--bz-fw-medium) !important; }
.bz-fw-semibold  { font-weight: var(--bz-fw-semibold) !important; }
.bz-fs-xs        { font-size: var(--bz-fs-xs) !important; }
.bz-fs-sm        { font-size: var(--bz-fs-sm) !important; }

/* ── Suggestion/alert strip (used in TenantAdmin) ───────────────────── */
.bz-suggestion {
    display: flex;
    align-items: flex-start;
    gap: var(--bz-space-3);
    padding: var(--bz-space-3) var(--bz-space-4);
    border-radius: var(--bz-radius-md);
    background: color-mix(in srgb, var(--bz-color-info) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--bz-color-info) 25%, transparent);
    margin-top: var(--bz-space-3);
    font-size: var(--bz-fs-sm);
    color: var(--bz-color-fg-muted);
}
.bz-suggestion i { color: var(--bz-color-info); margin-top: 2px; flex-shrink: 0; font-size: 16px; }

/* ── Form feedback row ──────────────────────────────────────────────── */
.bz-form-feedback {
    margin-bottom: var(--bz-space-3);
}

/* ── Improved DxGrid inside page cards ─────────────────────────────── */
/* Remove outer border when grid sits inside a bz-page-card */
.bz-page-card .dxbl-grid {
    border: none !important;
    border-radius: 0 !important;
}

/* Tighter header padding for better vertical rhythm */
.dxbl-grid .dxbl-grid-header-cell {
    padding-top: 9px !important;
    padding-bottom: 9px !important;
    font-size: var(--bz-fs-xs) !important;
    font-weight: var(--bz-fw-semibold) !important;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
    vertical-align: middle;
    color: var(--bz-color-fg-muted);
}

/* Standalone grids (outside bz-page-card) get soft corners, and scroll horizontally in place
   rather than letting a resized column push the whole page (see .bz-page-card above). */
.bz-dxgrid {
    border-radius: var(--bz-radius-md);
    overflow-x: auto;
    overflow-y: hidden;
}

/* Filter-row editors: compact, fill the cell, no visual competition with data rows */
.dxbl-grid .dxbl-grid-filter-row td {
    padding: 4px 6px !important;
    background: var(--bz-color-surface);
}
.dxbl-grid .bz-filter-cell {
    width: 100%;
    min-width: 0;
    font-size: var(--bz-fs-sm);
}
.dxbl-grid .bz-filter-range {
    display: inline-flex;
    gap: 4px;
    width: 100%;
}
.dxbl-grid .bz-filter-range .bz-filter-cell {
    width: 50%;
}

/* Data row height */
.dxbl-grid .dxbl-grid-data-row td {
    padding-top: 8px !important;
    padding-bottom: 8px !important;
}

/* Link styling inside grids */
.dxbl-grid a {
    color: var(--bz-color-accent);
    text-decoration: none;
    font-weight: var(--bz-fw-medium);
}
.dxbl-grid a:hover { text-decoration: underline; }

/* Pager container */
.dxbl-grid .dxbl-grid-pager {
    border-top: 1px solid var(--bz-color-border);
    padding: 6px 12px;
    font-size: var(--bz-fs-sm);
}

/* ── DevExpress popup / window — border refinements ─────────────────
   Dark-mode colours are handled by the DevExpress dark.min.css which is
   loaded dynamically by syncDxDarkTheme() in App.razor / SidebarFooter.razor.js
   whenever html.dark is active.  Only visual overrides that go beyond what
   the DevExpress theme provides are placed here. */
.dxbl-modal,
.dxbl-window {
    border-radius: var(--bz-radius-lg);
    box-shadow: var(--bz-shadow-lg);
}

/* ── Popup width standard ────────────────────────────────────────────
   Product decision: every modal popup in the system is 90% of the
   viewport wide. DevExpress drives popup width via --dxbl-popup-width
   (fed by each DxPopup's Width parameter and by adaptive mode), so the
   override must land on the dialog element with !important. Scoped to
   .dxbl-modal so dropdowns/toasts are unaffected. */
.dxbl-modal .dxbl-popup {
    width: 90vw !important;
    max-width: 90vw !important;
}

.dxbl-modal .dxbl-popup-footer,
.dxbl-window .dxbl-popup-footer {
    border-top: 1px solid var(--bz-color-border);
    gap: var(--bz-space-2);
}

/* ── Modal / drawer form polish ──────────────────────────────────────
   Consistent presentation for DxFormLayout-based forms hosted inside
   DevExpress popups (create/edit/confirm dialogs across all modules).
   Inputs fill their column so forms never look cramped, and captions
   read consistently. Scoped to popups so page-level layouts are intact. */
.dxbl-modal .dxbl-form-layout .dxbl-text-edit,
.dxbl-window .dxbl-form-layout .dxbl-text-edit,
.dxbl-modal .dxbl-form-layout .dxbl-edit,
.dxbl-window .dxbl-form-layout .dxbl-edit,
.dxbl-modal .dxbl-form-layout .dxbl-memo,
.dxbl-window .dxbl-form-layout .dxbl-memo,
.dxbl-modal .dxbl-form-layout .dxbl-combobox,
.dxbl-window .dxbl-form-layout .dxbl-combobox,
.dxbl-modal .dxbl-form-layout .dxbl-dropdown,
.dxbl-window .dxbl-form-layout .dxbl-dropdown,
.dxbl-modal .dxbl-form-layout .dxbl-date-edit,
.dxbl-window .dxbl-form-layout .dxbl-date-edit,
.dxbl-modal .dxbl-form-layout .dxbl-spin-edit,
.dxbl-window .dxbl-form-layout .dxbl-spin-edit {
    width: 100%;
}

/* ── DxGrid inline edit form ─────────────────────────────────── */
.bz-edit-form {
    background: var(--bz-color-bg-subtle);
    border-top: 2px solid var(--bz-color-accent);
}

.bz-edit-form-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 16px 16px;
    border-top: 1px solid var(--bz-color-border);
}

/* ── IntelligentAutomation visibility hardening (prompt 040) ─────────── */

/* Success badge variant (sits alongside bz-badge-danger / bz-badge-neutral). */
.bz-badge-success { color: var(--bz-color-ok); background: color-mix(in srgb, var(--bz-color-ok) 14%, transparent); }

/* Filter bar: a row of labelled inputs above a grid. */
.bz-filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bz-space-3);
    align-items: flex-end;
    padding: var(--bz-space-3) var(--bz-space-4);
}

.bz-inline-label { display: inline-flex; align-items: center; gap: var(--bz-space-2); margin: 0; }
.bz-input-inline { width: auto; min-width: 9rem; }

/* Chip editor (allowed tools / permissions). */
.bz-chip-editor { margin: var(--bz-space-3) 0; }
.bz-chips { display: flex; flex-wrap: wrap; gap: var(--bz-space-2); margin: var(--bz-space-2) 0; }
.bz-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--bz-space-1);
    padding: 2px var(--bz-space-2);
    font-size: var(--bz-fs-sm);
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-pill);
}
/* Assignee chip carrying a face instead of initials — same footprint as .bz-chip so a mixed row
   (some people have a picture, most do not) stays on one baseline. */
.bz-chip-img {
    width: 22px;
    height: 22px;
    border-radius: var(--bz-radius-pill);
    object-fit: cover;
    border: 1px solid var(--bz-color-border);
    background: var(--bz-color-surface-2);
    flex: 0 0 auto;
}
.bz-chip-read    { color: var(--bz-color-accent); background: color-mix(in srgb, var(--bz-color-accent) 12%, transparent); }
.bz-chip-propose { color: var(--bz-color-ok); background: color-mix(in srgb, var(--bz-color-ok) 12%, transparent); }
.bz-chip-x {
    border: none;
    background: transparent;
    cursor: pointer;
    color: var(--bz-color-fg-muted);
    font-size: 1rem;
    line-height: 1;
    padding: 0 2px;
}
.bz-chip-x:hover { color: var(--bz-color-danger); }

/* Disabled (no-access) dashboard quick-action card. */
.bz-dash-nav-card.bz-disabled { opacity: .55; cursor: not-allowed; pointer-events: none; }

/* Subtle inner panel (e.g. MCP tool preview). */
.bz-subtle { background: var(--bz-color-bg-subtle); box-shadow: none; }

/* Ordered/unordered helper list (MCP setup steps). */
.bz-list { margin: var(--bz-space-2) 0; padding-left: var(--bz-space-5); }
.bz-list li { margin-bottom: var(--bz-space-1); }

/* Review item + secret banner + advanced disclosure (shared by IA pages). */
.bz-review-item {
    padding: var(--bz-space-3) var(--bz-space-4);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    margin-bottom: var(--bz-space-3);
    background: var(--bz-color-surface);
}
.bz-review-item-head { display: flex; align-items: center; gap: var(--bz-space-2); margin-bottom: var(--bz-space-2); }
.bz-advanced { margin: var(--bz-space-2) 0; }
.bz-advanced > summary { cursor: pointer; color: var(--bz-color-fg-muted); font-size: var(--bz-fs-sm); }
.bz-secret-banner { border-left: 3px solid var(--bz-color-accent); padding: var(--bz-space-3) var(--bz-space-4); }

/* ─────────────────────────────────────────────────────────────────────────
   Shared form controls & helper text.
   These utility classes are used by hand-authored (non-DevExpress) forms across
   many module pages (AI smart entry, MCP clients, settings panels, wizards…).
   Defining them globally here keeps those pages visually consistent with the
   DevExpress surfaces instead of falling back to unstyled native controls.
   ───────────────────────────────────────────────────────────────────────── */

/* Field label — block by default so the control sits on its own line. */
.bz-label {
    display: block;
    margin-bottom: var(--bz-space-1);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    color: var(--bz-color-fg-muted);
}

/* Text input / textarea / select — full-width control matching the search box. */
.bz-input {
    display: block;
    width: 100%;
    padding: 0.45rem 0.75rem;
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    color: var(--bz-color-fg);
    font-family: var(--bz-font-sans);
    font-size: var(--bz-fs-sm);
    line-height: 1.5;
    min-height: var(--bz-control-height);
    transition: border-color var(--bz-duration-fast) var(--bz-ease),
                box-shadow var(--bz-duration-fast) var(--bz-ease);
}
textarea.bz-input { min-height: 5rem; resize: vertical; }
.bz-input::placeholder { color: var(--bz-color-fg-subtle); }
.bz-input:focus {
    outline: none;
    border-color: var(--bz-brand-primary);
    box-shadow: 0 0 0 3px var(--bz-color-focus-ring);
}
.bz-input:disabled,
.bz-input[readonly],
.bz-input[readonly="readonly"] {
    background: var(--bz-color-surface-2);
    color: var(--bz-color-fg-muted);
    cursor: not-allowed;
}
.bz-mono { font-family: var(--bz-font-mono); font-size: var(--bz-fs-xs); }

/* Staged record-document upload — file chip, type picker and description composer with the
   Upload button parked in the textarea's bottom-right corner. */
.bz-doc-upload {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    margin-bottom: var(--bz-space-3);
    padding: var(--bz-space-3);
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
}
.bz-doc-upload-file { display: flex; align-items: center; gap: var(--bz-space-2); }
.bz-doc-compose { position: relative; }
.bz-doc-compose textarea.bz-input { padding-bottom: 2.9rem; }
.bz-doc-compose > .kt-btn { position: absolute; right: var(--bz-space-2); bottom: var(--bz-space-2); }

/* Inline control (e.g. the voice-language picker) keeps its intrinsic width even
   though .bz-input sets width:100% — compound selector wins regardless of order. */
.bz-input.bz-input-inline { width: auto; min-width: 9rem; }

/* Responsive two-column form grid for review / detail field sets. */
.bz-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--bz-space-3) var(--bz-space-4);
    margin: var(--bz-space-3) 0;
}

/* Muted helper / hint line with an optional leading icon. */
.bz-hint {
    display: flex;
    align-items: flex-start;
    gap: var(--bz-space-2);
    margin: var(--bz-space-2) 0 0;
    color: var(--bz-color-fg-muted);
    font-size: var(--bz-fs-sm);
    line-height: 1.45;
}
.bz-hint i { color: var(--bz-color-info); margin-top: 1px; flex-shrink: 0; font-size: 15px; }

/* Inline error and empty-state messages. */
.bz-error-msg {
    margin: var(--bz-space-2) 0 0;
    color: var(--bz-color-danger);
    font-size: var(--bz-fs-sm);
}
.bz-empty-msg {
    margin: var(--bz-space-3) 0;
    color: var(--bz-color-fg-subtle);
    font-size: var(--bz-fs-sm);
}

/* Small button size modifier (pairs with .btn-primary/.btn-secondary). */
.btn-sm {
    padding: 0.2rem 0.55rem;
    font-size: var(--bz-fs-xs);
}

/* Lightweight data table for simple read-only lists (non-DevExpress surfaces). */
.bz-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--bz-fs-sm);
    margin: var(--bz-space-2) 0;
}
.bz-table th,
.bz-table td {
    text-align: left;
    padding: 0.45rem 0.6rem;
    border-bottom: 1px solid var(--bz-color-border);
}
.bz-table th {
    color: var(--bz-color-fg-muted);
    font-weight: var(--bz-fw-semibold);
    background: var(--bz-color-surface-2);
}
.bz-table tbody tr:hover { background: var(--bz-color-surface-2); }

/* GitLab #124: a row that navigates says so. The hover highlight above is already the feedback, so
   this is only the cursor — the @onclick and the stopPropagation on the interactive cells live in
   the component (TaskInboxList). */
.bz-table tr.bz-row-link { cursor: pointer; }

/* ── Business Partner (Relationship Management) wizard + detail ─────────
   This app ships only a subset of Bootstrap utilities (spacing/sizing,
   not flex/grid/alert/form-select), so these layout helpers live here as
   first-class bz-* classes loaded globally via tokens+shell. */

/* Generic flex helpers (replacements for the missing d-flex utilities). */
.bz-form-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--bz-space-3);
    margin-top: var(--bz-space-5);
}
.bz-actions-end {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--bz-space-2);
    margin-bottom: var(--bz-space-3);
}
.bz-actions-end-inline { display: inline-flex; gap: var(--bz-space-2); }

/* Centered wizard column. */
.bz-wizard { max-width: 980px; margin: 0 auto; padding: var(--bz-space-5); }
/* Wide variant: fill 90% of the available width (tenant onboarding). The shared EmptyLayout caps
   its content column (max-width 728px), so a page hosting a wide wizard lifts that cap too. */
.bz-wizard-wide { max-width: 90%; }
/* EmptyLayout caps content via max-inline-size and sizes shrink-to-fit as a flex item — lift both. */
main.empty-layout:has(.bz-wizard-wide) { max-inline-size: none; width: 100%; flex: 1 1 auto; }

/* Horizontal step indicator. */
.bz-wizard-steps {
    display: flex;
    /* 7+ steps with long labels overflow the card and clip the last tab — wrap instead. */
    flex-wrap: wrap;
    gap: var(--bz-space-2);
    list-style: none;
    padding: 0;
    margin: 0 0 var(--bz-space-5);
}
.bz-wizard-steps li {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: break-word;
    text-align: center;
    padding: 0.6rem 0.5rem;
    border-radius: var(--bz-radius-md);
    background: var(--bz-color-surface-2);
    color: var(--bz-color-fg-subtle);
    font-weight: var(--bz-fw-medium);
    font-size: var(--bz-fs-sm);
    border: 1px solid var(--bz-color-border);
}
.bz-wizard-steps li.active {
    background: var(--bz-brand-primary);
    color: var(--bz-color-accent-fg);
    border-color: var(--bz-brand-primary);
}
.bz-wizard-steps li.done {
    background: var(--bz-color-surface);
    color: var(--bz-color-ok);
    border-color: var(--bz-color-ok);
}
.bz-wizard-steps-clickable li { cursor: pointer; transition: background .12s, color .12s; }
.bz-wizard-steps-clickable li:hover { border-color: var(--bz-brand-primary); }
.bz-wizard-step { padding: var(--bz-space-2) 0 var(--bz-space-4); }
.bz-wizard-step h5 {
    font-size: var(--bz-fs-md);
    font-weight: var(--bz-fw-semibold);
    margin: 0 0 var(--bz-space-2);
    color: var(--bz-color-fg);
}
.bz-wizard-step h6 {
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-semibold);
    margin: var(--bz-space-4) 0 var(--bz-space-2);
    color: var(--bz-color-fg);
}

/* Selectable platform cards in the Add-connector wizard. */
.bz-connector-platform {
    display: flex;
    align-items: center;
    gap: var(--bz-space-3);
    padding: var(--bz-space-3);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    background: transparent;
    color: var(--bz-color-fg);
    cursor: pointer;
    text-align: left;
    transition: background .12s, border-color .12s, box-shadow .12s;
}
.bz-connector-platform:hover { border-color: var(--bz-color-accent); }
.bz-connector-platform-active {
    border-color: var(--bz-color-accent);
    background: color-mix(in srgb, var(--bz-color-accent) 12%, transparent);
    box-shadow: inset 0 0 0 1px var(--bz-color-accent);
}
.bz-connector-platform-active .ki-filled { color: var(--bz-color-accent); }

/* Field label + radio group row. */
.bz-field-label {
    display: block;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    color: var(--bz-color-fg-muted);
    margin-bottom: var(--bz-space-2);
}
.bz-field-block { margin-bottom: var(--bz-space-4); }
.bz-radio-row { display: flex; gap: var(--bz-space-5); flex-wrap: wrap; }
.bz-radio-row label { display: inline-flex; align-items: center; gap: var(--bz-space-1); cursor: pointer; }
.bz-help { color: var(--bz-color-fg-subtle); font-size: var(--bz-fs-sm); margin: 0 0 var(--bz-space-4); }

/* Padded body for a bz-page-card whose content sits directly under a bz-page-card-head
   (the card itself has no inner padding and clips to its radius). */
.bz-card-pad { padding: var(--bz-space-4) var(--bz-space-5); }

/* MCP clients page: endpoint row + tool-picker dropdown option + effective-tools preview. */
.bz-mcp-endpoint { display: flex; align-items: center; gap: var(--bz-space-2); flex-wrap: wrap; margin: var(--bz-space-2) 0; }
.bz-mcp-option { padding: var(--bz-space-1) 0; }
.bz-mcp-option-head { display: flex; align-items: center; gap: var(--bz-space-2); }
/* GitLab #90: .bz-page-card clips (overflow-y: hidden) rather than scrolling, so a tag box or
   preview block that grows past the card's height was pushing its own content unreachably off the
   bottom (e.g. "Select all" on either picker). Both scroll internally instead of growing unbounded. */
.bz-mcp-tagbox { display: block; width: 100%; max-height: 30vh; overflow-y: auto; }
.bz-mcp-preview {
    margin-top: var(--bz-space-5);
    padding: var(--bz-space-4);
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    max-height: 25vh;
    overflow-y: auto;
}
.bz-mcp-preview > .bz-label {
    display: block;
    margin-bottom: var(--bz-space-2);
    text-transform: uppercase;
    font-size: var(--bz-fs-xs);
    letter-spacing: .04em;
    color: var(--bz-color-fg-subtle);
}

/* Inline action buttons inside a DxGrid command cell. */
.bz-grid-actions { display: inline-flex; align-items: center; gap: var(--bz-space-2); }

/* "Select all / Clear" shortcuts under a tag-box picker. */
.bz-picker-actions { display: flex; gap: var(--bz-space-3); margin-top: var(--bz-space-2); }

/* Assistant chat: rendered markdown inside message bubbles. */
.bz-ai-chat-md { font-size: var(--bz-fs-sm); line-height: 1.5; }
.bz-ai-chat-md p { margin: 0 0 var(--bz-space-2); }
.bz-ai-chat-md p:last-child { margin-bottom: 0; }
.bz-ai-chat-md ul, .bz-ai-chat-md ol { margin: 0 0 var(--bz-space-2); padding-left: 1.2rem; }
.bz-ai-chat-md code {
    font-family: var(--bz-font-mono);
    font-size: var(--bz-fs-xs);
    background: var(--bz-color-surface-2);
    border-radius: var(--bz-radius-sm);
    padding: 0 4px;
}
.bz-ai-chat-md pre {
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-2) var(--bz-space-3);
    overflow-x: auto;
}
.bz-ai-chat-md table { border-collapse: collapse; margin: var(--bz-space-2) 0; max-width: 100%; }
.bz-ai-chat-md th, .bz-ai-chat-md td {
    border: 1px solid var(--bz-color-border);
    padding: 4px 8px;
    text-align: left;
    font-size: var(--bz-fs-xs);
}
.bz-ai-chat-md th { background: var(--bz-color-surface-2); font-weight: var(--bz-fw-semibold); }

/* GitLab #123: the assistant HAS been emitting record links since task 201 — they were simply
   invisible. Metronic's preflight resets every anchor to `color: inherit; text-decoration: inherit`
   and this file's own base rule drops the underline, so a linked business partner rendered as plain
   body text. Chat output is the one place an anchor must announce itself, so restate both here. */
.bz-ai-chat-md a,
.bz-ai-chat-table a:not(.bz-icon-action) {
    color: var(--bz-color-accent);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.bz-ai-chat-md a:hover,
.bz-ai-chat-table a:not(.bz-icon-action):hover {
    color: var(--bz-color-accent-hover);
    text-decoration-thickness: 2px;
}

/* Assistant chat: Results cards (read-tool datasets rendered as tables + export actions). */
.bz-ai-chat-results { padding: var(--bz-space-2) var(--bz-space-3); overflow-y: auto; max-height: 40%; }
.bz-ai-chat-result-card {
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-2) var(--bz-space-3);
    margin-bottom: var(--bz-space-2);
}
.bz-ai-chat-result-head {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    font-weight: var(--bz-fw-semibold);
    font-size: var(--bz-fs-sm);
    margin-bottom: var(--bz-space-2);
}
.bz-ai-chat-result-head .bz-hint { margin: 0 0 0 auto; }
.bz-ai-chat-result-scroll { overflow: auto; max-height: 240px; border: 1px solid var(--bz-color-border); border-radius: var(--bz-radius-sm); }
.bz-ai-chat-table { width: 100%; border-collapse: collapse; font-size: var(--bz-fs-xs); }
.bz-ai-chat-table th, .bz-ai-chat-table td {
    border-bottom: 1px solid var(--bz-color-border);
    padding: 4px 8px;
    text-align: left;
    white-space: nowrap;
}
.bz-ai-chat-table th { position: sticky; top: 0; background: var(--bz-color-surface-2); font-weight: var(--bz-fw-semibold); }
.bz-ai-chat-result-actions { display: flex; gap: var(--bz-space-2); margin-top: var(--bz-space-2); }

/* Schema-driven review form (AiSchemaForm): nested object groups + repeatable array items. */
.bz-schema-form { margin: var(--bz-space-2) 0; }
.bz-schema-form-caption { display: block; margin: var(--bz-space-3) 0 var(--bz-space-1); font-weight: var(--bz-fw-semibold); }
.bz-schema-form-group { margin-top: var(--bz-space-2); }
.bz-schema-form-item {
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-2) var(--bz-space-3);
    margin-bottom: var(--bz-space-2);
    background: var(--bz-color-surface-2);
}

/* Geo/address search row. */
.bz-geo-search { display: flex; gap: var(--bz-space-2); align-items: stretch; margin-bottom: var(--bz-space-3); }
.bz-geo-search .bz-search-input { max-width: none; flex: 1; }

/* Inline editable people grid in the wizard. */
.bz-mini-grid { width: 100%; border-collapse: collapse; margin-bottom: var(--bz-space-3); }
.bz-mini-grid th {
    text-align: left;
    font-size: var(--bz-fs-xs);
    font-weight: var(--bz-fw-semibold);
    color: var(--bz-color-fg-subtle);
    padding: var(--bz-space-2);
    border-bottom: 1px solid var(--bz-color-border);
}
.bz-mini-grid td { padding: var(--bz-space-2); vertical-align: middle; }

/* Alert boxes (no Bootstrap .alert in this theme). */
.bz-alert {
    border: 1px solid transparent;
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-3) var(--bz-space-4);
    margin-bottom: var(--bz-space-4);
    font-size: var(--bz-fs-sm);
}
.bz-alert ul { margin: var(--bz-space-2) 0 0; padding-left: 1.1rem; }
.bz-alert-danger { background: color-mix(in srgb, var(--bz-color-danger) 12%, var(--bz-color-surface)); color: var(--bz-color-danger); border-color: color-mix(in srgb, var(--bz-color-danger) 40%, transparent); }
.bz-alert-warn { background: color-mix(in srgb, var(--bz-color-warn) 12%, var(--bz-color-surface)); color: var(--bz-color-warn); border-color: color-mix(in srgb, var(--bz-color-warn) 40%, transparent); }
.bz-alert-info { background: color-mix(in srgb, var(--bz-color-info) 12%, var(--bz-color-surface)); color: var(--bz-color-info); border-color: color-mix(in srgb, var(--bz-color-info) 40%, transparent); }

/* Support session banner (task 277). Sticky inside #scrollable_content so a host operator acting
   as a tenant user can never scroll away from the fact — or from the way out. */
.bz-support-banner {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--bz-space-3);
    padding: var(--bz-space-2) var(--bz-space-5);
    font-size: var(--bz-fs-sm);
    color: var(--bz-color-warn);
    background: color-mix(in srgb, var(--bz-color-warn) 14%, var(--bz-color-surface));
    border-bottom: 1px solid color-mix(in srgb, var(--bz-color-warn) 45%, transparent);
}
.bz-support-banner-icon { font-size: 1rem; }
.bz-support-banner-text { font-weight: var(--bz-fw-medium); }
.bz-support-banner-expiry { color: color-mix(in srgb, var(--bz-color-warn) 80%, var(--bz-color-fg)); }
.bz-support-banner-action { margin-inline-start: auto; }

/* Review definition list (replaces the missing .row/.col grid). */
.bz-review { display: grid; grid-template-columns: 180px 1fr; gap: var(--bz-space-2) var(--bz-space-4); margin: 0; }
.bz-review dt { font-weight: var(--bz-fw-medium); color: var(--bz-color-fg-subtle); }
.bz-review dd { margin: 0; color: var(--bz-color-fg); }

/* Entity detail field grid — each child <div> is one <label>Caption</label><div>Value</div> pair,
   wrapping into a responsive multi-column layout (used by ProjectManagement Detail/Config pages). */
.bz-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--bz-space-4);
    margin: var(--bz-space-3) 0;
}
.bz-detail-grid > div > label {
    display: block;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    color: var(--bz-color-fg-subtle);
    margin-bottom: var(--bz-space-1);
}
.bz-detail-grid > div > div { color: var(--bz-color-fg); }
/* Span-based variant of the same pair, used by read-only quick views / detail pages
   (AddressQuickView, AddressDetail, ContactDetail). */
.bz-detail-field > .bz-detail-label {
    display: block;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    color: var(--bz-color-fg-subtle);
    margin-bottom: var(--bz-space-1);
}

.bz-detail-desc { color: var(--bz-color-fg-muted); margin: var(--bz-space-3) 0; line-height: 1.5; }

/* Rendered rich-text (RichTextView) — the tag set SafeHtml allows, nothing else. */
.bz-richtext p, .bz-richtext ul, .bz-richtext ol, .bz-richtext blockquote { margin: 0 0 var(--bz-space-2); }
.bz-richtext > :last-child { margin-bottom: 0; }
.bz-richtext ul, .bz-richtext ol { padding-left: 1.2rem; }
.bz-richtext blockquote {
    border-left: 3px solid var(--bz-color-border);
    padding-left: var(--bz-space-3);
    color: var(--bz-color-fg-subtle);
}
.bz-richtext code {
    font-family: var(--bz-font-mono);
    font-size: var(--bz-fs-xs);
    background: var(--bz-color-surface-2);
    border-radius: var(--bz-radius-sm);
    padding: 0 4px;
}
.bz-richtext pre {
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-2) var(--bz-space-3);
    overflow-x: auto;
}

/* Horizontal sub-page navigation strip (e.g. project Detail tabs: WBS, diary, defects…). */
.bz-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--bz-space-1);
    border-top: 1px solid var(--bz-color-border);
    margin-top: var(--bz-space-4);
    padding-top: var(--bz-space-3);
}
.bz-tabs a {
    padding: var(--bz-space-2) var(--bz-space-3);
    border-radius: var(--bz-radius-md);
    color: var(--bz-color-fg-muted);
    text-decoration: none;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    /* transform repeated: this rule outranks the shared press-feedback selector. */
    transition: background var(--bz-duration-fast) var(--bz-ease),
                color var(--bz-duration-fast) var(--bz-ease),
                transform var(--bz-duration-press) var(--bz-ease);
}
.bz-tabs a:hover { background: var(--bz-color-surface-2); color: var(--bz-color-fg); }

/* Detail summary metric cards. */
.bz-summary-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--bz-space-4);
    margin: var(--bz-space-4) 0;
}
.bz-card {
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-lg);
    padding: var(--bz-space-4);
    box-shadow: var(--bz-shadow-sm);
}
.bz-card-label { font-size: var(--bz-fs-xs); color: var(--bz-color-fg-subtle); text-transform: uppercase; letter-spacing: 0.03em; }
.bz-card-value { font-size: var(--bz-fs-lg, 18px); font-weight: var(--bz-fw-semibold); margin-top: var(--bz-space-1); color: var(--bz-color-fg); }

/* Header row pairing a <RecordDocumentAvatar> with a <PageHeader> on detail pages. */
.bz-page-header-row {
    display: flex;
    align-items: center;
    gap: var(--bz-space-4);
    /* GitLab #109: on a phone the 88px avatar squeezed the title into one word per line. */
    flex-wrap: wrap;
}
@media (max-width: 640px) {
    .bz-page-header-row {
        flex-direction: column;
        align-items: flex-start;
    }
    .bz-page-header-row .bz-page-header { width: 100%; }
}

/* ── Phone-compact toolbars and summary grids (task 286 — GitLab #117–#120) ──
   Below 640px a search box with a 220px floor, a row of 170px filters and a
   180px-minimum card grid each collapse to one item per row: a list page opened
   on nothing but its own toolbar, and a detail page on a column of single-value
   cards. Search takes a full row, filters pair up, cards become a 2–3 column
   grid. Deliberately shared: it reaches every .bz-toolbar and all 19 pages that
   use .bz-summary-cards, which is the point — the density problem is the same
   on every one of them. */
/* Desktop shows every filter at once, so the toggle only exists on a phone. Declared
   here, BEFORE the ≤640 block that turns it on — equal specificity, source order wins. */
.bz-filters-toggle { display: none; }

@media (max-width: 640px) {
    .bz-toolbar { gap: var(--bz-space-2); }

    /* Task 289 (GitLab #121): the board opened on four stacked rows of chrome — view
       pills, board pills, saved views, filters — before a single card. Two moves fix
       it without hiding anything: a pill row SWIPES instead of wrapping (that rule
       sits with .bz-board-pills further down, after the base rule it overrides), and
       the filters collapse behind a toggle so search plus that toggle own one row.
       The page-header chips that take over "Manage boards" / "Save current view"
       live in the pages themselves (`.bz-mobile-contents`). */
    /* Search shares its row with the toggle instead of claiming a full one. Compound
       selector on purpose: `.bz-toolbar .bz-search-input` below is the same specificity
       and later in source, so it would win otherwise. */
    /* Basis "everything but the toggle": with a 0 basis the search shrank far enough for the
       first filter to squeeze onto its row (task 291 review), which broke the two-per-row pairing. */
    .bz-toolbar.bz-filters .bz-search-input { flex: 1 1 calc(100% - 9rem); }

    .bz-filters-toggle {
        display: inline-flex;
        align-items: center;
        gap: var(--bz-space-1);
        flex: 0 0 auto;
        min-height: 44px;
        padding-inline: var(--bz-space-3);
        border: 1px solid var(--bz-color-border);
        border-radius: var(--bz-radius-sm);
        background: var(--bz-color-surface);
        color: var(--bz-color-fg);
        font-size: var(--bz-fs-sm);
        cursor: pointer;
    }

    .bz-filters--open .bz-filters-toggle {
        border-color: var(--bz-primary);
        color: var(--bz-primary);
    }

    .bz-filters:not(.bz-filters--open) > :not(.bz-search-input):not(.bz-filters-toggle) {
        display: none;
    }

    .bz-toolbar .bz-search-input {
        flex: 1 1 100%;
        min-width: 0;
        max-width: none;
    }

    .bz-toolbar .bz-filter,
    .bz-toolbar .bz-toolbar-select {
        flex: 1 1 calc(50% - var(--bz-space-2));
        min-width: 0;
    }

    /* Once everything wraps there is no "end of the row" left to push anything to. */
    .bz-toolbar-spacer { display: none; }

    .bz-summary-cards {
        grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
        gap: var(--bz-space-2);
        margin: var(--bz-space-3) 0;
    }

    .bz-card {
        padding: var(--bz-space-2) var(--bz-space-3);
        border-radius: var(--bz-radius-md);
    }

    .bz-card-value {
        font-size: var(--bz-fs-md);
        margin-top: 2px;
    }
}
.bz-page-header-row .bz-page-header { flex: 1; }

/* <RecordDocumentAvatar> — circular image/initials with a hover-reveal upload/remove overlay. */
.bz-avatar {
    position: relative;
    flex: 0 0 auto;
    border-radius: var(--bz-radius-pill);
    overflow: hidden;
    background: var(--bz-primary-50);
    box-shadow: var(--bz-shadow-sm);
}
.bz-avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.bz-avatar-initials {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--bz-primary);
    font-weight: var(--bz-fw-semibold);
    font-size: 1.5rem;
}
.bz-avatar-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--bz-space-2);
    background: color-mix(in srgb, black 45%, transparent);
    opacity: 0;
    transition: opacity var(--bz-duration-fast) var(--bz-ease);
}
.bz-avatar:hover .bz-avatar-overlay,
.bz-avatar:focus-within .bz-avatar-overlay {
    opacity: 1;
}
.bz-avatar-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--bz-radius-pill);
    border: none;
    background: var(--bz-surface);
    color: var(--bz-color-fg);
    cursor: pointer;
    padding: 0;
}
.bz-avatar-action:hover { background: var(--bz-primary-50); color: var(--bz-primary); }
.bz-avatar-error {
    margin-top: var(--bz-space-1);
    font-size: var(--bz-fs-xs);
    color: var(--bz-color-danger);
}

/* <RecordDocumentAvatar Shape="rect"> — rectangular logo variant (e.g. tenant logo). Square corners,
   object-fit:contain so the whole image shows instead of a cropped/cover circle. Reuses
   .bz-avatar-initials/-overlay/-action; only the hover-reveal trigger needs its own selector. */
.bz-logo-upload {
    position: relative;
    flex: 0 0 auto;
    border-radius: var(--bz-radius-md);
    overflow: hidden;
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    box-shadow: var(--bz-shadow-sm);
}
.bz-logo-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}
.bz-logo-upload:hover .bz-avatar-overlay,
.bz-logo-upload:focus-within .bz-avatar-overlay {
    opacity: 1;
}

/* image-crop.js — JS-owned crop overlay (outside the Blazor render tree, so plain CSS here). */
.bz-cropper-backdrop {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, black 55%, transparent);
}
.bz-cropper-modal {
    background: var(--bz-color-surface);
    border-radius: var(--bz-radius-lg);
    box-shadow: var(--bz-shadow-lg, var(--bz-shadow-sm));
    padding: var(--bz-space-5);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--bz-space-3);
    max-width: 90vw;
}
.bz-cropper-title {
    font-weight: var(--bz-fw-semibold);
    font-size: var(--bz-fs-lg, 18px);
    color: var(--bz-color-fg);
    align-self: flex-start;
}
.bz-cropper-stage {
    position: relative;
    border-radius: var(--bz-radius-pill);
    overflow: hidden;
    background: var(--bz-color-surface-2);
    touch-action: none;
    cursor: grab;
}
.bz-cropper-stage:active { cursor: grabbing; }
/* Shape="rect" (e.g. tenant logo) — rectangular stage, no pill mask. */
.bz-cropper-stage-rect { border-radius: var(--bz-radius-md); }
.bz-cropper-img {
    position: absolute;
    max-width: none;
    pointer-events: none;
    user-select: none;
}
.bz-cropper-hint {
    font-size: var(--bz-fs-xs);
    color: var(--bz-color-fg-subtle);
}
.bz-cropper-controls {
    width: 100%;
    display: flex;
    align-items: center;
}
.bz-cropper-zoom { width: 100%; accent-color: var(--bz-primary); }
.bz-cropper-actions {
    display: flex;
    gap: var(--bz-space-2);
    align-self: flex-end;
}

/* Filter select reusing the toolbar-select look. */
.bz-filter {
    padding: 0.38rem 0.7rem;
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    color: var(--bz-color-fg);
    font-size: var(--bz-fs-sm);
    min-width: 170px;
}

/* Settings field label with info button + status badge. */
.bz-setting-label {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-medium);
    color: var(--bz-color-fg-muted);
    margin-bottom: var(--bz-space-2);
}
.bz-info-btn {
    border: none;
    background: transparent;
    cursor: pointer;
    color: var(--bz-brand-primary);
    padding: 0;
    display: inline-flex;
    align-items: center;
}
.bz-info-btn i { font-size: 16px; }

/* AI-assist panel on the create wizard. */
.bz-ai-panel {
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-lg);
    background: linear-gradient(180deg, rgba(31,95,216,0.06), rgba(31,95,216,0.02));
    padding: var(--bz-space-4);
    margin-bottom: var(--bz-space-4);
}
.bz-ai-head {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    font-weight: var(--bz-fw-semibold);
    color: var(--bz-brand-primary);
    margin-bottom: var(--bz-space-1);
}
.bz-ai-head i { font-size: 18px; }

/* "or" divider between AI assist and the manual form. */
.bz-or {
    display: flex;
    align-items: center;
    text-align: center;
    color: var(--bz-color-fg-subtle);
    font-size: var(--bz-fs-sm);
    margin: var(--bz-space-2) 0 var(--bz-space-4);
}
.bz-or::before, .bz-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--bz-color-border);
}
.bz-or span { padding: 0 var(--bz-space-3); }

/* ============================================================
   Global AI assistant chat widget (bottom-right). Built from --bz-* tokens.
   ============================================================ */
.bz-ai-chat-root { position: fixed; bottom: var(--bz-space-5); right: var(--bz-space-5); z-index: 50; }

/* GitLab: the fixed bottom-right AI chat launcher (56px, collapsed) sits over the footer's
   right-aligned nav (TenantClock) on md+ screens, where the footer is a row with the nav on
   the right. Below md the footer stacks and centers, clear of the widget, so no padding there. */
@media (min-width: 768px) {
    footer.footer .kt-container-fluid {
        padding-right: calc(56px + var(--bz-space-5) * 2);
    }
}

/* Embedded (full-page) variant: same panel, undocked and stretched to fill the host page. */
.bz-ai-chat-root.is-embedded { position: static; z-index: auto; }
.bz-ai-chat-root.is-embedded .bz-ai-chat-panel {
    width: 100%; max-width: none;
    height: calc(100vh - 230px); min-height: 460px; max-height: none;
    animation: none;
}

.bz-ai-chat-launcher {
    width: 56px; height: 56px; border-radius: 50%;
    background: var(--bz-color-accent); color: #fff;
    border: none; cursor: pointer;
    box-shadow: var(--bz-shadow-md);
    font-size: 22px; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    transition: transform var(--bz-duration-fast) var(--bz-ease), filter var(--bz-duration-fast) var(--bz-ease);
}
/* Gated: a touch tap fires :hover and leaves the button stuck lifted until the
   next tap elsewhere. Pointer devices only. */
@media (hover: hover) and (pointer: fine) {
    .bz-ai-chat-launcher:hover { transform: translateY(-2px); filter: brightness(1.05); }
}
.bz-ai-chat-launcher:active { transform: scale(0.97); }

.bz-ai-chat-panel {
    width: 480px; max-width: calc(100vw - 2 * var(--bz-space-4));
    height: 700px; max-height: calc(100vh - 2 * var(--bz-space-5));
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    box-shadow: var(--bz-shadow-md);
    display: flex; flex-direction: column; overflow: hidden;
    /* Anchored to its launcher in the bottom-right corner, so it scales out of the
       launcher rather than out of its own centre. 280ms, not 120ms: this is a
       480x700 panel, and button-press timing on it reads as a snap. */
    transform-origin: bottom right;
    animation: bz-ai-chat-slide-up var(--bz-duration-slow) var(--bz-ease);
    transition: width var(--bz-duration-base) var(--bz-ease), height var(--bz-duration-base) var(--bz-ease);
}
/* Full-screen within the tab/viewport; restore via the header icon. */
.bz-ai-chat-panel.is-maximized {
    position: fixed; inset: var(--bz-space-4);
    width: auto; height: auto; max-width: none; max-height: none;
}
/* The small scale makes it read as emerging FROM the launcher; a pure translate
   reads as an unrelated panel sliding past. Never from scale(0) — nothing in the
   real world appears out of nothing. */
@keyframes bz-ai-chat-slide-up {
    from { opacity: 0; transform: translateY(12px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.bz-ai-chat-head {
    display: flex; align-items: center; gap: var(--bz-space-2);
    padding: var(--bz-space-3) var(--bz-space-4);
    border-bottom: 1px solid var(--bz-color-border);
    background: var(--bz-color-bg);
}
.bz-ai-chat-title { font-weight: 600; flex: 1; min-width: 0; color: var(--bz-color-fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bz-ai-chat-icon-btn {
    border: none; background: transparent; cursor: pointer;
    color: var(--bz-color-fg); padding: 4px; border-radius: var(--bz-radius-sm);
    display: inline-flex; align-items: center; justify-content: center;
}
.bz-ai-chat-icon-btn:hover { background: var(--bz-color-border); }

.bz-ai-chat-body { flex: 1; display: flex; min-height: 0; }
.bz-ai-chat-threads {
    width: 150px; border-right: 1px solid var(--bz-color-border);
    overflow-y: auto; background: var(--bz-color-bg);
}
.bz-ai-chat-threads-empty { padding: var(--bz-space-3); color: var(--bz-color-fg-muted, #64748b); font-size: 13px; }
.bz-ai-chat-thread-item {
    display: flex; align-items: center; gap: 4px;
    padding: var(--bz-space-2) var(--bz-space-3); cursor: pointer;
    border-bottom: 1px solid var(--bz-color-border);
}
.bz-ai-chat-thread-item:hover { background: var(--bz-color-border); }
.bz-ai-chat-thread-item.is-active { background: var(--bz-color-border); font-weight: 600; }
.bz-ai-chat-thread-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.bz-ai-chat-thread-del { opacity: 0; }
.bz-ai-chat-thread-item:hover .bz-ai-chat-thread-del { opacity: 1; }

.bz-ai-chat-main { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* min-height, not 0: the proposals block below may grow to 80% of the column, and a chat surface
   collapsed to nothing looks like the assistant died. */
.bz-ai-chat-surface { flex: 1; min-height: 120px; }

.bz-ai-chat-welcome {
    flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: var(--bz-space-3); text-align: center; padding: var(--bz-space-5); color: var(--bz-color-fg);
}
.bz-ai-chat-welcome-icon { font-size: 36px; color: var(--bz-color-accent); }

/* Review block under the chat. Height is a custom property so the drag handle can override it from
   JS (ai-proposals-resize.js) without a Blazor round-trip per pointermove; the 55% default is a
   share of the panel column, so `is-maximized` scales it up with the panel instead of capping it. */
.bz-ai-chat-proposals {
    /* flex-basis (not height): resolves against the flex column's main size, so the 55% default
       tracks the panel — including `is-maximized` — and a px value from the drag handle overrides it. */
    flex: 0 0 var(--bz-ai-proposals-h, 55%);
    min-height: 132px; /* one collapsed card */
    max-height: 80%;
    display: flex; flex-direction: column;
    border-top: 1px solid var(--bz-color-border);
    background: var(--bz-color-bg);
    overflow: hidden;
}
.bz-ai-chat-proposals-grip {
    flex: 0 0 auto; height: 10px; cursor: ns-resize;
    display: flex; align-items: center; justify-content: center;
    background: var(--bz-color-bg);
    touch-action: none; /* the pointer drag owns the gesture, not the scroller */
}
/* The grippy itself: a short bar, widening slightly on hover so the handle reads as grabbable. */
.bz-ai-chat-proposals-grip::before {
    content: ""; width: 32px; height: 3px; border-radius: var(--bz-radius-pill);
    background: var(--bz-color-border);
    transition: width var(--bz-duration-fast) var(--bz-ease), background-color var(--bz-duration-fast) var(--bz-ease);
}
.bz-ai-chat-proposals-grip:hover::before,
.bz-ai-chat-proposals-grip.is-dragging::before { width: 48px; background: var(--bz-color-accent); }

.bz-ai-chat-proposals-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: var(--bz-space-3); }

.bz-ai-chat-proposal-card {
    border: 1px solid var(--bz-color-border); border-radius: var(--bz-radius-sm);
    margin-bottom: var(--bz-space-3); background: var(--bz-color-surface);
    overflow: hidden;
}
.bz-ai-chat-proposal-head {
    display: flex; align-items: center; gap: 6px; font-weight: 600;
    padding: var(--bz-space-2) var(--bz-space-3);
    border-bottom: 1px solid var(--bz-color-border);
    background: var(--bz-color-bg);
}
.bz-ai-chat-proposal-icon { color: var(--bz-color-accent); }
.bz-ai-chat-proposal-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Items are separated by a rule rather than margin — several staged records in one card otherwise
   read as one long form. */
.bz-ai-chat-proposal-item { padding: var(--bz-space-3); }
.bz-ai-chat-proposal-item + .bz-ai-chat-proposal-item { border-top: 1px solid var(--bz-color-border); }
.bz-ai-chat-proposal-fields { list-style: none; margin: 4px 0 0; padding: 0; font-size: 13px; }
.bz-ai-chat-proposal-fields li { padding: 1px 0; }
.bz-ai-chat-field-name { color: var(--bz-color-fg-muted, #64748b); }
.bz-ai-chat-proposal-validation { color: var(--bz-color-danger, #b91c1c); font-size: 13px; margin: 2px 0; }
/* Sticky bottom bar: approve/reject stay reachable while a long item list scrolls past. */
.bz-ai-chat-proposal-actions {
    position: sticky; bottom: 0; z-index: 1;
    display: flex; align-items: center; gap: var(--bz-space-2); flex-wrap: wrap;
    padding: var(--bz-space-2) var(--bz-space-3);
    border-top: 1px solid var(--bz-color-border);
    background: var(--bz-color-bg);
}
.bz-ai-chat-skip-dup { margin-left: auto; font-size: 13px; }
.bz-ai-chat-proposal-note { color: var(--bz-color-fg-muted, #64748b); font-size: 13px; margin: 0; padding: var(--bz-space-2) var(--bz-space-3); }

/* ── Progress bars (storage migrations / backups live progress) ─────────────── */
.bz-progress { height: 8px; background: var(--bz-surface-3); border-radius: var(--bz-radius-pill); overflow: hidden; }
.bz-progress-fill { height: 100%; background: var(--bz-primary); transition: width var(--bz-duration-slow) var(--bz-ease); }
.bz-progress-fill--indeterminate { width: 40%; animation: bz-indeterminate 1.1s var(--bz-ease-in-out) infinite; }
/* Over 100% of a known capacity (workload board). The bar itself stays clamped at full
   width — the number next to it carries the overshoot; a bar wider than its track lies. */
.bz-progress-fill--over { background: var(--bz-color-danger); }
/* transform, NOT margin-left: margin forces layout -> paint -> composite on every
   frame, and this loop runs for the whole life of every API call in the app.
   translateX is a % of the element's OWN width (40% of the track), so the track's
   -40%..100% margin sweep becomes -100%..250% here. */
@keyframes bz-indeterminate {
    from { transform: translateX(-100%); }
    to   { transform: translateX(250%); }
}

/* Global API activity bar (MainLayout). Pinned above everything so a slow save is visible wherever
   the user has scrolled to; fades out rather than disappearing, so a burst of quick calls does not
   flicker. pointer-events:none — it must never intercept a click. */
.bz-topbar-progress {
    position: fixed;
    top: 0; left: 0; right: 0;
    height: 3px;
    z-index: 2000;
    overflow: hidden;
    background: transparent;
    pointer-events: none;
    opacity: 1;
    transition: opacity .25s ease;
}
.bz-topbar-progress--idle { opacity: 0; }



/* ── Toasts (<ToastHost>, rendered once in MainLayout) ─────────────────────────
   Lives here rather than in a ToastHost.razor.css: AGENTS.md puts cross-cutting
   component classes in shell.css, and Blazor's scoped-CSS rewriter has no defined
   behaviour for the @starting-style at-rule below. */
.bz-toast-host {
    position: fixed;
    right: var(--bz-space-4);
    /* --header-height is set on <body> in App.razor (60px); this tracks the mobile topbar. */
    top: calc(var(--header-height) + var(--bz-space-3));
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    z-index: 2000;
    pointer-events: none;
}

.bz-toast {
    pointer-events: auto;
    min-width: 260px;
    max-width: 420px;
    padding: var(--bz-space-3) var(--bz-space-4);
    background: var(--bz-color-surface);
    color: var(--bz-color-fg);
    border: 1px solid var(--bz-color-border);
    border-left: 4px solid var(--bz-color-accent);
    border-radius: var(--bz-radius-md);
    box-shadow: var(--bz-shadow-md);
    display: flex;
    align-items: center;
    gap: var(--bz-space-3);
    font-size: var(--bz-fs-sm);
    transition: opacity var(--bz-duration-base) var(--bz-ease),
                transform var(--bz-duration-base) var(--bz-ease);
}

/* Enters from above — the direction the stack grows from, so the motion says
   where it came from. Never from scale(0): things do not appear from nothing.
   ponytail: enter only. An exit needs IToastService to hold the item through its
   exit window (a C# change); a toast vanishing is far less jarring than one
   popping into existence, so the enter is where the value is. */
@starting-style {
    .bz-toast { opacity: 0; transform: translateY(-8px) scale(0.97); }
}

.bz-toast-success { border-left-color: var(--bz-color-ok); }
.bz-toast-warning { border-left-color: var(--bz-color-warn); }
.bz-toast-error { border-left-color: var(--bz-color-danger); }

.bz-toast-msg { flex: 1; }

.bz-toast-close {
    background: transparent;
    border: 0;
    color: var(--bz-color-fg-muted);
    font-size: 18px;
    cursor: pointer;
    padding: 0 var(--bz-space-1);
}

.bz-toast-close:hover { color: var(--bz-color-fg); }


/* ── Search-term highlight (record-document search results) ─────────────────── */
/* Built from --bz-color-warn so it flips with the theme: a hard-coded yellow is
   unreadable in dark mode (same trap as .text-muted mapping to a surface token). */
.bz-mark {
    background: color-mix(in srgb, var(--bz-color-warn) 38%, transparent);
    color: inherit;
    border-radius: var(--bz-radius-sm, 3px);
    padding: 0 2px;
    font-weight: 600;
}


/* ── Task boards (/task-management/board) ───────────────────────────────────── */
/* Hand-built columns: no Kanban package is referenced anywhere in the repo and one
   flex row of scrolling columns does not justify adding one. Drag/drop affordances
   (--over on a hovered column, --drop-before as the placeholder, --dragging on the
   held card) are toggled by kanban.js; every colour comes from a --bz-* token so the
   board flips with the theme. */
.bz-kanban {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-3);
    padding: var(--bz-space-3) 0;
    min-height: 12rem;
}

/* One swimlane row. A board without swimlanes renders exactly one of these, so the
   column strip behaves identically whether or not lanes exist. */
.bz-kanban-lane {
    display: flex;
    gap: var(--bz-space-3);
    align-items: flex-start;
    overflow-x: auto;
}

/* A swimlane block = its header + the column strip below it. Nested flex rather than
   one CSS grid: the columns already scroll horizontally as a flex row, and a grid
   would have to re-declare the column track per lane just to stay aligned. */
.bz-kanban-swimlane {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
}

.bz-kanban-lane-head {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    padding: var(--bz-space-2) var(--bz-space-3);
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    /* Sticks to the left edge while the column strip scrolls sideways, so the lane a
       row belongs to never scrolls out of sight. */
    position: sticky;
    left: 0;
}

.bz-kanban-lane-name { font-weight: var(--bz-fw-semibold); }

/* Lanes stack vertically, so a full-height column per lane would push the next lane
   off screen. Each cell gets its own modest scroll instead. */
.bz-kanban-swimlane .bz-kanban-col { max-height: 26rem; }

/* Workload rollup under a column header (Σ hours / capacity + the unestimated badge). */
.bz-kanban-col-load {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-1);
    padding: var(--bz-space-2) var(--bz-space-3);
    border-bottom: 1px solid var(--bz-color-border);
}

.bz-kanban-col-load-text {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    font-size: var(--bz-fs-xs);
    color: var(--bz-color-fg-muted);
}

/* Board selector: which board is open, not a filter — hence pills, not a select. */
.bz-board-pills {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    flex-wrap: wrap;
    padding: var(--bz-space-2) 0;
}

.bz-board-pill {
    padding: var(--bz-space-1) var(--bz-space-3);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-pill);
    background: var(--bz-color-surface);
    color: var(--bz-color-fg);
    font-size: var(--bz-fs-sm);
    cursor: pointer;
}

.bz-board-pill:hover { border-color: var(--bz-primary); }

.bz-board-pill--active {
    background: color-mix(in srgb, var(--bz-primary) 12%, var(--bz-color-surface));
    border-color: var(--bz-primary);
    color: var(--bz-primary);
    font-weight: var(--bz-fw-semibold);
}

.bz-board-pill:disabled { opacity: .6; cursor: default; }

/* Task 289 (GitLab #121): on a phone the pill rows swipe instead of wrapping. Declared
   AFTER the base rule above on purpose — same specificity, so source order decides. */
@media (max-width: 640px) {
    .bz-board-pills {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .bz-board-pills::-webkit-scrollbar { display: none; }

    .bz-board-pill {
        flex: 0 0 auto;
        white-space: nowrap;
    }
}

/* Task 291 (GitLab #129/#131): the phone scrollers above hide their scrollbar, so nothing
   said "there is more to the right". MainLayout.razor.js marks whichever element actually
   overflows with `bz-scroll-more`; these rules turn that into a sticky arrow at the end of a
   swiping row and a one-line hint above a wide grid, and both vanish once the scroller has
   been swiped to its end. Declared as compound selectors so they never shadow the plain
   base pill/lane rules the phone override above depends on. */
.bz-scroll-hint { display: none; }

@media (max-width: 640px) {
    .bz-board-pills.bz-scroll-more::after,
    .bz-kanban-lane.bz-scroll-more::after {
        content: "➜";
        position: sticky;
        inset-inline-end: 0;
        flex: 0 0 auto;
        align-self: center;
        padding: 0 var(--bz-space-2);
        color: var(--bz-primary);
        background: linear-gradient(to right, transparent, var(--bz-color-bg) 40%);
        pointer-events: none;
    }

    /* Which element scrolls depends on the page: inside a `.bz-page-card` it is the card, a
       standalone grid scrolls in its own `.bz-dxgrid` — and a DxGrid with fixed column widths
       scrolls INSIDE itself (`.dxbl-scroll-viewer-content`, the usual case). The hint sits above
       the grid either way, so it is matched as a descendant of the marked scroller, as the
       marked grid's earlier sibling, and as the earlier sibling of the grid that CONTAINS the
       marked viewer. */
    .bz-scroll-more .bz-scroll-hint,
    .bz-scroll-hint:has(~ .bz-dxgrid.bz-scroll-more),
    .bz-scroll-hint:has(~ .bz-dxgrid .bz-scroll-more) {
        display: block;
        position: sticky;
        inset-inline-start: 0;
        font-size: var(--bz-fs-xs);
        color: var(--bz-color-fg-muted);
        padding: var(--bz-space-1) var(--bz-space-3);
    }
}

.bz-kanban-col {
    display: flex;
    flex-direction: column;
    flex: 0 0 20rem;
    max-height: calc(100vh - 19rem);
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-lg);
}

.bz-kanban-col--over {
    border-color: var(--bz-primary);
    background: color-mix(in srgb, var(--bz-primary) 8%, var(--bz-color-surface-2));
}

/* A collapsed column keeps its header (name + count) so nothing disappears silently;
   it just stops taking a full column's width. */
.bz-kanban-col--collapsed { flex: 0 0 14rem; }

.bz-kanban-col-toggle {
    border: 0;
    background: none;
    color: var(--bz-color-fg-muted);
    padding: 0;
    line-height: 1;
    cursor: pointer;
    flex: 0 0 auto;
}

.bz-kanban-col-toggle:hover { color: var(--bz-primary); }

.bz-kanban-col-head {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    padding: var(--bz-space-3);
    border-bottom: 1px solid var(--bz-color-border);
}

.bz-kanban-col-name {
    font-weight: var(--bz-fw-semibold);
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bz-kanban-dot {
    /* Explicit, not inherited from a flex parent: inside a DxGrid cell the dot is a lone inline
       span, and an inline box takes no width/height at all — the Color columns rendered empty. */
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: var(--bz-radius-pill);
    flex: 0 0 auto;
}

/* Only the card list scrolls, so the header and the "new task" footer stay put. */
.bz-kanban-list {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    padding: var(--bz-space-2);
    overflow-y: auto;
    flex: 1;
    min-height: 3rem;
}

.bz-kanban-col-foot {
    padding: var(--bz-space-2);
    border-top: 1px solid var(--bz-color-border);
}

.bz-kanban-card {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    padding: var(--bz-space-3);
    background: var(--bz-color-surface);
    border: 1px solid var(--bz-color-border);
    border-left: 3px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    cursor: pointer;
}

.bz-kanban-card:hover { border-color: var(--bz-primary); }

.bz-kanban-card--dragging { opacity: .45; }

/* Drop placeholder: the card the dragged one would land above. */
.bz-kanban-card--drop-before { box-shadow: 0 -3px 0 0 var(--bz-primary); }

.bz-kanban-card-top {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    justify-content: space-between;
}

.bz-kanban-card-no {
    font-size: var(--bz-fs-xs);
    color: var(--bz-color-fg-muted);
}

.bz-kanban-card-title { line-height: 1.35; }

.bz-kanban-card-foot {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    flex-wrap: wrap;
}

/* ── Comment @mentions (task comments) ──────────────────────────────────────── */
.bz-mention {
    background: color-mix(in srgb, var(--bz-primary) 16%, transparent);
    color: var(--bz-primary);
    border-radius: var(--bz-radius-sm);
    padding: 0 2px;
    font-weight: var(--bz-fw-medium);
}

/* Autocomplete list under the comment box. Not a popup: it sits in flow so it never
   needs positioning JS and can never be clipped by the tab panel's overflow. */
.bz-mention-list {
    display: flex;
    flex-direction: column;
    margin-top: var(--bz-space-1);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    background: var(--bz-color-surface);
    max-height: 14rem;
    overflow-y: auto;
}

.bz-mention-item {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    padding: var(--bz-space-2) var(--bz-space-3);
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    color: inherit;
}

.bz-mention-item:hover { background: var(--bz-color-surface-2); }

/* ── Aufgabenplan workspace (task plan pin viewer) ──────────────────────────── */
/* Split layout: pin list on the left, pdf.js page + marker overlay on the right.
   CSS grid rather than the shell's row/col helpers, which this app does not ship. */
.bz-plan-layout {
    display: grid;
    grid-template-columns: minmax(18rem, 22rem) minmax(0, 1fr);
    gap: var(--bz-space-4);
    align-items: start;
}

@media (max-width: 1100px) {
    .bz-plan-layout { grid-template-columns: minmax(0, 1fr); }
}

.bz-plan-side {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    max-height: 78vh;
    overflow-y: auto;
}

/* Scroll box around the page canvas — a zoomed-in plan is meant to pan, not to stretch the page.
   Deliberately NOT a flex container: the viewer measures its own element's clientWidth to compute the
   fit-width scale, and a flex item shrinks to its content (i.e. to zero before the first render). */
.bz-plan-stage {
    overflow: auto;
    max-height: 78vh;
    background: var(--bz-color-surface-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    padding: var(--bz-space-3);
}

/* Centred while it fits, overflowing (and scrollable) once zoomed past the stage. */
.bz-plan-canvas {
    position: relative;
    display: block;
    margin: 0 auto;
    line-height: 0;
    box-shadow: 0 1px 6px rgb(0 0 0 / 18%);
    cursor: crosshair;
    touch-action: none; /* pointer events own the drag; the browser must not pan instead */
}

.bz-plan-canvas.is-dragging { cursor: grabbing; }

.bz-plan-page { display: block; }

/* The overlay never takes pointer events — the wrapper does all hit-testing. */
.bz-plan-markers {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.bz-plan-pin {
    display: flex;
    align-items: flex-start;
    gap: var(--bz-space-2);
    width: 100%;
    padding: var(--bz-space-2);
    border: 1px solid var(--bz-color-border);
    border-radius: var(--bz-radius-md);
    background: var(--bz-color-surface);
    text-align: left;
    cursor: pointer;
    color: inherit;
}

.bz-plan-pin:hover { background: var(--bz-color-surface-2); }

.bz-plan-pin.is-active {
    border-color: var(--bz-primary);
    box-shadow: 0 0 0 1px var(--bz-primary);
}

.bz-plan-pin-badge {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.65rem;
    height: 1.65rem;
    border-radius: 999px;
    font-size: var(--bz-fs-sm);
    font-weight: var(--bz-fw-semibold);
}

.bz-plan-pin-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}

.bz-plan-pin-title {
    line-height: 1.3;
    overflow-wrap: anywhere;
}

/* ── Calendar availability strip (Phase 2) ─────────────────────────────────
   One track per attendee across a fixed 07:00–20:00 working window; blocks are
   positioned as percentages by the component, so the track needs no grid. */
.bz-availability { margin-top: var(--bz-space-3); }

.bz-availability-row {
    display: flex;
    align-items: center;
    gap: var(--bz-space-2);
    margin-bottom: var(--bz-space-1);
}

.bz-availability-name {
    flex: 0 0 10rem;
    font-size: var(--bz-fs-sm);
    color: var(--bz-color-fg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bz-availability-track {
    position: relative;
    display: block;
    flex: 1 1 auto;
    height: 1.25rem;
    border-radius: var(--bz-radius-sm);
    background: var(--bz-color-surface-2);
}

.bz-availability-block {
    position: absolute;
    top: 0;
    bottom: 0;
    border-radius: var(--bz-radius-sm);
}

.bz-availability-busy { background: var(--bz-color-accent); }
.bz-availability-tentative { background: var(--bz-color-warn); opacity: .6; }
.bz-availability-oof { background: var(--bz-color-danger); }

/* Ranked "Suggest times" results, rendered under the availability strip. */
.bz-availability-suggestions {
    display: flex;
    flex-direction: column;
    gap: var(--bz-space-2);
    margin-top: var(--bz-space-3);
    padding-top: var(--bz-space-3);
    border-top: 1px solid var(--bz-color-border);
}

/* ── Appointment editor: form + day overview (work items #52, #53) ──────────
   Two columns inside the popup body; the day panel is advisory context, so it
   is the half that goes away on a narrow viewport rather than wrapping under
   a form the user is already scrolling. */
.bz-editor-cols {
    display: flex;
    align-items: flex-start;
    gap: var(--bz-space-4);
}

.bz-editor-main { flex: 1 1 auto; min-width: 0; }

.bz-day-panel {
    flex: 0 0 15rem;
    padding: var(--bz-space-3);
    border-left: 1px solid var(--bz-color-border);
}

/* Vertical twin of .bz-availability-track: the same fixed 07:00–20:00 window,
   with blocks positioned top/height as percentages by the component. */
.bz-day-track {
    position: relative;
    height: 520px;
    margin-top: var(--bz-space-2);
    border-radius: var(--bz-radius-sm);
    background: var(--bz-color-surface-2);
}

.bz-day-tick {
    position: absolute;
    left: 0;
    font-size: var(--bz-fs-xs);
    color: var(--bz-color-fg-muted);
    border-top: 1px solid var(--bz-color-border);
    width: 100%;
    padding-left: 2px;
}

.bz-day-block {
    position: absolute;
    left: 2.75rem;
    right: var(--bz-space-1);
    overflow: hidden;
    padding: 0 var(--bz-space-1);
    border-radius: var(--bz-radius-sm);
    font-size: var(--bz-fs-xs);
    line-height: 1.1rem;
    color: #fff;
    background: var(--bz-color-accent);
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* The unsaved draft: outlined rather than filled, so it never reads as an
   existing commitment on the day. */
.bz-day-draft {
    background: transparent;
    color: var(--bz-color-fg);
    border: 2px dashed var(--bz-color-accent);
}

/* Date + time as two editors over one value; the time half stays narrow. */
.bz-datetime-row {
    display: flex;
    gap: var(--bz-space-2);
}

.bz-time-edit { flex: 0 0 7rem; }

/* One break row: start / end / duration, each column the same width as the label above it so the
   header line actually sits over the field it names. */
.bz-break-col {
    flex: 0 0 7rem;
    display: inline-block;
}

@media (max-width: 991px) {
    .bz-day-panel { display: none; }
}

/* ── Resource booking blocks (Calendar Phase 4) ──────────────────────────────
   A PENDING booking is not holding the slot yet: it renders hollow (dashed
   outline, transparent fill) so the grid never claims a resource is taken when
   any other request can still win it. An approved/no-approval booking is a
   solid block, i.e. the scheduler's own default. */
.bz-res-block {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0 var(--bz-space-1);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bz-res-block-pending {
    background: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 4px,
        var(--bz-color-surface-2) 4px,
        var(--bz-color-surface-2) 8px);
    border: 1px dashed var(--bz-color-warn);
    border-radius: var(--bz-radius-sm);
    color: var(--bz-color-fg-muted);
    font-style: italic;
}

/* ── Breadcrumbs (#30) ────────────────────────────────────────────────────
   Moved here from Web.Common/Layout/Breadcrumbs.razor.css: the same bz-breadcrumb* classes are also
   emitted by PageHeader.razor and BreadcrumbItem.razor, two different components whose scoped-CSS
   `[b-xxx]` attribute never matches Breadcrumbs.razor's own scope — so only Breadcrumbs.razor itself
   ever picked up the styling. Global rules here cover all three emitters.
   Visual styling matches the Metronic Demo10 inline breadcrumb (small, secondary color, muted
   separators). */
.bz-breadcrumbs {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.875rem;
    line-height: 1.25rem;
    font-weight: 400;
    flex-wrap: wrap;
}

.bz-breadcrumb-item {
    color: var(--bz-color-fg-muted, #475569);
    text-decoration: none;
    transition: color var(--bz-duration-fast, 120ms) ease;
}

.bz-breadcrumb-item:hover {
    color: var(--bz-brand-primary, #1f5fd8);
}

.bz-breadcrumb-active {
    color: var(--bz-color-fg, #0f172a);
    font-weight: 500;
    cursor: default;
}

.bz-breadcrumb-active:hover {
    color: var(--bz-color-fg, #0f172a);
}

.bz-breadcrumb-sep {
    color: var(--bz-color-fg-subtle, #64748b);
    user-select: none;
}

/* ── Print (GitLab #66) ─────────────────────────────────────────────────────
   Hide the shell chrome so any page — the calendar print view, AddressListPrint,
   the CommercialOperations print preview, or a plain Ctrl+P — prints content only.
   Selectors come from Layouts/MainLayout.razor: #header (mobile header), #sidebar
   (the aside), footer.footer, the global activity bar and the AI chat widget. The
   main column indents by var(--sidebar-width), so it is zeroed alongside. Overflow
   containers clip to one page when printing, hence the visible/auto overrides. */
@media print {
    #header,
    #sidebar,
    footer.footer,
    .bz-topbar-progress,
    .bz-ai-chat-root,
    .bz-bottom-nav,
    .bz-quick-fab-wrap {
        display: none !important;
    }

    :root {
        --sidebar-width: 0px;
    }

    #scrollable_content {
        overflow: visible !important;
        height: auto !important;
    }
}

/* ── Phone shell: header toggle, bottom navigation, quick-actions FAB (GitLab #107) ──────────
   Everything here is scoped to below Tailwind's lg breakpoint (1024px), which is exactly where
   the sidebar turns into a KTUI drawer. Written as bz-* classes on --bz-* tokens because the
   compiled Metronic/Tailwind CSS is committed and not rebuilt on every change. */

/* The mobile header sits on bg-mono (light) / bg-background (dark) — both dark surfaces — so its
   contents are always light. Without this BrandMark's <strong> inherited the dark foreground and
   the brand name was invisible against the black bar. */
.bz-mobile-header { color: #fff; }
.bz-mobile-header strong,
.bz-mobile-header a { color: inherit; }
/* GitLab #111: a real tenant display name is long ("Anawaz Bau- und Sanierungs GmbH"). Let it
   truncate on one line instead of wrapping the header (flex-wrap) onto a second row. */
.bz-mobile-header .brand { min-width: 0; }
.bz-mobile-header .brand strong {
    display: block;
    max-width: calc(100vw - 96px);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 44px touch target, leading the header. */
.bz-mobile-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-inline-start: -10px;
    border: none;
    border-radius: var(--bz-radius-md);
    background: transparent;
    color: inherit;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}
.bz-mobile-nav-toggle:active { background: rgba(255, 255, 255, .14); }

/* Blocks that only exist on a phone (the Home "My Day" digest), and their counterpart:
   blocks that only exist on a desktop (task 286 — the calendar's own ← Today → row, which
   moves into the page header on a phone). Neither carries a `display` of its own outside the
   breakpoint, so a page may style the INNER element freely without out-specifying the
   utility — put the class on a wrapper, never on the element you are laying out. */
.bz-mobile-only { display: none; }

/* Same pair, for a wrapper whose CHILDREN have to stay in the parent's flex row:
   `display: contents` makes the wrapper itself disappear from layout, which
   `bz-mobile-only`'s `display: block` cannot do — a block inside a header action
   row would break the row instead of joining it (task 289 — GitLab #121). */
.bz-mobile-contents { display: none; }
.bz-desktop-contents { display: contents; }

.bz-bottom-nav,
.bz-quick-fab-wrap { display: none; }

@media (max-width: 1023.98px) {
    :root { --bz-mobile-nav-h: 64px; }

    .bz-mobile-only { display: block; }
    .bz-desktop-only { display: none; }
    .bz-mobile-contents { display: contents; }
    .bz-desktop-contents { display: none; }

    /* The saved-views bar is a label plus "no saved views yet" until the user saves
       one — a whole row of nothing above the board. Its Save action moves into the
       page header on a phone, so the empty bar has nothing left to show. */
    .bz-views-bar--empty { display: none; }

    .bz-bottom-nav {
        position: fixed;
        inset-inline: 0;
        bottom: 0;
        /* Below the drawer (100) and its backdrop (99), above page content. */
        z-index: 30;
        display: flex;
        align-items: stretch;
        background: var(--bz-color-surface);
        border-top: 1px solid var(--bz-color-border);
        box-shadow: 0 -1px 3px rgba(0, 0, 0, .08);
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }

    .bz-bottom-nav-item {
        flex: 1 1 0;
        min-width: 0;
        min-height: 44px;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        padding: var(--bz-space-2) 2px;
        border: none;
        background: transparent;
        color: var(--bz-color-fg-muted);
        font-size: var(--bz-fs-xs);
        text-decoration: none;
        cursor: pointer;
    }
    .bz-bottom-nav-item i { font-size: 20px; line-height: 1; }
    .bz-bottom-nav-item span {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .bz-bottom-nav-item.is-active { color: var(--bz-color-accent); }
    .bz-bottom-nav-item:active { background: var(--bz-color-surface-2); }

    /* Keep the last rows of any page reachable above the bar, and lift the floating chrome. */
    #scrollable_content { padding-bottom: var(--bz-mobile-nav-h); }
    .bz-ai-chat-root { bottom: calc(var(--bz-mobile-nav-h) + var(--bz-space-3)); }
    .bz-toast-host { top: calc(var(--header-height) + var(--bz-space-3)); }

    /* The bar's AI item replaces the launcher bubble on a phone (the open panel is a sibling and
       is unaffected), which also frees the corner for the quick-actions FAB. */
    .bz-ai-chat-launcher { display: none; }

    /* GitLab #110: the docked panel was sized in vh while its root had been lifted by the bottom
       bar, so `top` resolved NEGATIVE on a phone (-28px at 375x667, worse once the URL bar shows)
       and the header controls were clipped with nowhere to scroll. On a phone the panel is simply
       the screen: no vh anywhere, so the URL bar and the software keyboard (viewport meta carries
       interactive-widget=resizes-content) resize the layout viewport and the panel with it.
       Excludes the embedded SmartEntry variant, which is laid out by its host page. */
    .bz-ai-chat-root:not(.is-embedded) .bz-ai-chat-panel {
        position: fixed;
        inset: 0;
        width: auto;
        height: auto;
        max-width: none;
        max-height: none;
        border: 0;
        border-radius: 0;
        padding-top: env(safe-area-inset-top, 0px);
        padding-bottom: env(safe-area-inset-bottom, 0px);
        animation: none;
    }
    /* Already full-screen — the maximize toggle has nothing left to do. */
    .bz-ai-chat-root:not(.is-embedded) [data-testid="ai-chat-maximize"] { display: none; }
    /* The head must never be squeezed by the message list, and the DevExpress surface must be
       allowed to shrink so ITS list is the scroller (desktop keeps the 120px floor). */
    .bz-ai-chat-root:not(.is-embedded) .bz-ai-chat-head { flex: 0 0 auto; }
    .bz-ai-chat-root:not(.is-embedded) .bz-ai-chat-surface { min-height: 0; }

    .bz-quick-fab-wrap {
        position: fixed;
        right: var(--bz-space-4);
        bottom: calc(var(--bz-mobile-nav-h) + var(--bz-space-3));
        z-index: 31;
        display: flex;
        flex-direction: column;
        align-items: flex-end;
        gap: var(--bz-space-2);
    }

    .bz-quick-fab {
        width: 56px;
        height: 56px;
        border-radius: 50%;
        border: none;
        background: var(--bz-color-accent);
        color: var(--bz-color-accent-fg);
        box-shadow: var(--bz-shadow-md);
        font-size: 22px;
        line-height: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        cursor: pointer;
    }
    .bz-quick-fab:active { transform: scale(.97); }

    .bz-quick-sheet {
        display: flex;
        flex-direction: column;
        min-width: 200px;
        max-width: calc(100vw - var(--bz-space-4) * 2);
        padding: var(--bz-space-2);
        background: var(--bz-color-surface);
        border: 1px solid var(--bz-color-border);
        border-radius: var(--bz-radius-md);
        box-shadow: var(--bz-shadow-md);
    }

    .bz-quick-sheet-item {
        display: flex;
        align-items: center;
        gap: var(--bz-space-3);
        min-height: 44px;
        padding: var(--bz-space-2) var(--bz-space-3);
        border-radius: var(--bz-radius-sm);
        color: var(--bz-color-fg);
        font-size: var(--bz-fs-sm);
        font-weight: var(--bz-fw-medium);
        text-decoration: none;
    }
    .bz-quick-sheet-item:active { background: var(--bz-color-surface-2); }
}
