/* =============================================================
   app.css
   -------------------------------------------------------------
   Component styles — tokens live in wwwroot/css/theme.css and are
   imported below. Every color on this page is expected to flow
   from a var(--token). Hard-coded hex values that remain are
   either (a) locked status colors (mapped via tokens), or
   (b) known pre-theme holdovers being swept incrementally.
   ============================================================= */
@import url("css/theme.css");
@import url("css/app-chrome.css");

html, body {
    margin: 0;
    min-height: 100%;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
}

a, .btn-link {
    color: var(--accent-strong);
}

code {
    background: rgba(19, 29, 48, 0.06);
    border-radius: 0.5rem;
    padding: 0.15rem 0.4rem;
    color: #253349;
}

.top-row {
    display: flex;
    min-height: 4.5rem;
    background: rgba(255, 249, 241, 0.66);
    border-bottom: 1px solid rgba(12, 21, 36, 0.06);
    backdrop-filter: blur(18px);
}

.content {
    padding: 2rem 0 3rem;
}

.page-stack {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}

.bid-workspace-page {
    gap: 0.75rem;
}

.hero-panel,
.surface-card,
.metric-card {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 1rem;
    background: var(--surface);
    box-shadow: var(--shadow);
}

.hero-panel {
    display: grid;
    gap: 1.25rem;
    padding: 1.2rem;
    background: var(--surface);
    color: var(--text);
}

.hero-panel.compact {
    background: var(--surface);
}

.hero-panel::after {
    display: none;
}

.eyebrow {
    margin: 0 0 0.65rem;
    font-size: 0.78rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--accent-strong);
}

.hero-panel h1,
.surface-card h2,
.surface-card h3 {
    margin: 0;
    font-family: inherit;
}

/* PBI #15260 — bare h2/h3 inside .surface-card had no explicit
   font-size so they inherited Bootstrap's defaults (h2 = 2rem,
   h3 = 1.75rem), which read as page-headline weight against the
   card's body copy. Lock card-internal headings to a uniform
   1rem so the visual hierarchy is "card title sits at body
   weight, +bold" — consistent with the rest of the design
   system (cf. .purchase-orders-detail-header h2 / .linked-products-header h2).
   Pages that want a louder card heading already opt into
   .app-headline explicitly. */
.surface-card h2,
.surface-card h3 {
    font-size: 1rem;
    line-height: 1.25;
}

.hero-copy {
    max-width: 56rem;
    margin: 1rem 0 0;
    line-height: 1.6;
    color: var(--text-muted);
}

.hero-meta,
.pill-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.65rem;
}

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    border-radius: 999px;
    padding: 0.3rem 0.6rem;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.1;
}

.status-pill.success {
    background: var(--status-success-soft);
    color: var(--status-success);
    border: 1px solid color-mix(in oklab, var(--status-success) 22%, transparent);
}

.status-pill.warning {
    background: var(--status-warning-soft);
    color: var(--status-warning);
    border: 1px solid color-mix(in oklab, var(--status-warning) 25%, transparent);
}

.status-pill.danger,
.status-pill.error {
    background: var(--status-danger-soft);
    color: var(--status-danger);
    border: 1px solid color-mix(in oklab, var(--status-danger) 25%, transparent);
}

.status-pill.neutral {
    background: var(--surface-sunken);
    color: var(--text-muted);
    border: 1px solid var(--border);
}

/* PBI #15271 — every page-level "_statusMessage" surface migrated to
   the app-wide ToastService (<AppToast /> in MainLayout). What's left
   that still uses .status-banner is inline contextual alerts tied to
   a specific UI section: paste-import errors, AI-enrich errors, share
   errors, payment-plan errors, the BidDiagram RoomRig empty-state,
   the auth-state messages on Login/LoginReset/Contact/AboutEula, and
   the Bids access-denied chip. Those are persistent panel-level
   indicators (not transient one-off events), so they kept the class.
   Don't reach for .status-banner from new code — use ToastService for
   transient feedback. */
.status-banner {
    border-radius: var(--radius-md);
    padding: 0.9rem 1rem;
    font-weight: 600;
    margin-bottom: 1rem;
    border: 1px solid transparent;
}

/* Helper / instructional prose — annotated with .app-help-text to
   opt into the global hide/show toggle. The gear-menu "Hide helper
   text" option stamps data-help="hide" on <html> (via
   wwwroot/js/help-visibility.js); pros get a quieter UI, newbies
   keep the guide rails. The rule is display:none rather than
   visibility:hidden so layout reclaims the space. */
html[data-help="hide"] .app-help-text {
    display: none !important;
}


.status-banner.success {
    background: var(--status-success-soft);
    color: var(--status-success);
    border-color: color-mix(in oklab, var(--status-success) 25%, transparent);
}

/* .inventory-page no longer overrides the page-stack gap — that
   half-rem shrink caused the tab-strip around the Products-area
   sub-nav to jump when navigating from Products (1.5rem) to
   Inventory (was 1rem). Inventory now uses the default 1.5rem so
   the visual rhythm matches Products / Manufacturers / Suppliers.
   Exports keeps its override because it has its own (tighter) flow. */
.export-page {
    gap: 1rem;
}

/* Preview page Option-C quick-controls card — the compact top row of
   the six knobs users actually change per bid (Style · Phase ·
   Selection · Opening · Closing · Include cover). Everything else
   lives in the Advanced options <details> near the bottom of the
   page. Grid auto-fits so it wraps nicely on narrower viewports. */
.preview-quick-controls-card {
    padding: 0.75rem 1rem;
}

.preview-quick-grid {
    display: grid;
    /* Six-column base so each cell gets a predictable slice — Style
       and Phase were hogging space before because auto-fit let them
       expand to fill, which squeezed the 3-button Selection control
       into a wrapped mess. Explicit columns keep widths balanced. */
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 0.75rem;
    align-items: end;
}

/* Selection (3 segmented buttons) needs enough width for all three
   labels — let it take 2 columns so "Client bids + summary" doesn't
   wrap. */
.preview-quick-grid .preview-scope-strip {
    grid-column: span 2;
}

/* On narrower viewports (below ~960px) collapse to 2 columns so the
   dropdowns don't go microscopic. Selection stretches full-row on
   phone-ish widths. */
@media (max-width: 1024px) {
    .preview-quick-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .preview-quick-grid .preview-scope-strip {
        grid-column: 1 / -1;
    }
}

.preview-quick-grid > label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0; /* lets <select> shrink instead of overflowing the cell */
}

.preview-quick-grid .preview-inline-choice {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 0;
}

.inventory-toolbar-card,
.export-setup-card {
    padding: 0.9rem 1rem;
}

.inventory-toolbar-strip {
    align-items: end;
}

.inventory-tab-strip {
    margin-top: 0.8rem;
}

.inventory-editor-card {
    display: grid;
    gap: 1rem;
}

.inventory-editor-header {
    display: flex;
    align-items: start;
    justify-content: space-between;
    gap: 1rem;
}

.inventory-editor-header h2,
.export-setup-card h2 {
    margin: 0.15rem 0 0;
    font-size: 1.15rem;
    letter-spacing: -0.02em;
}

.inventory-editor-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.85rem;
}

.inventory-editor-grid > label {
    display: grid;
    gap: 0.35rem;
}

.inventory-editor-grid__wide {
    grid-column: span 2;
}

.inventory-grid .e-gridcontent td,
.inventory-grid .e-headercell {
    font-size: 0.83rem;
}

.inventory-grid .e-rowcell,
.inventory-grid .e-headercell {
    padding-block: 0.55rem;
}

.export-columns-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 0.55rem 1rem;
}

.export-preview-table-wrap {
    overflow: auto;
}

.export-preview-table th,
.export-preview-table td {
    white-space: nowrap;
}

@media (max-width: 1024px) {
    .inventory-editor-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .inventory-editor-grid__wide {
        grid-column: span 2;
    }
}

@media (max-width: 640px) {
    .inventory-editor-grid {
        grid-template-columns: 1fr;
    }

    .inventory-editor-grid__wide {
        grid-column: span 1;
    }

    .inventory-editor-header {
        flex-direction: column;
    }
}

.status-banner.warning {
    background: var(--status-warning-soft);
    color: var(--status-warning);
    border-color: color-mix(in oklab, var(--status-warning) 25%, transparent);
}

.status-banner.error,
.status-banner.danger {
    background: var(--status-danger-soft);
    color: var(--status-danger);
    border-color: color-mix(in oklab, var(--status-danger) 25%, transparent);
}

.toolbar-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin: 0.45rem 0 0.7rem;
}

.toolbar-row--tight {
    gap: 0.4rem;
    margin-top: 0.4rem;
}

.workspace-filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: end;
    margin: 0 0 0.6rem;
}

.workspace-filter-search {
    min-width: 22rem;
    flex: 1 1 28rem;
}

.compact-toolbar {
    margin: 0;
}

.metric-grid,
.surface-grid,
.queue-list {
    display: grid;
    gap: 1rem;
}

.metric-grid {
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.metric-card {
    padding: 1.2rem;
    background: var(--surface);
}

.metric-label,
.flag-label,
.settings-field > span,
.legacy-filter-field > span {
    display: block;
    color: var(--text-muted);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

/* PBI #15241 — required-field indicator. Renders a red asterisk
   after the label text so the operator knows a field is required
   BEFORE submit. Pair with aria-required="true" on the input. */
.flag-label--required::after {
    content: " *";
    color: var(--status-danger);
    font-weight: 700;
}

/* PBI #15241 — inline per-field error message. Replaces the
   post-submit-only banner pattern for required-field failures so
   the error renders next to the offending input, not only at the
   top of the page. */
.field-error {
    display: block;
    margin-top: 0.3rem;
    color: var(--status-danger);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.metric-card strong {
    display: block;
    margin: 0.55rem 0 0.45rem;
    font-size: 2rem;
    color: var(--text);
}

.metric-card p,
.surface-card li,
.surface-card td,
.surface-card th,
.surface-card span,
.surface-card p {
    line-height: 1.6;
}

.surface-grid {
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}

.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.65rem;
    margin-top: 1rem;
}

.filter-chip {
    border: 1px solid rgba(12, 21, 36, 0.12);
    background: rgba(255, 250, 244, 0.88);
    color: var(--text);
    border-radius: 999px;
    padding: 0.5rem 0.85rem;
    cursor: pointer;
    font: inherit;
}

.filter-chip.active {
    background: rgba(216, 136, 54, 0.12);
    border-color: rgba(216, 136, 54, 0.38);
    color: var(--accent-strong);
}

.surface-card {
    padding: 1rem;
}

.legacy-page-toolbar {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.legacy-action-btn {
    min-height: 2.5rem;
    padding: 0.55rem 0.9rem;
    border: 1px solid #d0d5dd;
    border-radius: 0.75rem;
    background: #ffffff;
    color: #344054;
    font: inherit;
    font-weight: 600;
}

.legacy-action-btn.green {
    background: #eff8ff;
    color: #175cd3;
}

.legacy-action-btn.red {
    background: #fef3f2;
    color: #b42318;
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--field-min-md), 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

/* PBI #15247 — records variant for Address/City/State/Zip-style
   forms that live inside narrower wrapper panels (legacy
   two-up / three-up). Using a smaller floor keeps the column
   count consistent (~2 columns) across Client, Company, and
   BidDetail Jobsite instead of collapsing to 1 column at 220 px
   or stretching to 1 at 180 px. */
.form-grid--records {
    grid-template-columns: repeat(auto-fit, minmax(var(--field-min-sm), 1fr));
}

.compact-form-grid {
    gap: 0.72rem 0.85rem;
    margin-top: 0.75rem;
}

.compact-form-grid .form-control {
    min-height: 2.05rem;
    padding-top: 0.38rem;
    padding-bottom: 0.38rem;
}

.compact-form-grid .form-dropdown {
    width: 100%;
}

.compact-form-grid .form-dropdown.e-control-wrapper,
.compact-form-grid .form-dropdown.e-multicolumn-list {
    width: 100%;
}

.compact-form-grid .form-dropdown .e-input-group,
.compact-form-grid .form-dropdown .e-ddl,
.compact-form-grid .form-dropdown .e-control.e-dropdownlist,
.compact-form-grid .form-dropdown .e-control.e-combobox {
    min-height: 2.05rem;
}

.compact-form-grid .form-dropdown input {
    min-height: 2.05rem;
}

.notes-block {
    display: block;
    margin-top: 1rem;
}

.legacy-shell-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.75rem;
}

.catalog-header-card {
    padding: 0.55rem 0.75rem 0.6rem;
}

.catalog-header-card .workspace-title-row {
    gap: 0.45rem;
}

.catalog-header-card h1 {
    margin: 0;
    font-size: 1.02rem;
    line-height: 1.05;
}

.catalog-header-card .workspace-meta {
    gap: 0.35rem;
}

.catalog-header-card .workspace-meta-pill {
    padding: 0.15rem 0.42rem;
    font-size: 0.7rem;
}

.catalog-header-card .workspace-client-inline {
    font-size: 0.75rem;
}

.compact-workspace-actions {
    align-items: flex-start;
    gap: 0.4rem;
}

.compact-workspace-actions .btn {
    min-height: 1.95rem;
    padding: 0.3rem 0.7rem;
    font-size: 0.78rem;
}

.catalog-input-warning {
    background: #fef2f2;
    border-color: rgba(220, 38, 38, 0.25);
}

.workspace-header-card {
    padding: 0.6rem 0.8rem 0.55rem;
}

.company-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(20rem, 0.9fr);
    gap: 1rem;
}

.company-form-panel,
.company-side-panel {
    padding: 1rem;
}

.company-form-panel h2,
.company-side-panel h2 {
    margin: 0;
}

.company-form-grid,
.preview-summary-grid {
    margin-top: 1rem;
}

.company-form-grid__wide {
    grid-column: 1 / -1;
}

.company-image-shell {
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    padding: 0.85rem;
    box-shadow: var(--shadow-sm);
}

.company-image {
    width: 100%;
    max-height: 14rem;
    object-fit: contain;
    display: block;
}

.company-preview-stack {
    display: grid;
    gap: 1rem;
    margin-top: 1rem;
}

.company-preview-stack p,
.preview-summary-grid p,
.preview-room-header span {
    margin: 0.15rem 0 0;
    color: var(--text-muted);
}

.rooms-layout {
    align-items: start;
}

.room-stat-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem;
    margin-top: 0.35rem;
}

.room-stat-grid .metric-card {
    padding: 0.9rem;
}

.preview-layout {
    align-items: start;
}

.preview-summary-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem 1.25rem;
}

.preview-metric-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.preview-room-section {
    display: grid;
    gap: 0.65rem;
    margin-top: 1rem;
}

.preview-room-section:first-of-type {
    margin-top: 0.75rem;
}

.preview-room-header {
    display: flex;
    justify-content: space-between;
    gap: 0.8rem;
    align-items: center;
}

.preview-room-header strong {
    font-size: 0.98rem;
    color: var(--text);
}

.preview-table td.numeric,
.preview-table th.numeric {
    text-align: right;
}

.purchase-orders-page {
    gap: 0.85rem;
}

.purchase-orders-toolbar-card,
.purchase-orders-grid-card,
.purchase-orders-detail-card {
    padding: 0.8rem;
}

.purchase-orders-command-strip {
    display: grid;
    gap: 0.7rem;
}

.purchase-orders-quick-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.purchase-orders-filter-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.65rem;
    align-items: end;
}

.purchase-orders-filter-field--search {
    min-width: 20rem;
    flex: 1 1 24rem;
}

.purchase-orders-go {
    min-width: 3.3rem;
}

.purchase-orders-view-toggle {
    display: inline-flex;
    gap: 0.4rem;
}

.purchase-orders-headline-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.purchase-orders-headline-row .app-headline {
    margin: 0;
}

.purchase-orders-row-menu {
    position: relative;
    display: inline-block;
}

.purchase-orders-row-menu > summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
    line-height: 1;
    min-width: 1.8rem;
    text-align: center;
}

.purchase-orders-row-menu > summary::-webkit-details-marker {
    display: none;
}

.purchase-orders-row-menu__panel {
    position: absolute;
    top: calc(100% + 0.25rem);
    right: 0;
    z-index: 20;
    min-width: 12rem;
    background: var(--surface, #fff);
    border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
    border-radius: 0.35rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
    padding: 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.purchase-orders-row-menu__item {
    background: transparent;
    border: 0;
    text-align: left;
    padding: 0.4rem 0.65rem;
    font: inherit;
    color: inherit;
    border-radius: 0.25rem;
    cursor: pointer;
}

.purchase-orders-row-menu__item:hover,
.purchase-orders-row-menu__item:focus-visible {
    background: var(--surface-hover, rgba(0, 0, 0, 0.06));
}

.purchase-orders-grid-shell {
    padding-top: 0.75rem;
}

.purchase-orders-grid .e-gridcontent,
.purchase-orders-grid .e-headercontent {
    font-size: 0.83rem;
}

.purchase-orders-grid .e-rowcell,
.purchase-orders-grid .e-headercell {
    padding-top: 0.34rem;
    padding-bottom: 0.34rem;
}

.purchase-orders-primary-cell {
    display: grid;
    gap: 0.08rem;
}

.purchase-orders-primary-cell strong {
    font-size: 0.83rem;
    color: var(--text);
}

.purchase-orders-subline {
    font-size: 0.68rem;
    color: var(--text-muted);
}

.purchase-order-detail-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-bottom: 1rem;
}

.purchase-order-detail-grid strong {
    display: block;
    margin-top: 0.25rem;
    font-size: 1rem;
    color: var(--text);
}

.purchase-order-summary-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 1rem;
}

.detail-inline-card {
    margin-top: 1rem;
    padding: 1rem;
}

.purchase-orders-row-actions {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.purchase-orders-detail-header {
    display: flex;
    justify-content: space-between;
    gap: 0.8rem;
    align-items: start;
    margin-bottom: 0.75rem;
}

.purchase-orders-detail-header h2 {
    margin: 0.1rem 0 0;
    font-size: 1rem;
}

.purchase-orders-detail-copy {
    margin: 0.22rem 0 0;
    color: var(--text-muted);
    font-size: 0.82rem;
}

.purchase-orders-detail-toolbar {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    align-items: center;
    margin-bottom: 0.75rem;
}

.purchase-orders-notes {
    margin: 0.8rem 0;
}

.po-status-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 4rem;
    border-radius: 999px;
    padding: 0.14rem 0.46rem;
    font-size: 0.72rem;
    font-weight: 700;
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--text-muted);
}

.po-status-badge.is-open {
    background: color-mix(in oklab, #d7e2ef 55%, var(--surface));
    color: var(--text);
}

.po-status-badge.is-issued {
    background: color-mix(in oklab, #e5cc78 38%, var(--surface));
    color: var(--text);
}

.po-status-badge.is-received {
    background: color-mix(in oklab, #89a35f 34%, var(--surface));
    color: var(--text);
}

.po-status-badge.is-closed {
    background: color-mix(in oklab, #b8bec8 42%, var(--surface));
    color: var(--text);
}

.po-status-badge.is-cancelled {
    background: color-mix(in oklab, #cf7a6e 42%, var(--surface));
    color: var(--text);
}

/* #15694: a9e5d4a raised this from 1080 to 1280, which stacked the PO
   detail header and toolbar on ordinary desktop windows (1280x800 laptops,
   maximised 1366x768). Both base rules are display:flex; space-between, and
   the flip to `display: grid` with no template yields a single column.
   Back onto the 1024 scale, per MainLayout.razor.css: "the column-count
   carves in app.css stay at 1024, because 1366px genuinely has the room."
   Deliberately does NOT carry 26d8ea0's `(pointer: coarse) and
   (max-width: 1366px)` arm — that arm is for app chrome; adding it here
   would stack this header on a 12.9" iPad Pro, which has the room. */
@media (max-width: 1024px) {
    .purchase-orders-detail-header,
    .purchase-orders-detail-toolbar {
        display: grid;
    }

    .purchase-orders-filter-field--search {
        min-width: 100%;
    }
}

.workspace-header-main {
    display: grid;
    gap: 0.35rem;
}

.workspace-title-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.55rem;
}

.workspace-header-card h1 {
    margin: 0;
    font-size: clamp(1rem, 1.05vw, 1.18rem);
    line-height: 1.05;
}

.workspace-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    align-items: center;
}

.workspace-client-inline {
    font-size: 0.8rem;
    font-weight: 700;
    color: #0f172a;
}

.workspace-meta-pill {
    display: inline-flex;
    align-items: center;
    border: 1px solid rgba(12, 21, 36, 0.1);
    background: rgba(255, 255, 255, 0.92);
    color: var(--text-muted);
    border-radius: 999px;
    padding: 0.2rem 0.48rem;
    font-size: 0.74rem;
    font-weight: 600;
    line-height: 1.2;
}

.workspace-switcher-grid {
    display: grid;
    grid-template-columns: minmax(14rem, 1fr) minmax(18rem, 1.2fr);
    gap: 0.5rem;
    margin-top: 0.45rem;
    padding: 0.55rem 0.6rem 0.65rem;
    border: 1px solid rgba(23, 92, 211, 0.14);
    border-radius: 0.95rem;
    background: linear-gradient(180deg, #f8fbff 0%, #eef5ff 100%);
}

.workspace-switcher-grid > label,
.workspace-dropdown-shell {
    display: grid;
    gap: 0.22rem;
    position: relative;
}

.workspace-switcher-grid .flag-label {
    color: #1d4ed8;
    font-size: 0.62rem;
    letter-spacing: 0.14em;
}

.workspace-picker-button {
    width: 100%;
    display: grid;
    gap: 0.12rem;
    text-align: left;
    border-radius: 0.85rem;
    border: 1px solid rgba(23, 92, 211, 0.28);
    background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);
    box-shadow: 0 4px 14px rgba(23, 92, 211, 0.08);
    padding: 0.58rem 0.8rem;
    color: #0f172a;
}

.workspace-dropdown-shell:first-child .workspace-picker-button {
    border-color: rgba(16, 185, 129, 0.32);
    box-shadow: 0 8px 20px rgba(16, 185, 129, 0.1);
}

.workspace-dropdown-shell:last-child .workspace-picker-button {
    border-color: rgba(245, 158, 11, 0.34);
    box-shadow: 0 8px 20px rgba(245, 158, 11, 0.12);
}

.workspace-picker-primary {
    font-weight: 700;
    font-size: 0.96rem;
}

.workspace-picker-meta {
    color: var(--text-muted);
    font-size: 0.73rem;
}

.workspace-picker-panel {
    position: absolute;
    z-index: 18;
    top: calc(100% + 0.35rem);
    left: 0;
    right: 0;
    min-width: 0;
    border-radius: 0.9rem;
    border: 1px solid rgba(23, 92, 211, 0.18);
    background: rgba(255, 255, 255, 0.98);
    box-shadow: 0 20px 40px rgba(15, 23, 42, 0.18);
    padding: 0.55rem;
}

.workspace-picker-toolbar,
.workspace-picker-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.55rem;
}

.workspace-picker-count {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 0.74rem;
    font-weight: 600;
}

.workspace-picker-table-wrap {
    max-height: 18rem;
    overflow: auto;
    margin: 0.45rem 0;
    border: 1px solid rgba(148, 163, 184, 0.18);
    border-radius: 0.7rem;
}

.workspace-picker-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.workspace-picker-table thead th {
    position: sticky;
    top: 0;
    background: #eef5ff;
    color: #47607f;
    font-size: 0.69rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0.55rem 0.65rem;
    border-bottom: 1px solid rgba(148, 163, 184, 0.18);
}

.workspace-picker-table tbody td {
    padding: 0.55rem 0.65rem;
    border-bottom: 1px solid rgba(226, 232, 240, 0.75);
}

.workspace-picker-row-button {
    appearance: none;
    border: 0;
    background: transparent;
    padding: 0;
    margin: 0;
    color: inherit;
    font: inherit;
    font-weight: 600;
    text-align: left;
    width: 100%;
    cursor: pointer;
}

.workspace-picker-row-button:hover,
.workspace-picker-row-button:focus-visible {
    text-decoration: underline;
    outline: none;
}

.workspace-picker-table tbody tr {
    cursor: pointer;
}

.workspace-picker-table tbody tr:hover {
    background: rgba(23, 92, 211, 0.06);
}

.workspace-picker-table tbody tr.selected {
    background: rgba(23, 92, 211, 0.1);
}

.auth-shell {
    display: grid;
    place-items: center;
    padding: 2rem 1rem 3rem;
}

.auth-panel {
    width: min(38rem, 100%);
    display: grid;
    gap: 1rem;
}

.auth-panel--wide {
    width: min(68rem, 100%);
}

.auth-panel__intro {
    display: grid;
    gap: 0.35rem;
}

.auth-form-grid {
    display: grid;
    gap: 0.85rem;
}

.auth-form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.55rem;
    align-items: center;
}

.auth-inline-check {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
    color: var(--text-muted);
}

.auth-state-card,
.auth-helper-panel {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.auth-state-card__actions {
    display: flex;
    gap: 0.55rem;
    align-items: center;
}

.auth-state-card__actions form {
    margin: 0;
}

.auth-reset-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
}

.auth-section {
    display: grid;
    gap: 0.85rem;
}

.auth-helper-copy {
    margin: 0.35rem 0 0;
    color: var(--text-muted);
}

@media (max-width: 1024px) {
    .auth-reset-grid {
        grid-template-columns: 1fr;
    }

    .auth-state-card,
    .auth-helper-panel {
        display: grid;
    }
}

.workspace-header-card .workspace-picker.e-multicolumncombobox,
.workspace-header-card .workspace-picker .e-multicolumncombobox,
.workspace-header-card .workspace-picker.e-control-wrapper,
.workspace-header-card .workspace-picker .e-control-wrapper,
.workspace-header-card .workspace-picker.e-input-group,
.workspace-header-card .workspace-picker .e-input-group {
    border-radius: 0.85rem;
    border: 1px solid rgba(23, 92, 211, 0.28);
    background: linear-gradient(180deg, #ffffff 0%, #f7fbff 100%);
    box-shadow: 0 4px 14px rgba(23, 92, 211, 0.08);
}

.workspace-header-card .workspace-picker .e-input,
.workspace-header-card .workspace-picker input {
    font-weight: 700;
    color: #0f172a;
}

.workspace-header-card .workspace-picker .e-input-group-icon,
.workspace-header-card .workspace-picker .e-ddl-icon,
.workspace-header-card .workspace-picker .e-multicolumn-list-icon {
    color: #1d4ed8;
}

.workspace-header-card .workspace-picker:hover .e-control-wrapper,
.workspace-header-card .workspace-picker:hover.e-control-wrapper,
.workspace-header-card .workspace-picker:hover .e-input-group,
.workspace-header-card .workspace-picker:hover.e-input-group {
    border-color: rgba(23, 92, 211, 0.45);
    box-shadow: 0 8px 18px rgba(23, 92, 211, 0.12);
}

.workspace-switcher-grid > label:first-child .workspace-picker.e-control-wrapper,
.workspace-switcher-grid > label:first-child .workspace-picker .e-control-wrapper,
.workspace-switcher-grid > label:first-child .workspace-picker.e-input-group,
.workspace-switcher-grid > label:first-child .workspace-picker .e-input-group {
    border-color: rgba(16, 185, 129, 0.32);
    box-shadow: 0 8px 20px rgba(16, 185, 129, 0.1);
}

.workspace-switcher-grid > label:last-child .workspace-picker.e-control-wrapper,
.workspace-switcher-grid > label:last-child .workspace-picker .e-control-wrapper,
.workspace-switcher-grid > label:last-child .workspace-picker.e-input-group,
.workspace-switcher-grid > label:last-child .workspace-picker .e-input-group {
    border-color: rgba(245, 158, 11, 0.34);
    box-shadow: 0 8px 20px rgba(245, 158, 11, 0.12);
}

.app-cta--icon {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
}

.overlay-dialog-backdrop {
    position: fixed;
    inset: 0;
    /* PBI #15254 — base modal layer; see --z-dialog in theme.css. */
    z-index: var(--z-dialog);
    display: grid;
    place-items: center;
    padding: 1.25rem;
    background: color-mix(in oklab, var(--bg-strong) 42%, transparent);
    backdrop-filter: blur(6px);
}

.duplicate-dialog-shell {
    width: min(60rem, calc(100vw - 2.5rem));
    max-height: calc(100vh - 2.5rem);
    overflow: auto;
}

/*
 * Product-Picker modal (pivot mid-session): reinstates the legacy
 * category-tree affordance the compact-toolbar redesign dropped.
 * Two-pane: tree on the left, filtered product list on the right.
 * Theme-token colors only.
 */
.product-picker-shell {
    width: min(78rem, calc(100vw - 2rem));
    height: min(80vh, calc(100vh - 2rem));
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.product-picker-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 0.85rem;
}

.product-picker-body {
    display: grid;
    grid-template-columns: 18rem 1fr;
    gap: 1rem;
    flex: 1 1 auto;
    min-height: 0; /* allow inner scrollers */
}

.product-picker-tree {
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface-sunken);
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.product-picker-search {
    padding: 0.5rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}

.product-picker-tree__list {
    list-style: none;
    margin: 0;
    padding: 0.35rem;
    overflow: auto;
    flex: 1 1 auto;
}

.product-picker-tree__item {
    margin: 0;
}

.product-picker-tree__row,
.product-picker-tree__sub {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    border: 0;
    background: transparent;
    color: var(--text);
    font: inherit;
    text-align: left;
    border-radius: 0.35rem;
    cursor: pointer;
}

.product-picker-tree__row:hover,
.product-picker-tree__sub:hover {
    background: var(--brand-sea-soft);
}

.product-picker-tree__row.is-active,
.product-picker-tree__sub.is-active {
    background: var(--brand-sea);
    color: var(--text-inverse);
    font-weight: 600;
}

.product-picker-tree__sublist {
    list-style: none;
    margin: 0.15rem 0 0.35rem 0.85rem;
    padding: 0;
    border-left: 1px solid var(--border);
}

.product-picker-list {
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--surface);
    overflow: auto;
    min-height: 0;
}

.product-picker-empty {
    padding: 1.5rem;
    color: var(--text-muted);
    font-style: italic;
    text-align: center;
}

.product-picker-products {
    list-style: none;
    margin: 0;
    padding: 0;
}

.product-picker-product {
    display: block;
    width: 100%;
    text-align: left;
    padding: 0.65rem 0.85rem;
    border: 0;
    border-bottom: 1px solid var(--border);
    background: transparent;
    color: var(--text);
    font: inherit;
    cursor: pointer;
}

.product-picker-product:hover {
    background: var(--brand-sea-soft);
}

.product-picker-product:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: -2px;
}

.product-picker-product__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.75rem;
}

.product-picker-product__model {
    font-weight: 600;
    color: var(--text);
}

.product-picker-product__price {
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.product-picker-product__meta {
    margin-top: 0.15rem;
    font-size: 0.85rem;
    color: var(--text-muted);
}

.product-picker-product__desc {
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    color: var(--text-muted);
    /* Two-line clamp so the list scans cleanly. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

@media (max-width: 1024px) {
    .product-picker-body {
        grid-template-columns: 1fr;
    }
    .product-picker-tree {
        max-height: 14rem;
    }
}

.duplicate-dialog-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
}

.duplicate-dialog-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.9rem;
    margin-top: 1rem;
}

.duplicate-dialog-grid > label,
.duplicate-dialog-choice-group {
    display: grid;
    gap: 0.35rem;
}

.duplicate-dialog-span-2 {
    grid-column: span 2;
}

.duplicate-dialog-options {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.duplicate-dialog-choice-group {
    padding: 0.9rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--surface-sunken);
}

.duplicate-dialog-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.products-alpha-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0.7rem 0 0.75rem;
}

.products-alpha-button {
    min-width: 2.6rem;
    min-height: 1.8rem;
    padding: 0.18rem 0.52rem;
    border-radius: 0.55rem;
    border: 1px solid rgba(12, 21, 36, 0.14);
    background: #fffdf8;
    color: #475467;
    font-size: 0.76rem;
    font-weight: 700;
}

.products-alpha-button.active {
    background: linear-gradient(180deg, #eaffcf 0%, #d9ff96 100%);
    border-color: rgba(90, 151, 25, 0.42);
    color: #2b4d08;
}

.catalog-grid.e-grid .e-filterbarcell,
.linked-picker-grid.e-grid .e-filterbarcell {
    background: #dcfce7;
}

.catalog-grid.e-grid .e-row:hover .e-rowcell,
.linked-picker-grid.e-grid .e-row:hover .e-rowcell {
    background: rgba(14, 116, 144, 0.07);
}

.linked-picker-panel {
    padding: 0.8rem;
}

.linked-picker-toolbar {
    margin: 0.65rem 0 0.8rem;
}

.product-detail-page .legacy-panel-grid.two-up {
    align-items: start;
}

.product-photo-card {
    margin-top: 0.9rem;
}

.linked-products-shell {
    display: grid;
    gap: 0.85rem;
}

.linked-products-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 0.8rem;
}

.linked-products-header h2 {
    margin: 0.15rem 0 0.2rem;
    font-size: 1rem;
}

.linked-products-header p {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.85rem;
}

.linked-products-table-wrap {
    overflow-x: auto;
}

.linked-products-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.83rem;
}

.linked-products-table th,
.linked-products-table td {
    padding: 0.5rem 0.55rem;
    border-bottom: 1px solid rgba(12, 21, 36, 0.08);
    vertical-align: middle;
}

.linked-products-table thead th {
    background: #eef6ff;
    color: #334155;
    font-size: 0.71rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.linked-model-link {
    padding: 0;
    border: 0;
    background: transparent;
    color: #175cd3;
    font-weight: 700;
    text-align: left;
}

.linked-subcopy {
    color: var(--text-muted);
    font-size: 0.74rem;
}

.linked-qty-input {
    min-width: 4.8rem;
    max-width: 5.5rem;
}

.linked-hide-toggle {
    margin: 0;
}

.linked-row-actions {
    display: flex;
    gap: 0.35rem;
    justify-content: flex-end;
}

.settings-shell {
    display: grid;
    gap: 0.9rem;
    padding: 0.9rem;
}

.settings-header-copy {
    margin: 0.1rem 0 0;
    font-size: 0.88rem;
    color: var(--text-muted);
}

.settings-tab-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    padding-bottom: 0.2rem;
}

.settings-tab-button {
    min-height: 2rem;
    padding: 0.35rem 0.8rem;
    border: 1px solid rgba(12, 21, 36, 0.14);
    border-radius: 999px;
    background: #fffdf8;
    color: #475467;
    font-size: 0.78rem;
    font-weight: 700;
}

.settings-tab-button.active {
    background: linear-gradient(180deg, #eef5ff 0%, #dbeafe 100%);
    border-color: rgba(23, 92, 211, 0.28);
    color: #175cd3;
}

.settings-panel-grid {
    display: grid;
    gap: 0.85rem;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
}

.settings-panel-grid--two {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.settings-page .legacy-form-panel {
    display: grid;
    gap: 0.8rem;
}

.settings-page .legacy-form-panel h2 {
    margin: 0;
    font-size: 0.96rem;
}

.settings-field-grid {
    display: grid;
    gap: 0.68rem;
}

.settings-field {
    display: grid;
    gap: 0.25rem;
}

.settings-rate-grid-head,
.settings-event-head {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.settings-field--inline {
    max-width: 13rem;
}

/* PBI #15248 — unified checkbox-row primitive. .checkbox-row +
   .settings-check predated this and styled inline-checkbox labels
   three different ways (different paddings/min-heights); JobDetail's
   status row also reached for .legacy-filter-field which is a
   label-over-field shape, not a toggle, so the row of 5 status
   checkboxes rendered tighter than the same shape on Settings.
   .form-toggle is the canonical class going forward; the two legacy
   aliases share its base rules so existing markup keeps working. */
.form-toggle,
.checkbox-row,
.settings-check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 1.9rem;
    font-size: 0.9rem;
}

.form-toggle--compact,
.settings-check--compact {
    justify-content: flex-start;
    min-height: 2.3rem;
}

.settings-inline-checks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.8rem;
}

.settings-rate-grid {
    display: grid;
    grid-template-columns: minmax(6.4rem, 1fr) repeat(2, minmax(5.2rem, 7rem));
    gap: 0.55rem;
    align-items: center;
}

.settings-rate-label,
.settings-event-label {
    font-size: 0.86rem;
    font-weight: 700;
    color: #0f172a;
}

.settings-event-table {
    display: grid;
    grid-template-columns: minmax(9rem, 1.35fr) minmax(12rem, 1fr) minmax(5.25rem, 0.45fr);
    gap: 0.55rem 0.7rem;
    align-items: center;
}

.settings-helper-copy {
    margin: 0;
    color: var(--text-muted);
    font-size: 0.88rem;
}

.settings-note-stack {
    display: grid;
    gap: 0.7rem;
}

.settings-note-card {
    border: 1px solid rgba(12, 21, 36, 0.08);
    border-radius: 0.9rem;
    background: var(--surface-sunken);
    padding: 0.8rem 0.9rem;
}

.settings-note-card strong {
    display: block;
    margin-bottom: 0.3rem;
}

.settings-note-card p {
    margin: 0;
    color: var(--text-muted);
}

.empty-state-panel {
    display: grid;
    gap: 0.2rem;
    padding: 0.9rem 1rem;
    border: 1px dashed rgba(12, 21, 36, 0.14);
    border-radius: 0.85rem;
    background: #fbfcfe;
    color: var(--text-muted);
}

/* PBI #15278 — empty-state Inline variant. The Card variant
   (.empty-state-panel, rendered by <EmptyState>) gives a full
   dashed-border panel for system/filter empty list pages. The
   Inline variant is the lighter prose-style affordance for
   "this slot has no data yet" copy that lives BETWEEN other
   panel chrome (search row → editor side-panel, sub-section
   inside a card). Pre-#15278 the codebase had two parallel
   unstyled idioms for this — `.empty-state-copy` had no rule
   at all so it rendered as a default paragraph, and
   `.portal-empty` was scoped only inside Dashboard.razor.css
   so CommentThread + PortalBidDetail rendered bare text. Both
   are now aliases of the canonical `.empty-state-inline` so
   existing markup keeps working without churn. */
.empty-state-inline,
.empty-state-copy,
.portal-empty {
    margin: 0.35rem 0;
    padding: 0.75rem 0.9rem;
    border: 1px dashed var(--border);
    border-radius: 0.55rem;
    background: var(--surface-sunken, transparent);
    color: var(--text-muted);
    font-style: italic;
    text-align: center;
    line-height: 1.45;
}

.e-popup .e-multicolumn-list .e-gridheader {
    background: linear-gradient(180deg, #eff6ff 0%, #dbeafe 100%);
    border-bottom: 1px solid rgba(23, 92, 211, 0.12);
}

.e-popup .e-multicolumn-list .e-row {
    font-size: 0.82rem;
}

.e-popup .e-multicolumn-list .e-row.e-active,
.e-popup .e-multicolumn-list .e-row:hover {
    background: #eff6ff;
}

.workspace-switcher-actions {
    margin: 0.3rem 0 0;
}

.workspace-rule-pills {
    margin-top: 0.3rem;
}

.workspace-rule-pills--subtle {
    margin-top: 0.1rem;
    opacity: 0.78;
}

.workspace-rule-pills--subtle .status-pill {
    padding: 0.18rem 0.46rem;
    font-size: 0.64rem;
}

.legacy-caption {
    margin: 0.2rem 0 0;
    color: var(--text-muted);
    font-size: 0.82rem;
    line-height: 1.35;
}

.legacy-list-shell,
.legacy-form-panel {
    background: rgba(255, 253, 250, 0.96);
}

.legacy-list-shell {
    padding: 0.8rem;
}

.workspace-operations-rail {
    margin-bottom: 0.4rem;
    border: 1px solid rgba(23, 92, 211, 0.12);
    border-radius: 0.85rem;
    background: linear-gradient(180deg, #f9fcff 0%, #f4f8ff 100%);
    padding: 0.45rem 0.55rem;
}

.workspace-operations-row {
    margin-top: 0;
}

.workspace-operations-row > label,
.workspace-operations-row > .workspace-product-search {
    min-width: 10rem;
    flex: 1 1 10rem;
}

.workspace-operations-row--split {
    display: grid;
    grid-template-columns: minmax(14rem, 1.35fr) auto minmax(10rem, 1fr);
    gap: 0.45rem;
    align-items: end;
}

.workspace-operations-row--secondary {
    grid-template-columns: minmax(20rem, 1fr) auto;
}

.workspace-inline-actions {
    display: inline-flex;
    align-items: end;
    gap: 0.35rem;
    white-space: nowrap;
}

.workspace-inline-actions .btn {
    min-width: auto;
}

.workspace-inline-actions--secondary {
    justify-self: end;
}

.workspace-move-pair {
    display: inline-flex;
    align-items: stretch;
    gap: 0.2rem;
}

.workspace-move-pair .btn {
    min-width: auto;
}

.workspace-operations-row > .workspace-product-query {
    min-width: 12rem;
    flex: 1 1 12rem;
}

.workspace-product-search {
    min-width: 18rem;
    flex: 1.5 1 18rem;
}

.legacy-filter-row,
.legacy-workspace-toolbar,
.legacy-tab-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    align-items: end;
    /* margin-top removed 2026-04-23 — was 0.45rem, but combined with
       the parent .page-stack { gap: 1.5rem } it produced a gap size
       that varied by page because the content panel BELOW the strip
       also contributes its own top padding (legacy-list-shell vs
       app-panel vs company-layout). Letting page-stack's gap do all
       the vertical rhythm keeps the strip spacing identical across
       Products / Inventory / Manufacturers / Suppliers. */
}

.legacy-filter-row {
    margin-bottom: 1rem;
}

.legacy-list-shell .toolbar-row {
    margin-bottom: 1rem;
}

.room-summary-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.65rem;
    margin-bottom: 0.55rem;
}

.room-summary-copy h2 {
    margin: 0;
    font-size: 0.98rem;
    line-height: 1.1;
}

.room-summary-copy .legacy-caption {
    margin-top: 0.15rem;
}

.legacy-filter-cluster {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.dashboard-filter-grid {
    display: grid;
    grid-template-columns: minmax(12rem, 0.9fr) minmax(18rem, 1.55fr) repeat(3, minmax(12rem, 0.85fr));
    gap: 0.7rem;
    align-items: end;
    margin-bottom: 0.85rem;
}

.legacy-filter-field {
    display: grid;
    gap: 0.25rem;
}

.dashboard-filter-field--wide {
    min-width: 0;
}

.legacy-search-row {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(12rem, 0.6fr) auto;
    gap: 0.75rem;
    align-items: center;
}

.legacy-filter-button,
.legacy-tab {
    border: 1px solid rgba(12, 21, 36, 0.12);
    background: #fff;
    color: var(--text);
    border-radius: 0.6rem;
    padding: 0.42rem 0.7rem;
    font: inherit;
    font-weight: 600;
    font-size: 0.82rem;
    line-height: 1.05;
    cursor: pointer;
}

.legacy-filter-button.active,
.legacy-tab.active {
    background: rgba(216, 136, 54, 0.14);
    border-color: rgba(216, 136, 54, 0.45);
    color: var(--accent-strong);
}

/* PBI #15216 — bid-workspace tab strip distinguishes in-page tab
   switching (plain buttons) from full-page nav-out (glyph + dashed
   separator) so the same .legacy-tab base style no longer reads as
   "same action" when half the buttons swap a panel and half navigate
   to a different page. */
.bid-workspace-tabs .legacy-tab--nav-out {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border-style: dashed;
    color: var(--muted, #4a5468);
}

.bid-workspace-tabs .legacy-tab--nav-out .legacy-tab__nav-glyph {
    font-size: 0.78rem;
    line-height: 1;
    opacity: 0.75;
}

.bid-workspace-tabs .visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.legacy-select,
.legacy-search {
    min-width: 9rem;
    width: 100%;
}

.quick-add-panel {
    margin-top: 1rem;
}

.quick-add-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
}

.room-editor-grid {
    align-items: start;
}

.room-summary-panel-head {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: flex-start;
}

.room-summary-panel h2 {
    margin: 0;
}

.workspace-footer-panel {
    margin-top: 0.85rem;
    padding: 0.62rem 0.72rem;
    overflow-x: auto;
}

.workspace-footer-grid {
    display: grid;
    grid-template-columns: minmax(9.4rem, 0.58fr) minmax(16.1rem, 1.04fr) minmax(13.95rem, 0.92fr) minmax(20.9rem, 1.34fr);
    gap: 0.5rem;
    align-items: start;
    min-width: 62rem;
}

.workspace-footer-block {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 0.8rem;
    background: var(--surface);
    overflow: hidden;
    min-height: 100%;
}

.workspace-footer-title {
    padding: 0.38rem 0.52rem;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--border);
    /* Was a hardcoded slate-600 (#344054) that vanished in dark.
       Use --text so the "LABOR / ADJUST / EQUIPMENT / TAX" titles
       stay legible against whatever surface the block paints. */
    color: var(--text);
    min-height: 1.95rem;
    display: flex;
    align-items: center;
}

.workspace-footer-rows {
    display: grid;
    flex: 1 1 auto;
    grid-auto-rows: minmax(2rem, auto);
}

.workspace-footer-rows > div {
    display: flex;
    justify-content: space-between;
    gap: 0.35rem;
    padding: 0.26rem 0.42rem;
    font-size: 0.77rem;
    border-bottom: 1px solid color-mix(in oklab, var(--border) 60%, transparent);
}

.workspace-footer-rows > div:last-child {
    border-bottom: 0;
}

.workspace-footer-rows > div strong {
    font-size: 0.84rem;
    color: var(--text);
}

.workspace-footer-rows.neutral > div {
    background: var(--surface-sunken);
}

.workspace-footer-rows--editable > div {
    align-items: center;
}

.workspace-footer-rows--editable .form-control {
    min-width: 0;
    height: 1.66rem;
    padding: 0.12rem 0.28rem;
    font-size: 0.7rem;
    line-height: 1.15;
    width: 100%;
    margin: 0;
}

.workspace-footer-metric-row {
    display: grid !important;
    grid-template-columns: 4.15rem minmax(0, 1fr);
    gap: 0.28rem;
    align-items: center;
}

.labor-block .workspace-footer-metric-row {
    grid-template-columns: 3.15rem minmax(0, 1fr);
}

.labor-block .footer-field-label {
    font-size: 0.67rem;
}

.workspace-footer-edit-row {
    display: grid !important;
    grid-template-columns: 5.15rem minmax(3.8rem, 0.92fr) minmax(4rem, 0.98fr) 3rem;
    gap: 0.28rem;
    align-items: center;
}

.workspace-footer-status-row {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.28rem;
    align-items: center;
}

.tax-block .workspace-footer-edit-row {
    grid-template-columns: 4.75rem minmax(3.5rem, 0.88fr) minmax(3.85rem, 0.96fr) 4rem;
}

.tax-block .workspace-footer-total {
    grid-template-columns: 4.35rem minmax(0, 1fr);
}

.tax-block .footer-tax-toggle {
    min-width: 3.9rem;
}

.workspace-footer-metric-row,
.workspace-footer-edit-row,
.workspace-footer-status-row {
    min-height: 2rem;
}

.workspace-footer-status-row .form-control {
    width: 100%;
}

.footer-status-group {
    display: grid;
    grid-template-columns: 4.9rem minmax(0, 1fr);
    gap: 0.28rem;
    align-items: center;
    min-width: 0;
}

.footer-field-label {
    /* PBI #13434-ish — labels sit on code-driven row colors
       (labor block pastels, totals blues, neutral sunken). Use
       --text so the contrast reads on every tint. In dark mode
       the override below swaps to the dark text token. */
    color: var(--text);
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    min-width: 0;
    font-size: 0.71rem;
    font-weight: 700;
    line-height: 1.1;
    text-align: right;
}

.footer-field-label::after {
    content: ":";
    margin-left: 0.08rem;
}

.footer-money {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.footer-date {
    min-width: 0;
    font-variant-numeric: tabular-nums;
    max-width: 8.1rem;
}

.footer-tax-toggle {
    display: grid;
    grid-template-columns: 0.78rem auto;
    align-items: center;
    justify-content: flex-start;
    white-space: nowrap;
    margin: 0;
    gap: 0.22rem;
    font-size: 0.68rem;
    min-width: 0;
    height: 1.72rem;
    width: 100%;
    align-self: center;
    box-sizing: border-box;
}

.footer-tax-toggle input,
.footer-status-toggle input {
    margin: 0;
}

.footer-tax-toggle input {
    width: 0.78rem;
    height: 0.78rem;
    justify-self: center;
    align-self: center;
}

.footer-tax-toggle span {
    display: inline-flex;
    align-items: center;
    line-height: 1;
    min-width: 0;
    height: 1.72rem;
}

.footer-status-toggle {
    display: grid;
    grid-template-columns: 0.82rem auto;
    align-items: center;
    justify-content: flex-start;
    gap: 0.22rem;
    min-width: 0;
    min-height: 1.72rem;
    margin: 0;
}

.footer-status-toggle span {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

.footer-status-toggle input {
    width: 0.78rem;
    height: 0.78rem;
    justify-self: center;
    align-self: center;
}

.footer-readonly {
    background: var(--surface-sunken);
    font-weight: 700;
}

.footer-negative {
    color: var(--status-danger);
}

/* PBI #15210 — labor-phase tone restores the legacy "used vs unused"
   cue from the Win-Forms frmBidView.vb (SetNoLaborBackColor): violet
   when the phase has no value, light cyan when it has. The old
   nth-child pastel descent rendered identically either way and lost
   the signal. Install is exempt (no class applied) and keeps its
   neutral row, matching the legacy code that skipped Install. */
.labor-block .workspace-footer-rows > .labor-phase-unused {
    background: color-mix(in oklab, #ee82ee 28%, var(--surface));
}

.labor-block .workspace-footer-rows > .labor-phase-used {
    background: color-mix(in oklab, #cff4fa 70%, var(--surface));
}

/* PBI #15213 — tax-basis field pattern restores the legacy frmBidItemEdit
   cue that visually distinguished which money field the sales tax was
   charged against. IsTaxSellPrice=true means tax basis = Sell Price
   (Sell Price active, Unit Cost passive); IsTaxSellPrice=false means
   tax basis = Unit Cost (Unit Cost active, Sell Price passive). Without
   the tint, Unit Cost and Sell Price look identical in the editor and
   the operator can't tell at a glance which one the tax engine will use.
   Applied to the wrapper <label> so the .flag-label + .form-control both
   sit inside the tinted band. */
.tax-basis-active {
    background: var(--status-info-soft);
    border-left: 3px solid var(--status-info);
    padding-left: 0.5rem;
    border-radius: 6px;
}

.tax-basis-active .flag-label {
    color: var(--status-info-ink);
    font-weight: 600;
}

.tax-basis-passive {
    background: color-mix(in oklab, var(--surface-muted, var(--surface)) 60%, transparent);
    border-left: 3px solid color-mix(in oklab, var(--border, #cbd5e1) 70%, transparent);
    padding-left: 0.5rem;
    border-radius: 6px;
    opacity: 0.85;
}

.workspace-footer-total {
    background: #eef6ff !important;
    font-weight: 700;
}

.workspace-footer-total .footer-field-label {
    color: #0f172a;
}

.workspace-grand-total {
    background: #dbeafe !important;
}

.workspace-footer-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.65rem;
    margin-top: 0.65rem;
}

.grid-edit-hint {
    color: #64748b;
    font-size: 0.73rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
}

/* #15694: a9e5d4a widened this from 1120 to 1280. Two of the three
   declarations below are inert (.legacy-workspace-toolbar sets display:flex
   at equal specificity and later in source, so grid-template-columns never
   applies, and justify-self is meaningless on a flex item) — but
   flex-wrap IS live, because .workspace-inline-actions is inline-flex.
   Narrowed back to 1120 rather than deleted, so wrapping below 1120 is
   preserved exactly as it was before a9e5d4a. */
@media (max-width: 1120px) {
    .workspace-operations-row--split,
    .workspace-operations-row--secondary {
        grid-template-columns: 1fr;
    }

    .workspace-inline-actions--secondary {
        justify-self: start;
        flex-wrap: wrap;
    }
}

.totals-mini-grid .form-control[readonly] {
    background: var(--surface-sunken);
}

.modal-scrim {
    position: fixed;
    inset: 0;
    /* PBI #15254 — base modal layer; see --z-dialog in theme.css. */
    z-index: var(--z-dialog);
    background: rgba(15, 23, 42, 0.48);
    backdrop-filter: blur(4px);
    display: grid;
    place-items: center;
    padding: 1.5rem;
}

.item-dialog {
    width: min(1420px, 100%);
    /* dvh with a vh fallback, matching the <=1024 and <=640 overrides
       that already moved. This base rule is what a 1366px iPad Pro in
       landscape actually resolves to, so it needs the same treatment. */
    max-height: calc(100vh - 3rem);
    max-height: calc(100dvh - 3rem);
    overflow: auto;
    border-radius: 1.2rem;
    background: #fffdfa;
    border: 1px solid rgba(12, 21, 36, 0.12);
    box-shadow: 0 30px 90px rgba(15, 23, 42, 0.25);
    padding: 1.15rem;
}

.item-dialog-title-block {
    padding: 0.8rem 1rem 0.9rem;
    border-radius: 1rem;
    border: 1px solid rgba(37, 99, 235, 0.16);
    background:
        linear-gradient(180deg, rgba(239, 246, 255, 0.98), rgba(255, 255, 255, 0.94)),
        radial-gradient(circle at top right, rgba(59, 130, 246, 0.14), transparent 45%);
}

/* PBI #15257 — title-block selectors track the canonical
   app-eyebrow / app-headline classes (was legacy-kicker / bare h2).
   PBI #15262 — dialog-scoped .app-eyebrow color override removed so
   the eyebrow inherits the canonical --brand-sea everywhere. */
.item-dialog-title-block h2 {
    font-size: 2.1rem;
    line-height: 1.04;
    color: #0f172a;
    letter-spacing: -0.03em;
}

.item-dialog-header {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: flex-start;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid rgba(12, 21, 36, 0.08);
}

.item-dialog-header h2,
.item-dialog-panel h3,
.item-dialog-side h3 {
    margin: 0;
}

.item-dialog-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: end;
    justify-content: flex-end;
}

.item-qty-inline {
    min-width: 8rem;
}

.item-dialog-tabs {
    margin-top: 1rem;
}

.item-dialog-body {
    display: grid;
    grid-template-columns: minmax(0, 1.75fr) minmax(21rem, 0.85fr);
    gap: 1rem;
    margin-top: 1rem;
}

.item-dialog-main {
    display: grid;
    gap: 1rem;
}

.item-dialog-panel,
.item-dialog-side {
    padding: 1rem;
}

/* PBI #15264 — modal size scale. Before this scale, each .item-dialog-panel
   on BidDetail/Products/ProductDetail set its own raw px max-width (720/820/
   880/960) so the same panel role rendered at four different widths. The
   --sm/--md/--lg modifiers collapse that to a 3-step rem scale; consumers
   pick a tier on the panel element and the inline max-width goes away.
   Bespoke per-dialog modifiers (copy-items-dialog, share-bid-dialog,
   payment-plan-dialog) that existed only to carry width hooks are
   retired in the same pass — they had no CSS rules backing them. */
.item-dialog-panel--sm { max-width: 45rem; }
.item-dialog-panel--md { max-width: 52rem; }
.item-dialog-panel--lg { max-width: 60rem; }

.item-dialog-search-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.75rem;
    align-items: end;
}

.item-dialog-search-panel {
    position: relative;
    overflow: visible;
    background:
        linear-gradient(180deg, rgba(255, 250, 240, 0.96), rgba(255, 255, 255, 0.98)),
        radial-gradient(circle at top right, rgba(250, 204, 21, 0.18), transparent 42%);
    border-color: rgba(194, 106, 0, 0.18);
}

.item-dialog-search-header {
    display: flex;
    justify-content: space-between;
    gap: 0.85rem;
    align-items: flex-start;
    margin-bottom: 0.8rem;
}

.item-dialog-search-header h3 {
    margin-top: 0.15rem;
}

.item-search-grid-shell {
    margin-top: 0.2rem;
}

.item-dialog-picker-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.75rem;
    align-items: end;
}

.item-dialog-dropdown-trigger {
    width: 100%;
    min-height: 2.25rem;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.65rem;
    padding: 0.48rem 0.72rem;
    border: 1px solid rgba(148, 163, 184, 0.45);
    border-radius: 0.8rem;
    background: #fff;
    color: #0f172a;
    text-align: left;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
}

.item-dialog-dropdown-trigger:hover,
.item-dialog-dropdown-trigger:focus {
    border-color: rgba(59, 130, 246, 0.55);
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
}

.item-dialog-dropdown-value {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.95rem;
    font-weight: 600;
}

.item-dialog-dropdown-icon {
    color: #64748b;
    font-size: 0.9rem;
}

.item-dialog-picker-button {
    min-width: 5.25rem;
}

.item-search-grid-toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.45rem;
}

.item-search-grid-shell--dropdown {
    position: relative;
    z-index: 6;
    margin-top: -0.15rem;
    padding: 0.75rem;
    min-width: min(1080px, calc(100vw - 7rem));
    width: min(1080px, calc(100vw - 7rem));
    max-width: calc(100vw - 7rem);
    border: 1px solid rgba(194, 106, 0, 0.18);
    border-radius: 0.95rem;
    background: #fffdfa;
    box-shadow: 0 14px 30px rgba(15, 23, 42, 0.12);
}

.dialog-product-grid {
    min-width: 1010px;
    border: 1px solid rgba(194, 106, 0, 0.15);
    border-radius: 0.9rem;
    overflow: hidden;
    background: #fff;
}

.dialog-product-grid .e-gridheader {
    background: linear-gradient(180deg, var(--surface-sunken) 0%, #eef4ff 100%);
}

.dialog-product-grid .e-headercell {
    font-weight: 700;
}

.dialog-product-grid .e-filterbarcell {
    background: #d9f99d;
}

.dialog-product-grid .e-filterbarcell input {
    background: #faffec;
    font-weight: 600;
}

.dialog-product-grid .e-row:hover .e-rowcell {
    background: #fff7ed;
}

.dialog-product-grid .e-gridpager {
    border-top: 1px solid rgba(12, 21, 36, 0.08);
}

.btn-product-use {
    background: linear-gradient(180deg, #4ade80 0%, #22c55e 100%);
    border-color: #16a34a;
    color: #052e16;
    font-weight: 700;
}

.btn-product-use:hover,
.btn-product-use:focus {
    background: linear-gradient(180deg, #64e18d 0%, #22c55e 100%);
    border-color: #15803d;
    color: #052e16;
}

.item-dialog-core-grid,
.item-dialog-side-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.9rem;
    margin-top: 1rem;
}

.item-dialog-side-grid {
    grid-template-columns: 1fr;
}

.item-dialog-span-2 {
    grid-column: span 2;
}

.item-dialog-photo-block {
    margin-top: 0.9rem;
}

.item-dialog-photo {
    width: 100%;
    min-height: 13rem;
    max-height: 18rem;
    object-fit: contain;
    display: grid;
    place-items: center;
    border-radius: 0.95rem;
    border: 1px solid rgba(12, 21, 36, 0.08);
    background:
        linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(255, 255, 255, 0.98)),
        radial-gradient(circle at top right, rgba(59, 130, 246, 0.08), transparent 45%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.82);
}

.item-dialog-photo--empty {
    color: #64748b;
    font-size: 0.86rem;
    font-weight: 600;
}

.item-dialog-metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    margin-top: 1rem;
}

.item-dialog-side {
    align-self: start;
    position: sticky;
    top: 0;
}

/* PBI #15252 — shared dialog-footer primitive. Every dialog footer
   used to mix two button systems (bootstrap .btn-outline-secondary
   for Cancel + .btn-primary for the action vs the design-token
   .app-cta / .app-cta--ghost trio); the primary action also drifted
   left/right across dialogs because each footer reinvented its own
   flex container. .dialog-footer is the single primitive:

     .dialog-footer            flex row, right-aligned, top border
     .dialog-footer__meta      margin-right:auto — left-edge slot for
                               status pills, summaries, or hints
     .dialog-footer__back      margin-right:auto — left-edge slot for
                               Back/secondary-nav (when paired with
                               Cancel + primary on the right)

   Combine with .app-cta / .app-cta--ghost / .app-cta--danger /
   .app-cta--ghost-danger for the buttons themselves. Order in markup
   is: Back/Meta → Cancel → primary action, so the primary always
   lands in the right-most position regardless of which optional
   slots are present. */
/* PBI #15257 — shared header for overlay dialogs. Mirrors the
   eyebrow + headline + subline + Close layout the Duplicate and
   Product-Picker dialogs already used; gives the bare-h2 dialogs
   (share, copy items, delete, payment plan, option group, AI
   alternatives, AI combos, phone link) the same canonical shape
   without coupling each to a per-dialog grid. Caller still owns
   the inner panel; this only governs the title row. */
.dialog-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 0.85rem;
}

.dialog-header__text {
    min-width: 0;
}

.dialog-header__text .app-headline,
.dialog-header__text .app-subline {
    margin: 0.15rem 0 0;
}

.dialog-footer {
    display: flex;
    flex-wrap: wrap;
    gap: 0.55rem;
    align-items: center;
    justify-content: flex-end;
    margin-top: 1rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--border);
}

.dialog-footer__meta,
.dialog-footer__back {
    margin-right: auto;
}

.item-dialog-footer {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
    margin-top: 1rem;
}

.item-dialog-package-block {
    margin-top: 1rem;
    border: 1px solid rgba(37, 99, 235, 0.12);
    border-radius: 1rem;
    background:
        linear-gradient(180deg, rgba(248, 250, 252, 0.98), rgba(255, 255, 255, 0.96)),
        radial-gradient(circle at top right, rgba(59, 130, 246, 0.08), transparent 45%);
    padding: 1rem;
}

.item-dialog-link-block {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(12, 21, 36, 0.08);
}

.item-dialog-link-block a {
    display: block;
    margin-top: 0.3rem;
    word-break: break-word;
}

.item-dialog-package-header {
    display: flex;
    justify-content: space-between;
    gap: 0.8rem;
    align-items: start;
}

.item-dialog-package-header strong {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.92rem;
    color: #0f172a;
}

.item-dialog-package-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0;
    padding: 0.38rem 0.65rem;
    border-radius: 999px;
    border: 1px solid rgba(12, 21, 36, 0.12);
    font-size: 0.78rem;
    font-weight: 700;
}

.item-dialog-package-toggle.is-on {
    background: #dcfce7;
    border-color: #86efac;
    color: #166534;
}

.item-dialog-package-toggle.is-off {
    background: #fee2e2;
    border-color: #fecaca;
    color: #991b1b;
}

.item-dialog-package-total {
    margin-top: 0.75rem;
}

.item-dialog-package-total strong {
    display: block;
    margin-top: 0.25rem;
    font-size: 1rem;
}

.item-dialog-package-list {
    display: grid;
    gap: 0.55rem;
    margin-top: 0.75rem;
    max-height: 19rem;
    overflow: auto;
    padding-right: 0.2rem;
}

.item-dialog-package-row {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.35fr) minmax(4.9rem, 5.2rem) minmax(5.4rem, 6.2rem);
    gap: 0.75rem;
    padding: 0.55rem 0.65rem;
    border: 1px solid rgba(12, 21, 36, 0.08);
    border-radius: 0.75rem;
    background: var(--surface-sunken);
    align-items: start;
}

.item-dialog-package-row.selected {
    border-color: rgba(34, 197, 94, 0.3);
    background: #f0fdf4;
}

.item-dialog-package-row.muted {
    opacity: 0.62;
}

.item-dialog-package-row strong,
.item-dialog-package-row span {
    display: block;
    line-height: 1.25;
}

.item-dialog-package-row span {
    color: var(--text-muted);
    font-size: 0.78rem;
}

.item-dialog-package-include {
    display: grid;
    grid-template-columns: 0.95rem minmax(0, 1fr);
    gap: 0.5rem;
    margin: 0;
    align-items: start;
}

.item-dialog-package-include input {
    margin-top: 0.18rem;
}

.item-dialog-package-copy small {
    display: block;
    margin-top: 0.22rem;
    color: #64748b;
    font-size: 0.75rem;
    line-height: 1.25;
}

.item-dialog-package-amount {
    text-align: right;
}

.item-dialog-package-amount strong {
    font-size: 0.95rem;
    color: #0f172a;
}

.compact-check {
    margin-top: 1.55rem;
}

.legacy-panel-grid {
    display: grid;
    gap: 1rem;
}

.legacy-panel-grid.two-up {
    grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
}

.legacy-panel-grid.three-up {
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.legacy-form-panel h2 {
    margin-bottom: 0.45rem;
    font-size: 1rem;
}

.legacy-fieldset {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(12, 21, 36, 0.08);
}

.legacy-fieldset h3 {
    margin: 0 0 0.75rem;
    font-size: 1rem;
}

.legacy-summary-grid {
    display: grid;
    gap: 0.75rem;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
}

.metric-card.compact strong {
    font-size: 1.15rem;
}

/* PBI #15248 — base flex/gap/align rules moved to the shared
   .form-toggle selector list above; .checkbox-row keeps only its
   ClientDetail-specific top-margin (the SameAsClient toggle sits in
   a form-grid--records row where neighbour fields carry a
   .flag-label span above the input, so the checkbox-only label
   needs a manual nudge to align its baseline). */
.checkbox-row {
    margin-top: 1.8rem;
}

.flow-list,
.queue-items {
    margin: 1rem 0 0;
    padding-left: 1.15rem;
}

.flow-list li + li,
.queue-items li + li {
    margin-top: 0.6rem;
}

.flag-grid {
    display: grid;
    gap: 1rem;
    margin-top: 1rem;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 1rem;
}

.data-table th {
    text-align: left;
    font-size: 0.78rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.data-table th,
.data-table td {
    padding: 0.8rem 0.5rem;
    border-bottom: 1px solid rgba(12, 21, 36, 0.08);
}

.queue-list {
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    margin-top: 1rem;
}

.queue-card {
    border-radius: 1.15rem;
    border: 1px solid rgba(12, 21, 36, 0.08);
    background: rgba(255, 252, 247, 0.88);
    padding: 1rem;
}

.queue-title {
    margin: 0;
    font-weight: 700;
    color: var(--text);
}

.queue-meta,
.queue-hidden,
.room-detail-header p {
    margin: 0.35rem 0 0;
    color: var(--text-muted);
}

.selected-card {
    cursor: pointer;
    transition: transform 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
}

.bid-workspace-page .e-grid .e-headercontent,
.bid-workspace-page .e-grid .e-gridheader {
    border-top-left-radius: 0.9rem;
    border-top-right-radius: 0.9rem;
}

.bid-workspace-page .e-grid .e-gridheader {
    border-bottom: 1px solid rgba(12, 21, 36, 0.08);
}

.bid-workspace-page .e-grid .e-headercell,
.bid-workspace-page .e-grid .e-rowcell {
    padding-top: 0.65rem;
    padding-bottom: 0.65rem;
}

.bid-workspace-page .e-grid .e-row:nth-child(even) .e-rowcell {
    background: rgba(23, 92, 211, 0.02);
}

.bid-workspace-page .e-grid .e-sortfilterdiv,
.bid-workspace-page .e-grid .e-filtermenudiv,
.bid-workspace-page .e-grid .e-headercelldiv .e-headertext + span[title="Press Enter to sort"] {
    display: none !important;
}

@media (max-width: 1024px) {
    .legacy-shell-header,
    .room-summary-bar,
    .quick-add-header,
    .room-summary-panel-head,
    .item-dialog-header {
        flex-direction: column;
        align-items: stretch;
    }

    .workspace-switcher-grid {
        grid-template-columns: 1fr;
    }

    .item-dialog-body {
        grid-template-columns: 1fr;
    }

    .item-dialog-core-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .item-dialog-search-row {
        grid-template-columns: 1fr;
    }

    /* #15700: collapsing to one column is not enough — the base rule's
       `min-width: 62rem` floor (992px) survives, so each Labor / Adjust /
       Equipment / Tax block stayed 992px wide inside a 768px panel and the
       user had to scroll sideways to reach a control whose label was
       already visible. Reset the floor with the collapse. */
    .workspace-footer-grid {
        grid-template-columns: 1fr;
        min-width: 0;
    }

    .item-dialog-side {
        position: static;
    }
}

@media (max-width: 640px) {
    .item-dialog-core-grid {
        grid-template-columns: 1fr;
    }

    .item-dialog-span-2 {
        grid-column: span 1;
    }
}

.selected-card:hover {
    transform: translateY(-1px);
    border-color: rgba(216, 136, 54, 0.28);
}

.selected-card.active {
    border-color: rgba(216, 136, 54, 0.45);
    box-shadow: 0 14px 34px rgba(17, 26, 43, 0.12);
}

.room-detail-header {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: start;
}

.queue-items {
    color: var(--text-muted);
}

.queue-items strong,
.timeline strong {
    display: block;
    color: var(--text);
}

.timeline {
    margin: 1rem 0 0;
    padding-left: 1.15rem;
}

.timeline li + li {
    margin-top: 0.9rem;
}

.ingestion-grid {
    align-items: start;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid,
.validation-message {
    color: #b42318;
}

.blazor-error-boundary {
    background: #b42318;
    color: white;
    padding: 1rem;
    border-radius: 1rem;
}

@media (max-width: 640px) {
    .content {
        padding-top: 1.25rem;
    }

    .hero-panel,
    .surface-card,
    .metric-card {
        border-radius: 1.2rem;
    }
}

.legacy-filter-chip {
    border: 1px solid #d0d5dd;
    background: #ffffff;
    color: #344054;
    border-radius: 999px;
    padding: 0.45rem 0.75rem;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.bidglory-grid.e-grid {
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    overflow: hidden;
    background: var(--surface);
    box-shadow: var(--shadow-sm);
}

.bidglory-grid.e-grid .e-gridheader {
    border-bottom: 1px solid var(--border);
    background: var(--surface-sunken);
}

.bidglory-grid.e-grid .e-headercell {
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding-top: 0.55rem;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid var(--border);
}

.bidglory-grid.e-grid .e-rowcell {
    color: var(--text);
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    border-color: var(--border);
    line-height: 1.25;
    background: var(--surface);
}

.bidglory-grid.e-grid .e-altrow .e-rowcell {
    background: var(--surface-sunken);
}

.bidglory-grid.e-grid .e-row:hover .e-rowcell {
    background: color-mix(in oklab, var(--brand-sea) 8%, var(--surface));
}

.bidglory-grid.e-grid .e-row.e-selectionbackground .e-rowcell,
.bidglory-grid.e-grid .e-row[aria-selected="true"] .e-rowcell {
    background: var(--brand-sea) !important;
    color: #ffffff;
}

/* Same row-status tints for the Syncfusion grid flavor. Keep
   identical to the plain-table rules so the two render paths
   carry the same color language. */
.bidglory-grid.e-grid .bid-row-labor .e-rowcell {
    background: #dff3fb;
}

.bidglory-grid.e-grid .bid-row-linked .e-rowcell {
    background: var(--brand-sea-soft);
}

.bidglory-grid.e-grid .bid-row-hidden .e-rowcell {
    background: var(--surface-sunken);
    color: var(--text-subtle);
    opacity: 0.72;
}

.bidglory-grid.e-grid .bid-row-option .e-rowcell {
    /* Purple tint — gives option rows a distinct semantic hue
       that won't collide with --status-warning-soft (yellow)
       used by warning pills on this screen. */
    background: color-mix(in oklab, #d9b7ff 22%, var(--surface));
}

.bidglory-grid.e-grid .bid-row-zero .e-rowcell {
    color: var(--status-danger);
}

.bidglory-grid.e-grid .e-gridcontent {
    background: var(--surface);
}

.bid-workspace-grid-shell {
    margin-top: 0.55rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    overflow: auto;
    background: var(--surface);
}

.bid-workspace-table {
    width: 100%;
    min-width: 1320px;
    border-collapse: collapse;
    font-size: 0.8rem;
}

.bid-workspace-table th,
.bid-workspace-table td {
    padding: 0.42rem 0.55rem;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    line-height: 1.25;
}

.bid-workspace-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface-sunken);
    color: var(--text-muted);
    text-align: left;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.bid-workspace-table td {
    color: var(--text);
    background: var(--surface);
}

.bid-workspace-table tr:nth-child(even) td {
    background: var(--surface-sunken);
}

.bid-workspace-table tr:hover td,
.bid-workspace-table tr.selected td {
    background: var(--brand-sea);
    color: #fff;
}

.bid-workspace-table .num {
    text-align: right;
}

.bid-workspace-table .center {
    text-align: center;
}

/* Code-driven row status colors. GetRowClass(item) in
   BidDetail.razor stamps one (or a combination) of these classes
   on each <tr>. The light-mode colors are the ORIGINAL pastels
   from before the theme pass — they read at a glance and
   distinguish each labor/linked/option/hidden row from the alt
   stripe. Dark overrides live below and mix the same pastel
   into the dark surface so identity survives into dark mode. */
.bid-workspace-table tr.bid-row-labor td {
    background: #dff3fb;  /* light cyan — labor */
}

.bid-workspace-table tr.bid-row-linked td {
    background: var(--brand-sea-soft);  /* brand-blue tint — linked package */
}

.bid-workspace-table tr.bid-row-hidden td {
    background: var(--surface-sunken);
    color: var(--text-subtle);
}

.bid-workspace-table tr.bid-row-option td {
    /* Purple tint — distinct semantic hue for option rows that
       avoids clashing with --status-warning-soft (yellow) used
       by warning pills on the same screen. */
    background: color-mix(in oklab, #d9b7ff 22%, var(--surface));
}

.bid-workspace-table tr.bid-row-zero td {
    color: var(--status-danger);
}

.bid-workspace-table tr.selected.bid-row-labor td,
.bid-workspace-table tr.selected.bid-row-linked td,
.bid-workspace-table tr.selected.bid-row-hidden td,
.bid-workspace-table tr.selected.bid-row-option td,
.bid-workspace-table tr.selected.bid-row-zero td {
    background: var(--brand-sea);
    color: #fff;
}

.bidglory-grid.e-grid .e-headertext {
    white-space: nowrap;
}

.room-cell {
    display: flex;
    flex-direction: column;
    gap: 0.22rem;
    line-height: 1.15;
}

.room-cell-top {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
}

.room-cell strong {
    font-size: 0.83rem;
}

.room-phase-pill,
.room-linked-pill {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    border-radius: 999px;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    line-height: 1;
    text-transform: uppercase;
    white-space: nowrap;
}

.room-phase-pill {
    padding: 0.18rem 0.42rem;
    background: #eef3fb;
    color: #516074;
    border: 1px solid #d5dfec;
}

.room-linked-pill {
    padding: 0.16rem 0.38rem;
    background: #f7efff;
    color: #6b31ce;
    border: 1px solid #e8d8ff;
}

.workspace-description-cell {
    display: inline-block;
    max-width: 34rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    vertical-align: bottom;
}

.bidglory-grid.e-grid .e-headercell .e-sortfilterdiv {
    margin-left: 0.35rem;
}

.bidglory-grid.e-grid .btn.btn-sm {
    border-radius: 0.65rem;
    padding: 0.22rem 0.55rem;
    font-size: 0.74rem;
    font-weight: 600;
}

.grid-link-button {
    border: 0;
    padding: 0;
    background: transparent;
    color: #1d4ed8;
    font: inherit;
    font-weight: 600;
    text-align: left;
    text-decoration: none;
}

.grid-link-button:hover,
.grid-link-button:focus-visible {
    color: #0f3d9e;
    text-decoration: underline;
}

.grid-action-pair {
    display: flex;
    gap: 0.35rem;
    align-items: center;
}

.bid-list-page .legacy-list-shell {
    padding-top: 0.75rem;
}

.bid-list-page .legacy-filter-row {
    margin-top: 0;
    margin-bottom: 0.65rem;
}

.bid-list-page .pill-row {
    margin: 0 0 0.7rem;
}

.bid-workspace-page .btn,
.bid-workspace-page .form-control,
.bid-workspace-page .form-select {
    /* --control-h-sm is 2rem under a mouse — identical to the literal
       this replaces — and 2.4rem under `pointer: coarse`. The bid
       workspace is the densest screen in the app and the one most
       likely to be driven from an iPad, so it gets the smaller of the
       two touch sizes rather than the full 44px. */
    min-height: var(--control-h-sm);
    font-size: 0.88rem;
}

.bid-workspace-page .form-control,
.bid-workspace-page .form-select {
    padding: 0.3rem 0.55rem;
}

.bid-workspace-page .btn {
    padding: 0.28rem 0.6rem;
}

.e-hide,
.bidglory-grid.e-grid caption.e-hide,
.bidglory-grid.e-grid .e-hide {
    display: none !important;
}

@media (max-width: 1024px) {
    .legacy-filter-cluster,
    .legacy-search-row,
    .settings-panel-grid,
    .settings-panel-grid--two {
        grid-template-columns: 1fr;
    }

    /* #15703: this grid was in the list above, so at 1024 all five fields
       stacked and consumed the entire 768px iPad-landscape viewport —
       every bid row started below the fold. Reverting the threshold to
       900 does not work either: the base track list needs
       12 + 18 + (12 × 3) = 66rem of minimums plus 4 × 0.7rem gaps ≈ 1101px,
       so 5-up genuinely cannot fit in the ~960px panel. Wrap to two rows
       instead: row 1 = Search In | Power Search, row 2 = Status | Location
       | Date. Fixed 3-up rather than auto-fit — auto-fit's track count
       drifts with each panel's padding across the six surfaces sharing
       this class. */
    .dashboard-filter-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .dashboard-filter-field--wide {
        grid-column: span 2;
    }

    .legacy-shell-header {
        flex-direction: column;
    }

    .workspace-switcher-grid {
        grid-template-columns: 1fr;
    }

    .settings-event-table {
        grid-template-columns: 1fr;
    }

    .duplicate-dialog-grid,
    .duplicate-dialog-options {
        grid-template-columns: 1fr;
    }

    .duplicate-dialog-span-2 {
        grid-column: auto;
    }

    .duplicate-dialog-actions,
    .duplicate-dialog-header {
        flex-direction: column;
        align-items: stretch;
    }
}

/* #15703 (phone tier): 3-up is right for tablets but not for a 390px
   viewport, where three tracks leave ~120px per field. Drop to a single
   column and release the Power Search span. */
@media (max-width: 640px) {
    .dashboard-filter-grid {
        grid-template-columns: 1fr;
    }

    .dashboard-filter-field--wide {
        grid-column: auto;
    }
}

/* =============================================================
   Dark-mode compatibility layer
   -------------------------------------------------------------
   Rules above this point were originally authored against a
   light-only palette and still contain hard-coded hex values in
   spots. This layer reaches into those spots in dark mode so
   they don't light up like headlights on a night drive.
   The long-term plan is to fold each of these into the rule it
   overrides; for now this is the safety net so nothing looks
   broken when a user flips the theme toggle.
   ============================================================= */

[data-theme="dark"] .legacy-action-btn,
[data-theme="dark"] .legacy-filter-button,
[data-theme="dark"] .legacy-tab,
[data-theme="dark"] .legacy-filter-chip {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border);
}

[data-theme="dark"] .legacy-action-btn.green,
[data-theme="dark"] .legacy-filter-button.active,
[data-theme="dark"] .legacy-tab.active {
    background: color-mix(in oklab, var(--brand-sea) 28%, var(--surface));
    color: var(--brand-glow);
    border-color: color-mix(in oklab, var(--brand-sea) 45%, transparent);
}

[data-theme="dark"] .legacy-action-btn.red {
    background: color-mix(in oklab, var(--status-danger) 22%, var(--surface));
    color: var(--status-danger);
}

[data-theme="dark"] .filter-chip {
    background: var(--surface);
    border-color: var(--border);
    color: var(--text);
}
[data-theme="dark"] .filter-chip.active {
    background: color-mix(in oklab, var(--brand-sea) 22%, var(--surface));
    border-color: color-mix(in oklab, var(--brand-sea) 45%, transparent);
    color: var(--brand-glow);
}

[data-theme="dark"] .workspace-switcher-grid {
    background: linear-gradient(180deg, var(--surface-raised) 0%, var(--surface) 100%);
    border-color: color-mix(in oklab, var(--brand-sea) 25%, var(--border));
}

[data-theme="dark"] .settings-tab-button,
[data-theme="dark"] .products-alpha-button {
    background: var(--surface);
    color: var(--text-muted);
    border-color: var(--border);
}
[data-theme="dark"] .settings-tab-button.active {
    background: color-mix(in oklab, var(--brand-sea) 28%, var(--surface));
    color: var(--brand-glow);
    border-color: color-mix(in oklab, var(--brand-sea) 45%, transparent);
}
[data-theme="dark"] .products-alpha-button.active {
    background: color-mix(in oklab, var(--status-success) 28%, var(--surface));
    border-color: color-mix(in oklab, var(--status-success) 45%, transparent);
    color: var(--status-success);
}

[data-theme="dark"] .settings-note-card,
[data-theme="dark"] .empty-state-panel,
[data-theme="dark"] .queue-card,
[data-theme="dark"] .legacy-list-shell,
[data-theme="dark"] .legacy-form-panel,
[data-theme="dark"] .workspace-footer-block {
    background: var(--surface-raised);
    border-color: var(--border);
}

[data-theme="dark"] .empty-state-panel {
    border-style: dashed;
    color: var(--text-muted);
}

/* PBI #15278 — dark-theme parity for the inline variant + aliases. */
[data-theme="dark"] .empty-state-inline,
[data-theme="dark"] .empty-state-copy,
[data-theme="dark"] .portal-empty {
    background: var(--surface-sunken);
    border-color: var(--border);
    color: var(--text-muted);
}

[data-theme="dark"] .workspace-operations-rail {
    background: linear-gradient(180deg, var(--surface-raised) 0%, var(--surface) 100%);
    border-color: color-mix(in oklab, var(--brand-sea) 25%, var(--border));
}

[data-theme="dark"] .item-dialog,
[data-theme="dark"] .item-search-grid-shell--dropdown {
    background: var(--surface-raised);
    border-color: var(--border);
    color: var(--text);
}

[data-theme="dark"] .item-dialog-title-block {
    background:
        linear-gradient(180deg, color-mix(in oklab, var(--brand-sea-soft) 60%, var(--surface-raised)), var(--surface-raised));
    border-color: color-mix(in oklab, var(--brand-sea) 30%, var(--border));
}
[data-theme="dark"] .item-dialog-title-block h2 {
    color: var(--text);
}

[data-theme="dark"] .item-dialog-search-panel {
    background:
        linear-gradient(180deg, color-mix(in oklab, var(--brand-cream) 20%, var(--surface-raised)), var(--surface-raised));
    border-color: color-mix(in oklab, var(--status-warning) 30%, var(--border));
}

[data-theme="dark"] .item-dialog-dropdown-trigger {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border-strong);
}
[data-theme="dark"] .item-dialog-dropdown-icon { color: var(--text-muted); }

[data-theme="dark"] .item-dialog-package-block,
[data-theme="dark"] .item-dialog-photo {
    background: var(--surface-raised);
    border-color: var(--border);
}

[data-theme="dark"] .item-dialog-package-row {
    border-color: var(--border);
}
[data-theme="dark"] .item-dialog-package-row.selected {
    background: color-mix(in oklab, var(--status-success) 18%, var(--surface-sunken));
    border-color: color-mix(in oklab, var(--status-success) 35%, transparent);
}

[data-theme="dark"] .item-dialog-package-toggle.is-on {
    background: color-mix(in oklab, var(--status-success) 22%, var(--surface));
    border-color: color-mix(in oklab, var(--status-success) 45%, transparent);
    color: var(--status-success);
}
[data-theme="dark"] .item-dialog-package-toggle.is-off {
    background: color-mix(in oklab, var(--status-danger) 22%, var(--surface));
    border-color: color-mix(in oklab, var(--status-danger) 45%, transparent);
    color: var(--status-danger);
}

[data-theme="dark"] .linked-products-table thead th {
    background: color-mix(in oklab, var(--brand-sea) 16%, var(--surface));
    color: var(--text-muted);
}

[data-theme="dark"] .linked-model-link,
[data-theme="dark"] .grid-link-button {
    color: var(--brand-glow);
}
[data-theme="dark"] .grid-link-button:hover,
[data-theme="dark"] .grid-link-button:focus-visible {
    color: var(--brand-sea);
}

/* Dark-mode row-status tints. Mix the ORIGINAL light pastels
   into the dark surface so each status keeps a distinguishable
   hue against the navy theme. Pairs with the .bid-row-* rules
   in the light section above — edit in lockstep. */
[data-theme="dark"] .bid-workspace-table tr.bid-row-labor td,
[data-theme="dark"] .bidglory-grid.e-grid .bid-row-labor .e-rowcell {
    background: color-mix(in srgb, #dff3fb 16%, var(--surface));
}
[data-theme="dark"] .bid-workspace-table tr.bid-row-linked td,
[data-theme="dark"] .bidglory-grid.e-grid .bid-row-linked .e-rowcell {
    /* Brand-sea bed in dark, mirrors the light --brand-sea-soft anchor. */
    background: color-mix(in srgb, var(--brand-sea) 22%, var(--surface));
}
[data-theme="dark"] .bid-workspace-table tr.bid-row-option td,
[data-theme="dark"] .bidglory-grid.e-grid .bid-row-option .e-rowcell {
    /* Purple bed — matches the light-mode option-row hue rather
       than re-using the cream that clashed with warning pills. */
    background: color-mix(in srgb, #d9b7ff 16%, var(--surface));
}
[data-theme="dark"] .bid-workspace-table tr.bid-row-hidden td,
[data-theme="dark"] .bidglory-grid.e-grid .bid-row-hidden .e-rowcell {
    background: var(--surface-sunken);
    color: var(--text-subtle);
    opacity: 0.72;
}

/* Dark-mode labor + totals rows — preserve the code-driven hue
   identity. The light-mode CSS gives each labor row a distinct
   pastel (pink / lavender / mauve / cyan) that carries semantic
   meaning (different labor kinds), and the totals rows use a
   blue bed. Mixing those originals into the dark surface at a
   low ratio keeps the tint readable without clashing with the
   navy theme. Labels bump to --text via the rule above so the
   text pops on every tinted row. */
/* PBI #15210 — dark-theme labor-phase tone. Mirrors the violet/cyan
   "unused vs used" cue restored on the light theme, at lower opacity
   so the navy surface still reads. */
[data-theme="dark"] .labor-block .workspace-footer-rows > .labor-phase-unused { background: color-mix(in oklab, #ee82ee 18%, var(--surface)); }
[data-theme="dark"] .labor-block .workspace-footer-rows > .labor-phase-used { background: color-mix(in oklab, #cff4fa 22%, var(--surface)); }

[data-theme="dark"] .workspace-footer-total {
    background: color-mix(in oklab, #dbeafe 22%, var(--surface)) !important;
}
[data-theme="dark"] .workspace-footer-total .footer-field-label {
    color: var(--text);
}
[data-theme="dark"] .workspace-grand-total {
    background: color-mix(in oklab, #93c5fd 30%, var(--surface)) !important;
}

[data-theme="dark"] .dialog-product-grid {
    background: var(--surface);
    border-color: color-mix(in oklab, var(--status-warning) 25%, var(--border));
}
[data-theme="dark"] .dialog-product-grid .e-gridheader {
    background: linear-gradient(180deg, var(--surface-sunken) 0%, color-mix(in oklab, var(--brand-sea) 12%, var(--surface)) 100%);
}
[data-theme="dark"] .dialog-product-grid .e-filterbarcell {
    background: color-mix(in oklab, var(--status-success) 22%, var(--surface));
}
[data-theme="dark"] .dialog-product-grid .e-filterbarcell input {
    background: color-mix(in oklab, var(--status-success) 12%, var(--surface));
    color: var(--text);
}
[data-theme="dark"] .dialog-product-grid .e-row:hover .e-rowcell {
    background: color-mix(in oklab, var(--brand-sea) 14%, var(--surface));
}

[data-theme="dark"] .e-popup .e-multicolumn-list .e-gridheader {
    background: linear-gradient(180deg, var(--surface-sunken) 0%, color-mix(in oklab, var(--brand-sea) 14%, var(--surface)) 100%);
    border-bottom: 1px solid color-mix(in oklab, var(--brand-sea) 24%, var(--border));
}
[data-theme="dark"] .e-popup .e-multicolumn-list .e-row.e-active,
[data-theme="dark"] .e-popup .e-multicolumn-list .e-row:hover {
    background: color-mix(in oklab, var(--brand-sea) 14%, var(--surface));
}

[data-theme="dark"] .room-phase-pill {
    background: color-mix(in oklab, var(--brand-sea) 16%, var(--surface));
    color: var(--text-muted);
    border-color: color-mix(in oklab, var(--brand-sea) 30%, var(--border));
}
[data-theme="dark"] .room-linked-pill {
    background: color-mix(in oklab, var(--brand-glow) 22%, var(--surface));
    color: var(--brand-glow);
    border-color: color-mix(in oklab, var(--brand-glow) 35%, transparent);
}

[data-theme="dark"] .workspace-meta-pill,
[data-theme="dark"] .workspace-client-inline {
    background: var(--surface-raised);
    color: var(--text-muted);
    border-color: var(--border);
}

[data-theme="dark"] .footer-negative { color: var(--status-danger); }
[data-theme="dark"] .validation-message,
[data-theme="dark"] .invalid { color: var(--status-danger); }

[data-theme="dark"] .settings-rate-label,
[data-theme="dark"] .settings-event-label,
[data-theme="dark"] .workspace-client-inline {
    color: var(--text);
}

[data-theme="dark"] .catalog-input-warning {
    background: color-mix(in oklab, var(--status-danger) 16%, var(--surface));
    border-color: color-mix(in oklab, var(--status-danger) 35%, transparent);
}

[data-theme="dark"] .footer-field-label {
    color: var(--text-muted);
}

[data-theme="dark"] code {
    background: var(--surface-sunken);
    color: var(--text);
}

[data-theme="dark"] .top-row {
    background: color-mix(in oklab, var(--brand-sea-soft) 60%, var(--surface));
    border-bottom-color: var(--border);
}

[data-theme="dark"] .blazor-error-boundary {
    background: var(--status-danger);
    color: var(--text-inverse);
}

[data-theme="dark"] #blazor-error-ui {
    background: color-mix(in oklab, var(--status-warning) 28%, var(--surface));
    color: var(--status-warning-ink);
}

.preview-options-card,
.preview-side-card {
    align-self: start;
}

.preview-settings-grid {
    display: grid;
    gap: 0.38rem 0.5rem;
    grid-template-columns: repeat(6, minmax(0, 1fr));
}

.preview-settings-grid > label {
    display: grid;
    gap: 0.28rem;
    min-width: 0;
}

.preview-settings-grid__wide {
    grid-column: 1 / -1;
}

.preview-settings-grid__span2 {
    grid-column: span 2;
}

.preview-settings-grid .checkbox-row {
    min-height: 1.36rem;
    border: 1px solid var(--border);
    border-radius: 0.56rem;
    padding: 0.12rem 0.34rem;
    background: var(--surface);
    font-size: 0.78rem;
    line-height: 1.18;
}

.preview-toggle-bank {
    display: grid;
    gap: 0.22rem;
}

.preview-toggle-bank .flag-label {
    margin: 0;
}

.preview-toggle-grid {
    display: grid;
    gap: 0.24rem 0.3rem;
    grid-template-columns: repeat(6, minmax(0, 1fr));
}

.preview-scope-strip {
    display: grid;
    gap: 0.3rem;
}

.preview-scope-buttons {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.32rem;
}

.preview-scope-button {
    min-height: 1.72rem;
    border: 1px solid var(--border);
    border-radius: 0.58rem;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-weight: 600;
    padding: 0.3rem 0.45rem;
    text-align: center;
    font-size: 0.8rem;
}

.preview-scope-button.active {
    background: color-mix(in oklab, var(--accent) 12%, var(--surface));
    border-color: color-mix(in oklab, var(--accent) 24%, var(--border));
    color: var(--accent-strong);
}

.preview-inline-choice {
    display: grid;
    grid-template-columns: max-content minmax(7rem, 0.85fr);
    gap: 0.35rem;
    align-items: center;
}

.preview-header-card .app-headline {
    font-size: clamp(1.08rem, 1.5vw, 1.32rem);
}

.preview-header-card .app-subline {
    max-width: 42rem;
    line-height: 1.24;
}

.app-panel--compact {
    padding-top: 0.48rem;
    padding-bottom: 0.52rem;
}

.app-panel--compact .app-panel__body,
.app-panel--compact .legacy-shell-header {
    gap: 0.52rem;
}

.app-panel--compact .app-headline {
    font-size: clamp(0.98rem, 1.28vw, 1.16rem);
    line-height: 1.04;
}

.app-panel--compact .app-subline {
    font-size: 0.8rem;
    line-height: 1.2;
    margin-top: 0.28rem;
}

.app-panel--compact .workspace-meta {
    gap: 0.22rem;
}

.app-panel--compact .app-chip {
    padding: 0.12rem 0.36rem;
    font-size: 0.62rem;
}

.preview-url {
    max-width: 20rem;
    word-break: break-word;
}

.project-manager-page {
    gap: 0.85rem;
}

.project-manager-toolbar-card,
.project-manager-grid-card,
.project-manager-detail-card {
    padding: 0.8rem;
}

.project-manager-command-strip {
    display: grid;
    gap: 0.7rem;
}

.project-manager-command-strip--workspace {
    gap: 0.52rem;
}

.project-manager-topline {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.project-manager-bid-title {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex: 1 1 18rem;
}

.project-manager-quick-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.project-manager-filter-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.65rem;
    align-items: end;
}

.project-manager-filter-strip--workspace .legacy-filter-field {
    min-width: 11rem;
}

.project-manager-filter-field--search {
    min-width: 18rem;
    flex: 1 1 22rem;
}

.project-manager-go {
    min-width: 3.3rem;
}

.project-manager-color-strip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.16rem 0.2rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
}

.project-manager-color-toggle {
    width: 0.95rem;
    height: 0.95rem;
    border-radius: 999px;
    border: 1px solid transparent;
    opacity: 0.42;
    cursor: pointer;
}

.project-manager-color-toggle.active {
    opacity: 1;
    box-shadow: 0 0 0 1px color-mix(in oklab, var(--text) 12%, transparent);
}

.project-manager-color-toggle--gold {
    background: #e5cc78;
}

.project-manager-color-toggle--olive {
    background: #89a35f;
}

.project-manager-color-toggle--gray {
    background: #b8bec8;
}

.project-manager-view-toggle {
    display: inline-flex;
    gap: 0.4rem;
}

.project-manager-grid .e-gridcontent,
.project-manager-grid .e-headercontent {
    font-size: 0.83rem;
}

.project-manager-workspace-page {
    gap: 0.8rem;
}

.project-manager-grid--workspace .e-rowcell,
.project-manager-grid--workspace .e-headercell {
    padding-top: 0.34rem;
    padding-bottom: 0.34rem;
}

.pm-state-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.65rem;
    border-radius: 999px;
    padding: 0.14rem 0.42rem;
    font-size: 0.72rem;
    font-weight: 700;
    border: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--text-muted);
}

.pm-state-badge.is-on.is-ordered {
    background: color-mix(in oklab, #e5cc78 38%, var(--surface));
    color: var(--text);
}

.pm-state-badge.is-on.is-received {
    background: color-mix(in oklab, #89a35f 34%, var(--surface));
    color: var(--text);
}

.pm-state-badge.is-on.is-installed {
    background: color-mix(in oklab, #b8bec8 42%, var(--surface));
    color: var(--text);
}

.project-manager-detail-copy {
    display: grid;
    gap: 0.65rem;
    margin-top: 0.9rem;
}

.project-manager-detail-copy p {
    margin: 0;
}

.project-manager-detail-header {
    display: flex;
    justify-content: space-between;
    gap: 0.8rem;
    align-items: start;
    margin-bottom: 0.75rem;
}

.project-manager-detail-header h2 {
    margin: 0.1rem 0 0;
    font-size: 1rem;
}

.project-manager-notes {
    margin: 0.8rem 0;
}

@media (max-width: 1024px) {
    .preview-settings-grid {
        grid-template-columns: 1fr;
    }

    .preview-settings-grid__wide {
        grid-column: auto;
    }

    .preview-settings-grid__span2 {
        grid-column: auto;
    }

    .preview-toggle-grid {
        grid-template-columns: 1fr;
    }

    .preview-scope-buttons,
    .preview-inline-choice {
        grid-template-columns: 1fr;
    }
}

[data-theme="dark"] .preview-settings-grid .checkbox-row {
    background: var(--surface-raised);
    border-color: var(--border);
}

.reports-page {
    gap: 0.85rem;
}

.reports-filter-card {
    padding: 0.95rem 1rem;
}

.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: 0.75rem;
}

.stats-card {
    padding: 0.85rem 0.95rem;
}

.stats-card strong {
    display: block;
    margin-top: 0.3rem;
    font-size: 1.35rem;
    line-height: 1.1;
}

.reports-filter-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0.75rem;
    align-items: end;
}

.reports-filter-actions {
    display: flex;
    align-items: end;
    justify-content: flex-start;
    min-height: 100%;
}

.reports-pipeline-grid,
.reports-template-grid {
    display: grid;
    gap: 0.85rem;
}

.reports-pipeline-grid {
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

.reports-template-meta {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 0.7rem;
    margin-bottom: 0.9rem;
}

.reports-chip-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.reports-table th,
.reports-table td {
    white-space: nowrap;
}

.reports-table td:nth-child(2),
.reports-table td:nth-child(3) {
    white-space: normal;
    min-width: 12rem;
}

.table-scroll {
    overflow-x: auto;
}

.maintenance-layout {
    align-items: start;
}

.maintenance-table tbody tr {
    cursor: pointer;
}

.maintenance-table tbody tr.selected {
    background: color-mix(in srgb, var(--panel-accent) 12%, var(--panel-bg));
}

.table-subline {
    color: var(--text-muted);
    font-size: 0.82rem;
}

.maintenance-checkbox-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.65rem 1rem;
    margin-top: 1rem;
}

.taxonomy-tab-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.catalog-grid-header {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: center;
    margin-bottom: 0.8rem;
}

.table-badge {
    display: inline-flex;
    align-items: center;
    margin-left: 0.45rem;
    padding: 0.08rem 0.42rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--panel-accent) 16%, var(--panel-bg));
    color: var(--text);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.02em;
}

.table-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    justify-content: flex-end;
}

.table-action {
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    border-radius: 999px;
    padding: 0.28rem 0.65rem;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
}

.table-action--danger {
    color: var(--status-danger);
}

.table-wrap {
    white-space: normal;
    word-break: break-word;
}

.selected-row {
    background: color-mix(in srgb, var(--panel-accent) 12%, var(--panel-bg));
}

.company-portal-editor {
    min-height: 24rem;
    font-family: "SFMono-Regular", ui-monospace, Menlo, Monaco, Consolas, monospace;
    font-size: 0.9rem;
    line-height: 1.45;
}

.utility-progress {
    width: 100%;
    height: 0.75rem;
    margin: 0.55rem 0 0.8rem;
}

[data-theme="dark"] .table-badge {
    background: color-mix(in srgb, var(--panel-accent) 20%, var(--panel-bg));
}

/* =============================================================
   First-run welcome dialog (PBI #13647 / Task #13758)
   ============================================================= */

.bidglory-welcome-backdrop {
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--text, #1a1a1a) 56%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    z-index: 5000;
    animation: bidglory-welcome-fade-in 0.18s ease-out;
}

.bidglory-welcome-backdrop[hidden] {
    display: none;
}

.bidglory-welcome-panel {
    max-width: 540px;
    width: 100%;
    margin: 0;
    box-shadow: 0 24px 80px -20px rgba(0, 0, 0, 0.45),
                0 0 0 4px color-mix(in oklab, var(--brand-glow) 22%, transparent);
}

.bidglory-welcome-list {
    margin: 0.85rem 0 1rem;
    padding-left: 1.2rem;
    line-height: 1.55;
}

.bidglory-welcome-list li + li {
    margin-top: 0.5rem;
}

.bidglory-welcome-tip {
    font-size: 0.8rem;
    color: var(--text-muted, #777);
    margin: 0.6rem 0 0.9rem;
}

.bidglory-welcome-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.4rem;
}

/* Body lock while modal is open — prevents scroll-bleed under the
   backdrop. Removed by welcome-dialog.js on dismiss. */
body.bidglory-welcome-open {
    overflow: hidden;
}

@keyframes bidglory-welcome-fade-in {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* =============================================================
   Version-features carousel (PBI #13647 / Task #13759)
   ============================================================= */

.bidglory-features-backdrop {
    position: fixed;
    inset: 0;
    background: color-mix(in srgb, var(--text, #1a1a1a) 56%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    z-index: 4900; /* below the welcome dialog (5000) */
    animation: bidglory-welcome-fade-in 0.18s ease-out;
}

.bidglory-features-backdrop[hidden] {
    display: none;
}

.bidglory-features-panel {
    width: 100%;
    max-width: 620px;
    margin: 0;
    box-shadow: 0 24px 80px -20px rgba(0, 0, 0, 0.45),
                0 0 0 4px color-mix(in oklab, var(--brand-glow) 22%, transparent);
}

.bidglory-features-header {
    margin-bottom: 0.85rem;
}

/* Track + slides — flex row of fixed-width slides; JS translates the
   track to bring the active slide into view. */
.bidglory-features-viewport {
    overflow: hidden;
    border-radius: 6px;
}

.bidglory-features-track {
    display: flex;
    transition: transform 0.25s ease-out;
    width: 100%;
}

.bidglory-features-slide {
    flex: 0 0 100%;
    width: 100%;
    padding: 0.4rem 0.2rem 0.6rem;
}

.bidglory-features-slide__heading {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.bidglory-features-footer {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 1.1rem;
}

.bidglory-features-nav {
    appearance: none;
    border: 1px solid var(--border, #d0d7de);
    background: var(--surface, #fff);
    color: var(--text, #1a1a1a);
    border-radius: 50%;
    width: 2rem;
    height: 2rem;
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
}

.bidglory-features-nav:hover:not(:disabled) {
    background: var(--surface-sunken, #f5f8fb);
    border-color: var(--brand-sea, #2563eb);
}

.bidglory-features-nav:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.bidglory-features-pos {
    font-size: 0.8rem;
    color: var(--text-muted, #777);
    font-variant-numeric: tabular-nums;
}

.bidglory-features-spacer {
    flex: 1;
}

body.bidglory-features-open {
    overflow: hidden;
}

/* ============================================================
   PBI #15274 — LoadingPanel + GridSkeleton (shared loading state)

   Sibling chrome to .empty-state-panel; deliberately reuses the
   .app-panel surface so a page's loading state and its loaded
   state share visual weight (no layout shift when data arrives).
   .grid-skeleton renders a pulse-animated row/cell grid for
   list/table pages where a layout-shaped placeholder reduces
   perceived latency over a bare "Loading…" line.
   ============================================================ */

.loading-panel {
    display: grid;
    gap: 0.75rem;
}

.loading-panel__message {
    margin: 0;
}

.grid-skeleton {
    display: grid;
    gap: 0.45rem;
}

.grid-skeleton__head,
.grid-skeleton__row {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.6rem;
    align-items: center;
}

.grid-skeleton__cell {
    display: block;
    height: 0.85rem;
    border-radius: 0.4rem;
    background: linear-gradient(
        90deg,
        color-mix(in oklab, var(--border) 55%, transparent) 0%,
        color-mix(in oklab, var(--border) 25%, transparent) 50%,
        color-mix(in oklab, var(--border) 55%, transparent) 100%);
    background-size: 200% 100%;
    animation: grid-skeleton-pulse 1.4s ease-in-out infinite;
}

.grid-skeleton__cell--head {
    height: 1.05rem;
    background: linear-gradient(
        90deg,
        color-mix(in oklab, var(--brand-sea) 28%, transparent) 0%,
        color-mix(in oklab, var(--brand-sea) 12%, transparent) 50%,
        color-mix(in oklab, var(--brand-sea) 28%, transparent) 100%);
    background-size: 200% 100%;
}

/* Pseudo-random cell widths so the bars don't read as a mechanical
   8-column ruler. Each cell rides at a different fraction of the
   1fr track and aligns to the start so the trailing whitespace
   reads as "this column is shorter than that one". */
.grid-skeleton__row .grid-skeleton__cell {
    justify-self: start;
    width: 90%;
}
.grid-skeleton__row .grid-skeleton__cell:nth-child(2) { width: 65%; }
.grid-skeleton__row .grid-skeleton__cell:nth-child(3) { width: 80%; }
.grid-skeleton__row .grid-skeleton__cell:nth-child(4) { width: 55%; }
.grid-skeleton__row .grid-skeleton__cell:nth-child(5) { width: 75%; }
.grid-skeleton__row .grid-skeleton__cell:nth-child(6) { width: 50%; }

@keyframes grid-skeleton-pulse {
    0%   { background-position: 100% 0; opacity: 0.85; }
    50%  { background-position: 0    0; opacity: 1; }
    100% { background-position: -100% 0; opacity: 0.85; }
}

@media (prefers-reduced-motion: reduce) {
    .grid-skeleton__cell {
        animation: none;
    }
}

/* =============================================================
   Tablet + touch layer
   -------------------------------------------------------------
   Everything below is layout and hit-area at the four --bp-*
   stops from theme.css. The carves above this point handle the
   grids that already had a breakpoint; this section covers the
   ones that never had one and the surfaces (Syncfusion grids,
   modals, raw tables) that need device-shape treatment rather
   than a column count.

   No colors, no --status-* tokens, no behavior.
   ============================================================= */

/* --- tablet landscape and below (1024px) ------------------------ */
@media (max-width: 1024px) {
    /* Company screen is a 1.6fr / 20rem split with no carve at all,
       so the side panel squeezed the form to ~26rem on an iPad. */
    .company-layout {
        grid-template-columns: 1fr;
    }

    /* The bid-workspace tab strip is the same problem NavMenu had:
       enough tabs to wrap, and wrapping costs a whole row above the
       grid. Swipe instead. */
    .legacy-tab-strip {
        /* #15698: a scroll container must also be the containing block, or
           absolutely-positioned descendants resolve against the initial
           containing block and land in the PAGE's overflow instead of this
           box's. The .visually-hidden SR span inside each tab button was
           escaping to x≈596 and scrolling a 390px viewport 207px sideways.
           Anchoring the strip (not the button) keeps the invariant in the
           same rule that creates the scroller, so they cannot drift. */
        position: relative;
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
        scrollbar-width: none;
    }

    .legacy-tab-strip::-webkit-scrollbar {
        display: none;
    }

    .legacy-tab-strip > * {
        flex: 0 0 auto;
    }

    /* A 22rem floor on the search box forces the whole filter row to
       overflow before its siblings get a chance to wrap. */
    .workspace-filter-search {
        min-width: 0;
        flex: 1 1 100%;
    }

    /* Four fixed-min columns need ~52rem; an iPad in landscape has
       about 60rem of content width once the shell padding is off, so
       this is right at the edge. Two columns is comfortable. */
    .item-dialog-package-row,
    .settings-rate-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* The 1.5rem scrim padding costs 3rem of width that a 1420px
       dialog on a 1024px tablet does not have to spare. Trim it, but
       keep enough that the dialog still reads as a layer above the
       page rather than a new screen. */
    .modal-scrim {
        padding: 0.85rem;
    }

    .item-dialog {
        max-height: calc(100dvh - 1.7rem);
    }
}

/* --- tablet portrait and below (768px) -------------------------- */
@media (max-width: 768px) {
    /* Dense BidDetail footer rows: label + field side by side needs a
       label column wide enough to read, which at 768 leaves the field
       under 4rem. Let the label size to its text and give the rest to
       the input; the 4-up edit row folds to two rows of two. */
    .workspace-footer-metric-row,
    .labor-block .workspace-footer-metric-row,
    .tax-block .workspace-footer-total {
        grid-template-columns: minmax(3.5rem, auto) minmax(0, 1fr);
    }

    .workspace-footer-edit-row,
    .tax-block .workspace-footer-edit-row {
        grid-template-columns: minmax(4.5rem, auto) minmax(0, 1fr);
    }

    .footer-status-group {
        grid-template-columns: minmax(4rem, auto) minmax(0, 1fr);
    }

    /* #15704: `display: block` on the table made it an ordinary block box,
       so the anonymous inner table sized to content and `width: 100%`
       (base rule) stopped filling a narrow panel; `white-space: nowrap`
       additionally stopped every cell wrapping. Hang the scroller on the
       PARENT instead — the table stays a table, keeps width:100% and keeps
       wrapping, and the parent still catches the overflow when min-content
       genuinely exceeds the viewport. Needs Safari 15.4+ / Chrome 105+. */
    :has(> .data-table) {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
    }

    .settings-rate-grid,
    .item-dialog-package-row,
    .maintenance-checkbox-grid,
    .export-columns-grid {
        grid-template-columns: 1fr;
    }

    /* Toolbars stop being a row of equals and start being a wrapped
       cluster with the primary action first. */
    .toolbar-row,
    .compact-toolbar,
    .compact-workspace-actions {
        gap: 0.4rem;
    }

    .toolbar-divider {
        display: none;
    }

    /* Right-aligned dialog footers push the primary button off the
       edge once three actions wrap. Full-width stack instead, in DOM
       order — the primary action is last in every footer in this
       codebase, so it lands at the bottom, nearest the thumb, and the
       visual order still matches the tab order. */
    .dialog-footer,
    .item-dialog-footer {
        flex-direction: column;
        align-items: stretch;
    }

    .dialog-footer > *,
    .item-dialog-footer > * {
        width: 100%;
        justify-content: center;
    }

    .dialog-footer__meta,
    .dialog-footer__back {
        margin-right: 0;
    }
}

/* --- phone (640px) ---------------------------------------------- */
@media (max-width: 640px) {
    /* Near-full-bleed — a hairline of scrim is all that is left, just
       enough to keep the dialog reading as a layer over the page. */
    .modal-scrim {
        padding: 0.4rem;
        /* Top-aligned rather than centered: a tall dialog that grows
           past the viewport would otherwise centre itself and clip
           equally off the top and the bottom, hiding its own header. */
        align-items: start;
    }

    .item-dialog {
        max-height: calc(100dvh - 0.8rem);
        padding: 0.9rem 0.85rem calc(0.9rem + var(--safe-b));
    }

    .preview-quick-grid {
        grid-template-columns: 1fr;
    }
}

/* --- Syncfusion grids on touch ---------------------------------- */
/* Keyed on the pointer, not the width: the grid is the same size
   problem on a 1024px iPad and a 1440px touch laptop. */
@media (pointer: coarse) {
    /* Syncfusion scrolls the grid body horizontally itself; these
       only make that scroll feel native and stop a horizontal swipe
       that runs off the last column from dragging the page with it. */
    .bidglory-grid.e-grid .e-gridcontent {
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
    }

    /* A 0.5rem row cell is a ~30px tap target and these rows are the
       primary way a user opens a bid. Taller rows, and the header
       filter/sort affordances get room to be hit accurately. */
    .bidglory-grid.e-grid .e-rowcell {
        padding-top: 0.7rem;
        padding-bottom: 0.7rem;
    }

    .bidglory-grid.e-grid .e-headercell {
        padding-top: 0.7rem;
        padding-bottom: 0.7rem;
    }

    /* The densest inputs in the app: the BidDetail footer edit rows pin
       `height: 1.66rem` with 0.7rem text — a 27px box with 11px type,
       which is not hittable or readable on a tablet. `height` (not
       min-height) is what's set, so min-height alone would do nothing;
       release it to auto and let the touch token drive the box. Kept at
       0.82rem rather than the 16px anti-zoom size because this grid is
       still four columns wide in landscape — the ≤768 rule below takes
       it the rest of the way once the row has stacked. */
    .workspace-footer-rows--editable .form-control {
        height: auto;
        min-height: var(--control-h-sm);
        font-size: 0.82rem;
        padding: 0.25rem 0.4rem;
    }
}

/* #15695: theme.css's `:root input.form-control { font-size: 16px }` is
   (0,2,1) and beats the (0,2,0) density rules above on EVERY coarse-pointer
   device, so the workspace ignored its own sizing on iPad. Raise specificity
   to (0,3,1)/(0,4,1) here rather than weakening the theme rule — that rule is
   the anti-focus-zoom net and must keep winning at phone widths. Gated to
   ≥769px, where the rows have not yet stacked; below that 16px is correct.
   Footer group stays AFTER the page group: the .form-control arms tie. */
@media (pointer: coarse) and (min-width: 769px) {
    :root .bid-workspace-page input.form-control,
    :root .bid-workspace-page textarea.form-control,
    :root .bid-workspace-page select.form-select,
    :root .bid-workspace-page .e-input-group input.e-input {
        font-size: 0.88rem;
    }

    :root .workspace-footer-rows--editable input.form-control,
    :root .workspace-footer-rows--editable .e-input-group input.e-input {
        font-size: 0.82rem;
    }
}


/* =============================================================
   Customer portal surfaces
   -------------------------------------------------------------
   #15696 / #15701. These rules previously lived in
   Components/Pages/Portal/Dashboard.razor.css and Login.razor.css
   as Blazor-isolated stylesheets, which broke two ways:

     1. ::deep compiles to "[scopeid] .foo" — a DESCENDANT
        combinator — so a ::deep rule can never match the
        component's OWN ROOT element. .portal-auth-shell and
        .portal-header are exactly that, so those rules were dead.
     2. The .portal-line-item / .portal-room-card / .portal-comments
        selectors live in PortalBidDetail.razor and
        CommentThread.razor, which have no .razor.css and therefore
        no scope id at all — so Dashboard's scope id could never
        match them either.

   Five components share this class namespace and only two of them
   carried a scope. Every selector here is .portal-* prefixed, so
   globalising collides with nothing — the same move .portal-empty
   already got under #15278.
   ============================================================= */

/* ---- portal: dashboard, bid detail, comments ---- */
/*
 * PBI #13780 / Task #13785 — portal theme polish.
 *
 * Scoped to Dashboard.razor + reused by PortalBidDetail/CommentThread
 * via deep selectors below. ALL colors via theme tokens — no hex.
 * The portal eventually serves logged-in customers; styling matches
 * the existing app shell (same theme.css tokens) so light + dark
 * theme parity is automatic.
 */

.portal-header {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}

.portal-header__brand {
    min-width: 0;
}

.portal-header .app-headline {
    margin: 0.15rem 0 0.4rem;
}

.portal-rep-card {
    background: var(--brand-sea-soft);
    border: 1px solid var(--border);
    border-radius: 0.55rem;
    padding: 0.85rem 1rem;
    min-width: 14rem;
    color: var(--text);
}

.portal-rep-card__name {
    font-weight: 600;
    margin: 0.25rem 0 0.5rem;
    color: var(--brand-sea-ink);
}

.portal-rep-card p {
    margin: 0.15rem 0;
    font-size: 0.9rem;
}

.portal-link {
    color: var(--brand-sea);
    text-decoration: none;
}

.portal-link:hover {
    text-decoration: underline;
}

.portal-section {
    margin-bottom: 1.75rem;
}

.portal-section__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.75rem;
}

.portal-section__title {
    margin: 0;
    font-size: 1.1rem;
    color: var(--text);
}

/* PBI #15278 — `.portal-empty` un-scoped and promoted to a global
   alias of `.empty-state-inline` (see app.css). Previously this rule
   only matched inside Dashboard.razor's scoped CSS so CommentThread
   and PortalBidDetail (both also using .portal-empty) rendered bare
   un-styled text. The rule now lives in app.css for all three. */

/* ── Bid card grid ─────────────────────────────────────── */

.portal-bid-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
    gap: 0.85rem;
}

.portal-bid-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.55rem;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.portal-bid-card:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}

.portal-bid-card__link {
    display: block;
    padding: 0.95rem 1rem;
    color: var(--text);
    text-decoration: none;
}

.portal-bid-card__heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
}

.portal-bid-card__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
}

.portal-bid-card__meta {
    margin: 0 0 0.5rem;
    color: var(--text-muted);
    font-size: 0.85rem;
}

.portal-bid-card__cta {
    margin: 0;
    color: var(--brand-sea);
    font-size: 0.9rem;
    font-weight: 600;
}

/* ── Rep list ──────────────────────────────────────────── */

.portal-rep-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
    gap: 0.6rem;
}

.portal-rep-list__item {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.45rem;
    padding: 0.75rem 0.9rem;
}

.portal-rep-list__name {
    margin: 0 0 0.2rem;
    font-weight: 600;
    color: var(--text);
}

.portal-rep-list__contact {
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-muted);
}

/* ── Bid detail: rooms + line items ─────────────────────── */

.portal-room-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.55rem;
    margin-bottom: 0.85rem;
    overflow: hidden;
}

.portal-room-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.65rem 1rem;
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border);
}

.portal-room-card__title {
    margin: 0;
    font-size: 0.95rem;
    color: var(--text);
}

.portal-room-card__total {
    font-weight: 600;
    color: var(--text);
}

.portal-line-items {
    list-style: none;
    margin: 0;
    padding: 0;
}

.portal-line-item {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 1rem;
    border-bottom: 1px solid var(--border);
}

.portal-line-item:last-child {
    border-bottom: none;
}

.portal-line-item__main {
    flex: 1 1 auto;
}

.portal-line-item__name {
    margin: 0;
    color: var(--text);
}

.portal-line-item__meta {
    margin: 0.15rem 0 0;
    font-size: 0.8rem;
    color: var(--text-muted);
}

.portal-line-item__price {
    margin: 0;
    flex: 0 0 auto;
    align-self: center;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ── Comment thread ─────────────────────────────────────── */

.portal-comments {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 0.55rem;
    padding: 1rem;
}

.portal-comments__compose {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-bottom: 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--border);
}

.portal-comments__label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text);
}

.portal-comments__name,
.portal-comments__body {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border);
    border-radius: 0.4rem;
    background: var(--surface);
    color: var(--text);
    font: inherit;
}

.portal-comments__body {
    min-height: 5rem;
    resize: vertical;
}

.portal-comments__error {
    margin: 0;
    padding: 0.4rem 0.6rem;
    color: var(--status-danger-ink);
    background: var(--status-danger-soft);
    border-radius: 0.35rem;
    font-size: 0.85rem;
}

.portal-comments__actions {
    display: flex;
    justify-content: flex-end;
}

.portal-comments__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
}

.portal-comment {
    background: var(--surface-sunken);
    border-left: 3px solid var(--brand-sea);
    border-radius: 0.4rem;
    padding: 0.65rem 0.85rem;
}

.portal-comment--rep {
    border-left-color: var(--status-success);
    background: var(--status-success-soft);
}

.portal-comment__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
}

.portal-comment__author {
    font-weight: 600;
    color: var(--text);
}

.portal-comment__when {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 0.8rem;
}

.portal-comment__body {
    margin: 0;
    color: var(--text);
    white-space: pre-wrap;
}

/* ── Responsive carve ──────────────────────────────────────
   The portal is the one surface in this app that is opened on a
   phone more often than anything else — a customer taps a link in
   an email to look at a bid. Phone is the design target here, not
   an afterthought, so this carve goes further than the internal
   pages do.

   Stops are the --bp-* scale from theme.css. The 768 stop was 720
   before this pass; 720 sat just under every iPad width, so an iPad
   in portrait held the two-column header and squeezed the bid list
   to a single narrow column of cards. */

@media (max-width: 768px) {
    .portal-header {
        grid-template-columns: 1fr;
        gap: 1rem;
    }

    .portal-rep-card {
        min-width: 0;
    }

    .portal-section {
        margin-bottom: 1.25rem;
    }
}

@media (max-width: 640px) {
    /* Title and action stop sharing a line — the action is usually a
       link whose label is longer than the space left beside a
       section title. */
    .portal-section__head {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.35rem;
    }

    /* Name-left / price-right holds until the name wraps to three
       lines and drags the price down beside its own last word.
       Stack, and let the price sit under the name as its own line. */
    .portal-line-item {
        flex-direction: column;
        gap: 0.2rem;
    }

    .portal-line-item__price {
        align-self: flex-start;
        font-weight: 600;
    }

    .portal-room-card__head {
        flex-wrap: wrap;
        gap: 0.25rem 0.75rem;
    }

    /* A right-aligned Post button on a phone is a 6rem target in the
       far corner. Full width instead. */
    .portal-comments__actions {
        justify-content: flex-start;
    }

    .portal-comments__actions > * {
        width: 100%;
        justify-content: center;
    }

    .portal-comments {
        padding: 0.85rem 0.75rem;
    }

    .portal-comment__head {
        flex-wrap: wrap;
    }

    .portal-comment__when {
        margin-left: 0;
        flex-basis: 100%;
    }
}

/* Touch targets — the portal's tap targets are links inside cards,
   which have no min-height of their own at all. */
@media (pointer: coarse) {
    .portal-bid-card__link {
        padding: 1.05rem 1rem;
    }

    .portal-link {
        display: inline-block;
        padding-block: 0.35rem;
    }

    .portal-comments__name,
    .portal-comments__body {
        min-height: var(--control-h);
        /* iOS zooms the page on focus below 16px; see theme.css. */
        font-size: 16px;
    }

    /* Hover lift latches after a tap and leaves one card floating. */
    .portal-bid-card:hover {
        transform: none;
    }
}

/* ---- portal: auth (login + password request) ---- */
/*
 * PBI #13786 / Task #13787 — portal auth pages styles.
 * Scoped via Login.razor's component CSS but applied to siblings
 * (PasswordRequest.razor) via selectors. Theme tokens only.
 */

.portal-auth-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    /* dvh, not vh: on iOS Safari 100vh is the height with the URL bar
       hidden, so a vh-based min-height pushes the card below the fold
       on first paint and the page opens scrolled. */
    min-height: calc(100dvh - 6rem);
    padding: 2rem 1rem calc(2rem + var(--safe-b));
}

.portal-auth-card {
    max-width: 30rem;
    width: 100%;
}

.portal-auth-form {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    margin-top: 1rem;
}

.portal-auth-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text);
    margin-top: 0.4rem;
}

.portal-auth-input {
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 0.4rem;
    background: var(--surface);
    color: var(--text);
    font: inherit;
}

.portal-auth-input:focus {
    outline: 2px solid var(--ring);
    outline-offset: 1px;
    border-color: var(--brand-sea);
}

.portal-auth-checkbox {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.35rem 0 0.6rem;
    font-size: 0.9rem;
    color: var(--text);
}

.portal-auth-banner {
    margin-top: 0.85rem;
    padding: 0.55rem 0.75rem;
    border-radius: 0.4rem;
    font-size: 0.9rem;
}

.portal-auth-banner--ok {
    background: var(--status-success-soft);
    color: var(--status-success-ink);
}

.portal-auth-banner--err {
    background: var(--status-danger-soft);
    color: var(--status-danger-ink);
}

.portal-auth-foot {
    margin: 1rem 0 0;
    text-align: center;
    font-size: 0.9rem;
}

/* ── Responsive carve ──────────────────────────────────────
   Sign-in is where a customer's session starts and it is nearly
   always started on a phone. Stops are the --bp-* scale in
   theme.css. */

@media (max-width: 640px) {
    .portal-auth-shell {
        /* Centering a card that is nearly as tall as the viewport
           just clips it symmetrically once the keyboard opens.
           Top-align and let the field the user tapped stay visible. */
        align-items: flex-start;
        padding: 1rem 0.85rem calc(1.5rem + var(--safe-b));
        min-height: 0;
    }

    .portal-auth-card {
        max-width: none;
    }

    .portal-auth-form .app-cta {
        width: 100%;
    }
}

@media (pointer: coarse) {
    .portal-auth-input {
        min-height: var(--control-h);
        /* 16px is the iOS focus-zoom threshold, not a design size —
           see the note in theme.css. */
        font-size: 16px;
    }

    /* A native checkbox is ~13px; the label is the real target, so
       give the whole row a tappable height. */
    .portal-auth-checkbox {
        min-height: var(--control-h);
    }
}
