/* =================================================================
   WF Portal — Data Studio Page Layout
   ================================================================= */

.ds-page {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* ── Library column (left) ───────────────────────────────────────── */

.ds-library {
    width: 300px;
    min-width: 300px;
    background-color: var(--theme-background-subtle);
    border-right: 1px solid var(--theme-border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* ── Composer column (center) ────────────────────────────────────── */

.ds-composer {
    flex: 1;
    padding: 1rem 1.25rem;
    overflow-y: auto;
}

/* ── Vertical selector bar (global admin only) ───────────────────── */

.ds-vertical-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: var(--theme-warning-muted, #fff8e1);
    border: 1px solid var(--theme-warning, #ffc107);
    border-radius: var(--theme-radius);
    font-size: var(--theme-font-size-sm);
}

.ds-vertical-bar label {
    font-weight: 600;
    white-space: nowrap;
    color: var(--theme-text);
}

.ds-vertical-bar select {
    max-width: 200px;
}

/* ── Entity selector bar ─────────────────────────────────────────── */

.ds-entity-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}

.ds-entity-bar label {
    font-weight: 600;
    white-space: nowrap;
    font-size: var(--theme-font-size-sm);
    color: var(--theme-text);
}

.ds-entity-bar select {
    max-width: 320px;
}

/* ── Entity header card ──────────────────────────────────────────── */

.ds-entity-header {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
    background: var(--theme-background-raised);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
}

.ds-entity-header-info {
    flex: 1;
    min-width: 0;
}

.ds-entity-header-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--theme-text);
    margin-bottom: 0.15rem;
}

.ds-entity-header-subtitle {
    font-size: var(--theme-font-size-xs);
    color: var(--theme-text-muted);
    font-family: var(--theme-font-mono);
}

.ds-entity-header-desc {
    font-size: var(--theme-font-size-sm);
    color: var(--theme-text-muted);
    margin-top: 0.35rem;
}

.ds-entity-header-actions {
    display: flex;
    gap: 0.35rem;
    flex-shrink: 0;
}

/* ── Section headers ─────────────────────────────────────────────── */

.ds-section-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--theme-border);
}

.ds-section-title {
    font-size: var(--theme-font-size-base);
    font-weight: 600;
    color: var(--theme-text);
}

.ds-section-badge {
    font-size: var(--theme-font-size-xs);
    color: var(--theme-text-muted);
}

/* ── Drop zones ──────────────────────────────────────────────────── */

.ds-drop-zone {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.75rem;
    margin-top: 0.5rem;
    border: 2px dashed var(--theme-border);
    border-radius: var(--theme-radius);
    color: var(--theme-text-muted);
    font-size: var(--theme-font-size-sm);
    transition: border-color 150ms ease, background-color 150ms ease;
}

.ds-drop-zone.active {
    border-color: var(--theme-primary);
    background-color: var(--theme-primary-muted);
    color: var(--theme-primary);
}

/* ── Empty state ─────────────────────────────────────────────────── */

.ds-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 60%;
    color: var(--theme-text-muted);
    text-align: center;
}

.ds-empty-icon {
    font-size: 3.5rem;
    margin-bottom: 1rem;
    opacity: 0.25;
}

.ds-empty-text {
    font-size: var(--theme-font-size-base);
    font-weight: 500;
    margin-bottom: 0.25rem;
}

.ds-empty-hint {
    font-size: var(--theme-font-size-sm);
    max-width: 340px;
    line-height: 1.5;
}

/* ── Child entities area ─────────────────────────────────────────── */

.ds-children-columns {
    display: flex;
    gap: 1.5rem;
    margin-top: 0.5rem;
}

.ds-children-column {
    flex: 1;
    min-width: 0;
}

.ds-children-column-header {
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
    color: var(--theme-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 0.5rem;
}

/* ── Catalog tab ─────────────────────────────────────────────────── */

.ds-catalog-category {
    margin-bottom: 0.25rem;
}

.ds-catalog-category-header {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.75rem;
    font-size: var(--theme-font-size-xs);
    font-weight: 600;
    color: var(--theme-text-muted);
    cursor: pointer;
    user-select: none;
    background: var(--theme-background-hover);
    border-radius: var(--theme-radius-button);
    margin: 0.15rem 0.25rem;
    transition: background-color 120ms ease;
}

.ds-catalog-category-header:hover {
    background: var(--theme-border);
}

.ds-catalog-item {
    padding-right: 0.4rem;
}

.ds-catalog-import-btn {
    padding: 0.1rem 0.3rem;
    font-size: 0.6rem;
    line-height: 1;
    border-radius: var(--theme-radius-button);
    flex-shrink: 0;
    opacity: 0.5;
    transition: opacity 120ms ease;
}

.ds-catalog-item:hover .ds-catalog-import-btn {
    opacity: 1;
}

.ds-catalog-entity {
    border-bottom: 1px solid var(--theme-border);
}

.ds-catalog-entity:last-child {
    border-bottom: none;
}

.ds-catalog-desc {
    padding: 0.15rem 0.75rem 0.35rem 2.2rem;
    font-size: 0.65rem;
    color: var(--theme-text-muted);
    line-height: 1.3;
    font-style: italic;
}

.ds-catalog-preview {
    padding: 0.1rem 0.5rem 0.4rem 1.8rem;
}

.ds-catalog-preview-item {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.12rem 0.4rem;
    font-size: 0.65rem;
    color: var(--theme-text-muted);
}

.ds-catalog-preview-item .wf-dtype-badge {
    font-size: 0.5rem;
}

/* ── Linked badge ────────────────────────────────────────────────── */

.ds-linked-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.1rem 0.4rem;
    font-size: 0.6rem;
    font-weight: 500;
    border-radius: 999px;
    background-color: var(--theme-info-muted, var(--theme-background-hover));
    color: var(--theme-info, var(--theme-primary));
    white-space: nowrap;
}

/* Composite-key summary in the entity header — yellow tint, like the property "Key N" badges */
.ds-entity-key-summary {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 500;
    border-radius: 4px;
    background-color: rgba(255, 193, 7, 0.15);
    color: #806000;
    border: 1px solid rgba(255, 193, 7, 0.4);
    white-space: nowrap;
    vertical-align: middle;
}

/* Child-relationship summary badge */
.ds-entity-rel-summary {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 500;
    border-radius: 4px;
    background-color: rgba(13, 202, 240, 0.12);
    color: #0a6e82;
    border: 1px solid rgba(13, 202, 240, 0.4);
    white-space: nowrap;
    vertical-align: middle;
}

/* =================================================================
   Responsive — Tablet
   ================================================================= */

@media (max-width: 991.98px) {
    .ds-library {
        width: 240px;
        min-width: 240px;
    }
    .ds-children-columns {
        flex-direction: column;
    }
}

/* =================================================================
   Responsive — Mobile
   ================================================================= */

@media (max-width: 767.98px) {
    .ds-page {
        flex-direction: column;
        flex: none;
    }
    .ds-library {
        width: 100%;
        min-width: unset;
        max-height: 40vh;
        border-right: none;
        border-bottom: 1px solid var(--theme-border);
    }
    .ds-composer {
        min-height: 60vh;
    }
    .ds-children-columns {
        flex-direction: column;
    }
}

/* ── Section Tabs ─────────────────────────────────────── */

.ds-section-tabs {
    display: flex;
    gap: 0;
    border-bottom: 2px solid var(--theme-border);
    margin-bottom: 1rem;
}

.ds-section-tab {
    padding: 0.5rem 1.25rem;
    font-weight: 600;
    font-size: 0.875rem;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    color: var(--theme-text-muted);
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.ds-section-tab:hover {
    color: var(--theme-text);
}

.ds-section-tab.active {
    color: var(--theme-primary);
    border-bottom-color: var(--theme-primary);
}

.ds-section-tab .badge {
    font-size: 0.7rem;
    margin-left: 0.35rem;
    vertical-align: middle;
}

/* ── Data Browser ─────────────────────────────────────── */

.ds-data-toolbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}

.ds-data-toolbar .form-control {
    max-width: 280px;
}

.ds-data-toolbar .ds-data-count {
    font-size: 0.8rem;
    color: var(--theme-text-muted);
    margin-left: auto;
}

.ds-data-table {
    overflow-x: auto;
}

.ds-data-table table {
    font-size: 0.82rem;
}

.ds-data-table th {
    cursor: pointer;
    /* Allow header text to wrap so long labels ("SERVICE LOCATION / ACCESS
       INSTRUCTIONS") don't force the column wider than its data. But cap
       visually at 2 lines so an ultra-narrow column doesn't stack the
       header text into a disruptive 4-line tower.

       DO NOT use `display:-webkit-box` + `-webkit-line-clamp` here — that
       property requires block display, which removes the th from table-
       cell layout and causes every th to stack vertically in the first
       column (headers become a left-side tower, data rows flow below).
       Use line-height + max-height as a soft clamp instead; keeps the th
       as display:table-cell. No ellipsis on line 2, but clipped cleanly. */
    white-space: normal;
    line-height: 1.2;
    max-height: 2.4em;
    overflow: hidden;
    vertical-align: bottom;
    user-select: none;
    /* Leave room on the right for the .ds-col-resize-grip overlay. */
    padding-right: 0.75rem;
}

.ds-data-table th:hover {
    background-color: var(--theme-background-subtle);
}

.ds-data-cell {
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ds-data-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 0.75rem 0;
    font-size: 0.85rem;
}

.ds-data-detail {
    background: var(--theme-background-subtle);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    padding: 1rem;
    margin-top: 0.75rem;
}

.ds-data-detail dt {
    font-weight: 600;
    font-size: 0.8rem;
    color: var(--theme-text-muted);
}

.ds-data-detail dd {
    margin-bottom: 0.5rem;
    word-break: break-all;
}

.ds-data-empty {
    text-align: center;
    padding: 3rem 1rem;
    color: var(--theme-text-muted);
}

.ds-data-empty i {
    font-size: 2.5rem;
    display: block;
    margin-bottom: 0.75rem;
    opacity: 0.4;
}

/* ── Import Wizard ────────────────────────────────────── */

.ds-import-steps {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.ds-import-step {
    flex: 1;
    text-align: center;
    padding: 0.4rem 0;
    font-size: 0.75rem;
    font-weight: 600;
    border-bottom: 3px solid var(--theme-border);
    color: var(--theme-text-muted);
}

.ds-import-step.active {
    border-bottom-color: var(--theme-primary);
    color: var(--theme-primary);
}

.ds-import-step.done {
    border-bottom-color: var(--theme-success, #28a745);
    color: var(--theme-success, #28a745);
}

.ds-import-mapping {
    max-height: 400px;
    overflow-y: auto;
}

.ds-import-mapping table td {
    vertical-align: middle;
}

.ds-import-preview {
    max-height: 300px;
    overflow: auto;
    font-size: 0.8rem;
}

.ds-import-results {
    text-align: center;
    padding: 1.5rem;
}

.ds-import-results .ds-result-number {
    font-size: 1.75rem;
    font-weight: 700;
}

.ds-import-results .ds-result-label {
    font-size: 0.8rem;
    color: var(--theme-text-muted);
}

.ds-import-dropzone {
    border: 2px dashed var(--theme-border);
    border-radius: var(--theme-radius);
    padding: 2rem;
    text-align: center;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
}

.ds-import-dropzone:hover {
    border-color: var(--theme-primary);
    background: var(--theme-background-subtle);
}

/* ── View Selector ───────────────────────────────────── */

.ds-view-select {
    max-width: 200px;
    font-size: 0.8rem;
}

.ds-toolbar-spacer {
    flex: 1;
}

/* ── Column Picker ───────────────────────────────────── */

.ds-column-picker {
    background: var(--theme-background-raised);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    padding: 0.75rem;
    margin-bottom: 0.75rem;
}

.ds-column-picker-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
}

.ds-column-picker-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
    max-height: 200px;
    overflow-y: auto;
}

.ds-column-picker-item {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.8rem;
    cursor: pointer;
    white-space: nowrap;
}

.ds-column-picker-item input[type="checkbox"] {
    margin: 0;
}

/* ── Advanced Filter Builder ──────────────────────────── */

.ds-filter-builder {
    background: var(--theme-background-raised);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    padding: 0.75rem;
    margin-bottom: 0.75rem;
}

.ds-filter-builder-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.ds-filter-rule {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.35rem;
}

/* Incomplete rule (empty / whitespace value) — visible flag instead of
   silent drop. Mirror of FormFilterBar's .ef-filterbar-rule.is-incomplete
   styling. Red-tinted left border + flag icon make it clear the rule
   exists but won't apply until the user enters a value. */
.ds-filter-rule.is-incomplete {
    border-left: 3px solid var(--theme-danger);
    padding-left: 0.375rem;
    background-color: var(--theme-danger-bg-subtle);
    border-radius: var(--theme-radius-sm);
}

.ds-filter-rule-flag {
    color: var(--theme-danger);
    font-size: 0.85rem;
    flex-shrink: 0;
    cursor: help;
}

.ds-filter-conj {
    width: 70px;
    flex-shrink: 0;
    font-size: 0.75rem;
}

.ds-filter-conj-label {
    width: 70px;
    flex-shrink: 0;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--theme-text-muted);
    text-align: center;
}

.ds-filter-field {
    width: 140px;
    flex-shrink: 0;
    font-size: 0.8rem;
}

.ds-filter-op {
    width: 110px;
    flex-shrink: 0;
    font-size: 0.8rem;
}

.ds-filter-value {
    flex: 1;
    min-width: 100px;
    font-size: 0.8rem;
}

.ds-filter-actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

/* ── Top-level Match pill (AND / OR) ─────────────────────
   SSMS-grade compact toggle. Visible only when meaningful
   (≥2 groups OR top-level rule + group). Pre-PR-322 the
   filter UI had no top-level combinator at all — flat
   AND/OR per-rule was the only option.
   Mirrors the .ds-report-match-* pattern in the Report
   Builder so the two surfaces feel consistent without
   sharing component code. */

.ds-filter-toplevel {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.5rem;
    margin-bottom: 0.5rem;
    background: var(--theme-info-bg-subtle, rgba(13, 202, 240, 0.08));
    border: 1px solid var(--theme-info-border-subtle, rgba(13, 202, 240, 0.2));
    border-radius: var(--theme-radius-sm);
    font-size: 0.8rem;
}

.ds-filter-toplevel-label {
    font-weight: 600;
    color: var(--theme-text-muted);
    flex-shrink: 0;
}

.ds-filter-toplevel-hint {
    font-style: italic;
    color: var(--theme-text-muted);
    font-size: 0.75rem;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Two-button pill — the active button picks up the info
   color, inactive button stays neutral. Tighter padding
   than a regular .btn-sm so the chip stays compact. */
.ds-filter-pill {
    display: inline-flex;
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius-pill, 999px);
    overflow: hidden;
    flex-shrink: 0;
}

.ds-filter-pill-btn {
    background: transparent;
    border: none;
    padding: 0.2rem 0.6rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--theme-text-muted);
    cursor: pointer;
    transition: background-color 0.1s ease, color 0.1s ease;
}

.ds-filter-pill-btn:hover {
    background: var(--theme-background-hover);
    color: var(--theme-text);
}

.ds-filter-pill-btn.active {
    background: var(--theme-info);
    color: var(--theme-text-on-info, #fff);
}

.ds-filter-pill-btn.active:hover {
    background: var(--theme-info-hover, var(--theme-info));
    color: var(--theme-text-on-info, #fff);
}

/* ── Indented sub-group container ─────────────────────────
   Vertical accent bar on the left + soft indented padding
   makes nested groups visually distinct from top-level
   rules. The header carries the group's AND/OR logic
   selector + Ungroup + ✕ controls. */

.ds-filter-group {
    border-left: 3px solid var(--theme-info, #0dcaf0);
    background: var(--theme-background-subtle, rgba(0, 0, 0, 0.02));
    border-radius: 0 var(--theme-radius-sm) var(--theme-radius-sm) 0;
    padding: 0.4rem 0.5rem 0.4rem 0.6rem;
    margin: 0.4rem 0 0.4rem 0.5rem;
}

.ds-filter-group-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.35rem;
    font-size: 0.8rem;
}

.ds-filter-group-label {
    font-weight: 600;
    color: var(--theme-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-size: 0.7rem;
}

.ds-filter-group-logic {
    width: 70px;
    flex-shrink: 0;
    font-size: 0.75rem;
}

.ds-filter-group-actions {
    margin-top: 0.35rem;
}

/* In-group rule rows — slightly tighter than top-level so
   the visual hierarchy stays clear (top-level rules are
   the "outer scope"; in-group rules are nested children). */
.ds-filter-rule-in-group {
    margin-bottom: 0.25rem;
}

.ds-filter-rule-in-group .ds-filter-conj-label {
    color: var(--theme-info, #0dcaf0);
    text-transform: lowercase;
    font-style: italic;
}

/* ── Saved-sort builder ─────────────────────────────────────
   Sits inside the filter builder panel, separated by a
   subtle border. Visual style mirrors the filter rules so
   the two view-definition surfaces feel like peer controls.
   Order top-down = priority; the leading number badge makes
   priority obvious at a glance. */

.ds-sort-builder {
    margin-top: 0.75rem;
    padding-top: 0.6rem;
    border-top: 1px dashed var(--theme-border);
}

.ds-sort-builder-header {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.4rem;
    font-size: 0.85rem;
}

.ds-sort-builder-hint {
    font-style: italic;
    color: var(--theme-text-muted);
    font-size: 0.7rem;
    margin-left: 0.25rem;
}

.ds-sort-rule {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.3rem;
}

.ds-sort-rule.is-incomplete {
    border-left: 3px solid var(--theme-danger);
    padding-left: 0.375rem;
    background-color: var(--theme-danger-bg-subtle);
    border-radius: var(--theme-radius-sm);
}

/* Priority badge — leading element so the user sees
   "1, 2, 3..." going down. Compact + colored like the info
   chip pattern. */
.ds-sort-priority {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--theme-info, #0dcaf0);
    color: var(--theme-text-on-info, #fff);
    font-size: 0.7rem;
    font-weight: 700;
    cursor: help;
}

.ds-sort-reorder {
    display: inline-flex;
    flex-direction: column;
    gap: 0.05rem;
    flex-shrink: 0;
}

.ds-sort-actions {
    display: flex;
    gap: 0.5rem;
    margin-top: 0.4rem;
}

/* ── Date Filters ────────────────────────────────────── */

.ds-date-filters {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: var(--theme-warning-muted, #fff8e1);
    border: 1px solid var(--theme-warning, #ffc107);
    border-radius: var(--theme-radius);
    font-size: 0.8rem;
}

.ds-history-badge {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
    padding: 0.15rem 0.5rem;
    font-size: 0.7rem;
    font-weight: 600;
    border-radius: 999px;
    background: var(--theme-warning, #ffc107);
    color: #000;
}

/* ── Group Bar ───────────────────────────────────────── */

.ds-group-bar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
}

.ds-group-header-row {
    background: var(--theme-background-subtle) !important;
}

.ds-group-header-row td {
    padding: 0.5rem 0.75rem !important;
    font-size: 0.85rem;
    border-top: 2px solid var(--theme-border);
}

.ds-group-summary {
    font-weight: 400;
    color: var(--theme-text-muted);
    margin-left: 0.75rem;
    font-size: 0.8rem;
}

/* ── Export Dropdown ──────────────────────────────────── */

.ds-export-group {
    position: relative;
}

.ds-export-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 1050;
    min-width: 160px;
    background: var(--theme-background-raised);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    padding: 0.25rem 0;
    margin-top: 0.25rem;
}

.ds-export-option {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.4rem 0.75rem;
    font-size: 0.8rem;
    border: none;
    background: none;
    color: var(--theme-text);
    cursor: pointer;
    transition: background-color 120ms ease;
}

.ds-export-option:hover {
    background: var(--theme-background-hover);
}

/* ── Views Tab Items ─────────────────────────────────── */

.ds-view-item {
    transition: background-color 120ms ease;
}

.ds-view-item.active {
    background: var(--theme-primary-muted, rgba(13, 110, 253, 0.08));
    border-left: 3px solid var(--theme-primary);
}

/* ── Inline Edit Cells ───────────────────────────────── */

.ds-edit-cell {
    padding: 0.1rem 0.15rem !important;
}

.ds-edit-input {
    width: 100%;
    border: 1px solid var(--theme-border);
    border-radius: 3px;
    padding: 0.15rem 0.35rem;
    font-size: 0.8rem;
    background: var(--theme-background-raised);
    transition: border-color 150ms ease, box-shadow 150ms ease;
}

.ds-edit-input:focus {
    border-color: var(--theme-primary);
    box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.15);
    outline: none;
}

/* ── "Seed now" button for date/time/datetime edit cells ───────────
   The <input type="date|time|datetime-local"> widgets are annoying
   to populate from scratch (step buttons, spinner, no keyboard
   autocomplete for today/now). A small clock button sitting flush
   to the right of the input fills the current value and saves in
   one click. Wrapping div holds input + button side by side so the
   input stretches to fill remaining cell width while the button
   stays a fixed size. tabindex="-1" on the button keeps Tab-order
   clean — users keyboarding through the row skip past the seed
   shortcut and land on the next input, same as if the button
   weren't there. */
.ds-edit-cell-wrap {
    display: flex;
    align-items: stretch;
    gap: 2px;
    width: 100%;
}

.ds-edit-cell-wrap > .ds-edit-input,
.ds-edit-cell-wrap > .ds-card-input {
    flex: 1 1 auto;
    min-width: 0; /* lets the input shrink below its content size */
}

.ds-edit-seed-btn {
    flex: 0 0 auto;
    width: 1.5rem;
    padding: 0;
    border: 1px solid var(--theme-border);
    border-radius: 3px;
    background: var(--theme-background-subtle);
    color: var(--theme-text-muted);
    font-size: 0.7rem;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
}

.ds-edit-seed-btn:hover {
    background: var(--theme-primary-muted, #e3f0ff);
    color: var(--theme-primary);
    border-color: var(--theme-primary);
}

.ds-edit-seed-btn:active {
    transform: translateY(1px);
}

/* Save feedback animations */
.ds-row-saving {
    opacity: 0.7;
}

.ds-row-saved {
    animation: ds-flash-green 1.5s ease-out;
}

@keyframes ds-flash-green {
    0% { background-color: rgba(25, 135, 84, 0.2); }
    100% { background-color: transparent; }
}

/* ── Card View ───────────────────────────────────────── */

.ds-card-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.ds-card {
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    background: var(--theme-background-raised);
    overflow: hidden;
    transition: box-shadow 150ms ease;
}

.ds-card:hover {
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

.ds-card-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: var(--theme-background-subtle);
    border-bottom: 1px solid var(--theme-border);
    font-size: 0.85rem;
}

.ds-card-body {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.5rem;
    padding: 0.75rem;
}

.ds-card-field {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.ds-card-label {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--theme-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.ds-card-value {
    font-size: 0.85rem;
    color: var(--theme-text);
    min-height: 1.5em;
}

.ds-card-input {
    font-size: 0.85rem;
}

/* ── Report Builder ──────────────────────────────────────────────── */

.ds-report-builder {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.ds-report-section {
    border: 1px solid var(--theme-border);
    border-radius: 6px;
    padding: 0.75rem 1rem;
    margin-bottom: 0.75rem;
    background: var(--theme-surface);
}

.ds-report-section-label {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--theme-text-muted);
    margin-bottom: 0.6rem;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* Accordion: each numbered section is now a <details>/<summary>. The chevron is a pure
   CSS triangle before the step pill — rotates 90° when the section is open.
   `.ds-report-runbar` opts out of accordion styling: it's always a plain <div>. */
.ds-report-accordion > summary.ds-report-section-label {
    cursor: pointer;
    list-style: none;   /* hide default Firefox/Safari marker */
    user-select: none;
    margin-bottom: 0;   /* tighter when collapsed */
}
.ds-report-accordion > summary.ds-report-section-label::-webkit-details-marker {
    display: none;       /* hide default Chrome/Edge triangle */
}
.ds-report-accordion > summary.ds-report-section-label::before {
    content: "";
    display: inline-block;
    width: 0.45rem;
    height: 0.45rem;
    border-right: 2px solid var(--theme-text-muted);
    border-bottom: 2px solid var(--theme-text-muted);
    transform: rotate(-45deg);
    margin-right: 0.25rem;
    transition: transform 0.15s ease;
    flex-shrink: 0;
}
.ds-report-accordion[open] > summary.ds-report-section-label {
    margin-bottom: 0.6rem;
}
.ds-report-accordion[open] > summary.ds-report-section-label::before {
    transform: rotate(45deg);
}
.ds-report-accordion > summary.ds-report-section-label:hover::before {
    border-color: var(--theme-primary);
}

/* Summary info — the "3 rules · Match All" preview text next to the section title.
   Muted, mixed-case, non-semibold so it reads as status not as another header label. */
.ds-report-section-summary {
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.78rem;
    color: var(--theme-text);
    opacity: 0.8;
    margin-left: 0.5rem;
    /* Truncate aggressively when space is tight — the full state is visible when expanded. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex-shrink: 1;
}
.ds-report-section-summary.text-danger {
    opacity: 1;
    font-weight: 600;
}

/* Run bar — no accordion affordance. Visually identical to the old section. */
.ds-report-runbar > .ds-report-section-label {
    margin-bottom: 0.6rem;
}

/* AND/OR match toggle — appears when 2+ filter rules exist.
   Segmented pill style: subtle resting state, theme-primary accent when selected. */
.ds-report-match-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.15rem 0.3rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--theme-primary) 18%, transparent);
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.7rem;
}
.ds-report-match-label {
    color: var(--theme-text-muted);
    padding-left: 0.35rem;
}
.ds-report-match-btn {
    border: 0;
    padding: 0.15rem 0.7rem;
    border-radius: 999px;
    background: transparent;
    color: var(--theme-text-muted);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease, transform 0.08s ease;
}
.ds-report-match-btn:hover {
    color: var(--theme-text);
    background: color-mix(in srgb, var(--theme-primary) 8%, transparent);
}
.ds-report-match-btn:active {
    transform: scale(0.97);
}
.ds-report-match-btn.active {
    background: var(--theme-primary);
    color: var(--theme-primary-contrast, #fff);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--theme-primary) 40%, transparent);
}

.ds-report-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    border-radius: 50%;
    background: var(--theme-primary);
    color: var(--theme-text-on-primary);
    font-size: 0.7rem;
    font-weight: 700;
    flex-shrink: 0;
}

/* Entity rows */
.ds-report-entity-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.1rem;
    padding: 0.15rem 0;
}

.ds-report-entity-role {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    min-width: 54px;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
    background: var(--theme-primary);
    color: var(--theme-text-on-primary);
    text-align: center;
    flex-shrink: 0;
}

.ds-report-entity-role.joined {
    background: #0dcaf0;
    color: #000;
}

.ds-report-entity-role.add {
    background: transparent;
    border: 1px dashed var(--theme-primary);
    color: var(--theme-primary);
}

.ds-report-entity-select {
    max-width: 240px;
    flex: 1;
}

.ds-report-entity-name {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--theme-text);
}

.ds-report-entity-remove {
    padding: 0.15rem 0.4rem;
    line-height: 1;
}

.ds-report-add-join {
    margin-top: 0.25rem;
}

/* Column picker */
.ds-report-col-toggle {
    font-size: 0.7rem !important;
}

.ds-report-col-group {
    margin-bottom: 0.5rem;
}

.ds-report-col-group-label {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--theme-text-muted);
    padding: 0.2rem 0.5rem;
    background: var(--theme-background-subtle);
    border-radius: 3px;
    margin-bottom: 0.35rem;
    display: inline-block;
}

.ds-report-col-checks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 0.5rem;
    padding-left: 0.25rem;
}

.ds-report-col-item {
    font-size: 0.8rem;
    cursor: pointer;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
    border: 1px solid transparent;
    transition: background 0.1s;
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    color: var(--theme-text-muted);
}

.ds-report-col-item.selected {
    color: var(--theme-text);
    background: var(--theme-background-subtle);
    border-color: var(--theme-border);
}

.ds-report-col-item:hover {
    background: var(--theme-background-subtle);
    border-color: var(--theme-border);
}

/* Filter rows */
.ds-report-filter-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
    flex-wrap: wrap;
}

/* Incomplete report-filter rule — same red-tinted treatment as
   ds-filter-rule.is-incomplete and ef-filterbar-rule.is-incomplete.
   "Incomplete" envelope is broader for reports: missing entity,
   missing property, OR (when op needs it) missing value all qualify. */
.ds-report-filter-row.is-incomplete {
    border-left: 3px solid var(--theme-danger);
    padding-left: 0.375rem;
    background-color: var(--theme-danger-bg-subtle);
    border-radius: var(--theme-radius-sm);
}

.ds-report-filter-flag {
    color: var(--theme-danger);
    font-size: 0.85rem;
    flex-shrink: 0;
    cursor: help;
    margin-left: 0.25rem;
}

/* PR #326 — Report Builder sibling-group filter UX. Each group is a
   paren-wrapped set of rules — visualized as an indented panel with a
   label header + per-rule rows. Groups combine via the outer match
   toggle (top of the Filters section). */
.ds-report-filter-group {
    border-left: 3px solid var(--theme-info);
    padding: 0.5rem 0.75rem;
    margin: 0.5rem 0;
    background: var(--theme-info-bg-subtle, var(--theme-background-subtle));
    border-radius: var(--theme-radius-sm);
}

.ds-report-filter-group-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.25rem;
    font-size: 0.85rem;
}

.ds-report-filter-group-label {
    font-weight: 600;
    color: var(--theme-info-emphasis, var(--theme-text));
}

.ds-report-filter-grouped-row {
    margin-left: 0.5rem;
}

/* PR #326 — Report Builder multi-column sort UX. Mirrors ds-sort-rule
   but adds an entity dropdown + priority chip styled like the GroupBy
   rows so the visual rhythm of the Report Builder stays consistent. */
.ds-report-sort-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
    flex-wrap: wrap;
}

.ds-report-sort-row.is-incomplete {
    border-left: 3px solid var(--theme-danger);
    padding-left: 0.375rem;
    background-color: var(--theme-danger-bg-subtle);
    border-radius: var(--theme-radius-sm);
}

.ds-report-sort-priority {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    padding: 0 0.4rem;
    background: var(--theme-primary);
    color: var(--theme-primary-contrast);
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 600;
    flex-shrink: 0;
    cursor: help;
}

.ds-report-sort-dir {
    min-width: 4.25rem;
    flex: 0 0 auto;
}

.ds-report-sort-reorder {
    display: inline-flex;
    gap: 0.125rem;
    flex-shrink: 0;
}

.ds-report-filter-entity {
    max-width: 140px;
    flex: 0 0 auto;
}

.ds-report-filter-entity-label {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    min-width: 80px;
    padding: 0.15rem 0.4rem;
    border-radius: 3px;
    background: var(--theme-primary);
    color: var(--theme-text-on-primary);
    text-align: center;
    flex-shrink: 0;
}

.ds-report-filter-field {
    max-width: 180px;
    flex: 1 1 auto;
}

.ds-report-filter-op {
    max-width: 150px;
    flex: 0 0 auto;
}

.ds-report-filter-value {
    max-width: 200px;
    flex: 1 1 auto;
}

.ds-report-filter-value-placeholder {
    flex: 1 1 auto;
    max-width: 200px;
}

/* ═══════════════════════════════════════════════════════════════════
   Relationships tab — composite keys + parents + children
   ═══════════════════════════════════════════════════════════════════ */

.ds-rel-container {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 0.5rem 0;
}

.ds-rel-section {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 6px;
    padding: 1rem;
    background: var(--bs-body-bg, #fff);
}

.ds-rel-section-header {
    margin-bottom: 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--bs-border-color, #dee2e6);
}

.ds-rel-section-title {
    font-weight: 600;
    font-size: 1rem;
    display: flex;
    align-items: center;
}

.ds-rel-section-subtitle {
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.85rem;
    margin-top: 0.15rem;
}

.ds-rel-subsection-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--bs-secondary-color, #6c757d);
    margin: 0.75rem 0 0.4rem 0.25rem;
}

/* Key slot cards — the 3 composite-key slots */
.ds-key-slots {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.ds-key-slot {
    flex: 1 1 0;
    min-width: 140px;
    padding: 0.65rem 0.75rem;
    border-radius: 6px;
    border: 2px solid transparent;
    background: var(--bs-light, #f8f9fa);
}

.ds-key-slot.filled {
    border-color: var(--theme-warning, #ffc107);
    background: rgba(255, 193, 7, 0.08);
}

.ds-key-slot.empty {
    border-color: var(--bs-border-color, #dee2e6);
    border-style: dashed;
}

.ds-key-slot-label {
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-secondary-color, #6c757d);
    margin-bottom: 0.25rem;
}

.ds-key-slot.filled .ds-key-slot-label {
    color: var(--theme-warning-dark, #997404);
}

.ds-key-slot-name {
    font-weight: 500;
    font-size: 0.9rem;
    color: var(--bs-body-color, #212529);
}

.ds-key-slot-display {
    font-size: 0.75rem;
    color: var(--bs-secondary-color, #6c757d);
    margin-top: 0.1rem;
}

.ds-key-slot-empty {
    font-size: 0.8rem;
    font-style: italic;
}

/* Entity cards for parents / children — clickable navigation */
.ds-rel-list {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.ds-rel-entity-card {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 5px;
    background: var(--bs-body-bg, #fff);
    cursor: pointer;
    text-align: left;
    transition: background-color 120ms, border-color 120ms;
    font-size: 0.875rem;
}

.ds-rel-entity-card:hover {
    background: var(--bs-light, #f8f9fa);
    border-color: var(--bs-primary, #0d6efd);
}

.ds-rel-entity-name {
    font-weight: 600;
    color: var(--bs-body-color, #212529);
}

.ds-rel-entity-muted {
    color: var(--bs-secondary-color, #6c757d);
    font-size: 0.8rem;
    font-style: italic;
}

/* ═══════════════════════════════════════════════════════════════════
   Report Builder — Phase D polish (parent hint, chip picker, join mode)
   ═══════════════════════════════════════════════════════════════════ */

.ds-report-parent-hint {
    padding: 0.4rem 0.6rem;
    margin: 0.35rem 0;
    background: rgba(13, 110, 253, 0.04);
    border-left: 3px solid var(--bs-info, #0dcaf0);
    border-radius: 4px;
    font-size: 0.8rem;
}

.ds-report-add-related {
    margin-top: 0.5rem;
    padding: 0.5rem;
    background: var(--bs-light, #f8f9fa);
    border-radius: 5px;
}

.ds-report-add-related-label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--bs-secondary-color, #6c757d);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 0.4rem;
}

.ds-report-related-chips {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.ds-report-related-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    padding: 0.3rem 0.6rem;
    border: 1px solid var(--bs-primary, #0d6efd);
    border-radius: 999px;
    background: var(--bs-body-bg, #fff);
    color: var(--bs-body-color, #212529);
    font-size: 0.825rem;
    cursor: pointer;
    transition: background-color 120ms, transform 80ms;
}

.ds-report-related-chip:hover {
    background: var(--theme-primary);
    color: var(--theme-text-on-primary);
    transform: translateY(-1px);
}

.ds-report-related-chip:hover i.text-primary {
    color: var(--theme-text-on-primary) !important;
}

.ds-report-joinmode {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.6rem;
    padding: 0.4rem 0.6rem;
    background: rgba(0, 0, 0, 0.02);
    border-radius: 5px;
    flex-wrap: wrap;
}

.ds-report-joinmode-label {
    font-size: 0.8rem;
    font-weight: 500;
    color: var(--bs-body-color, #212529);
}

/* Phase G: chained joins in Report Builder */
.ds-report-related-group {
    margin-bottom: 0.5rem;
}

.ds-report-related-group:last-child {
    margin-bottom: 0;
}

.ds-report-related-group-label {
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--bs-secondary-color, #6c757d);
    margin-bottom: 0.25rem;
    padding-left: 0.1rem;
}

.ds-report-entity-via {
    font-size: 0.75rem;
    margin-left: 0.25rem;
}

/* =================================================================
   Phase P2 — Variance / Time-Travel styling
   ================================================================= */

/* Section wrapper gets a subtle clock-themed accent when variance is on */
.ds-variance-section {
    border-left: 3px solid var(--bs-info, #0dcaf0);
    padding-left: 0.75rem;
    transition: border-color 120ms ease-in-out;
}

/* Cells flagged as CHANGED — warm yellow tint, strongest signal */
.ds-variance-cell-changed {
    background-color: rgba(255, 193, 7, 0.18) !important;
    border-left: 2px solid rgba(255, 193, 7, 0.85) !important;
    font-weight: 500;
    cursor: help;
}

/* Cells flagged as ADDED — fresh green tint */
.ds-variance-cell-added {
    background-color: rgba(25, 135, 84, 0.14) !important;
    border-left: 2px solid rgba(25, 135, 84, 0.75) !important;
    cursor: help;
}

/* Cells flagged as REMOVED — muted red tint */
.ds-variance-cell-removed {
    background-color: rgba(220, 53, 69, 0.14) !important;
    border-left: 2px solid rgba(220, 53, 69, 0.75) !important;
    color: var(--bs-secondary-color, #6c757d);
    text-decoration: line-through;
    cursor: help;
}

/* Whole-row accent when _rowStatus = "added" (new leaf pair in this snapshot) */
.ds-variance-row-added > td:first-child {
    box-shadow: inset 3px 0 0 rgba(25, 135, 84, 0.5);
}

/* Badges used in the legend reuse cell styling for consistency */
.badge.ds-variance-cell-changed,
.badge.ds-variance-cell-added,
.badge.ds-variance-cell-removed {
    font-size: 0.7rem;
    padding: 0.18rem 0.45rem;
    border-radius: 0.25rem;
}

/* ── Status column badges (variance report "added" / "changed" / etc.) ──
   Theme-variable driven so they remain readable on light AND dark themes.
   The previous Bootstrap subtle/emphasis pair inverted poorly on some themes
   and rendered white-on-white in the status column. Solid colored text on
   the theme-muted bg + a faint border lets the badge read as a pill-shape
   even when the row behind it is tinted. */
.ds-status-badge {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    line-height: 1.2;
    letter-spacing: 0.02em;
    text-transform: lowercase;
    background: var(--theme-surface);
    color: var(--theme-text);
    border: 1px solid var(--theme-border);
}
.ds-status-added {
    background: var(--theme-success-muted);
    color: var(--theme-success);
    border-color: var(--theme-success);
}
.ds-status-changed {
    background: var(--theme-warning-muted);
    color: var(--theme-warning);
    border-color: var(--theme-warning);
}
.ds-status-removed {
    background: var(--theme-danger-muted);
    color: var(--theme-danger);
    border-color: var(--theme-danger);
}
.ds-status-unchanged {
    background: var(--theme-surface);
    color: var(--theme-text-muted);
    border-color: var(--theme-border);
}

/* ── Report Viewer cell display modes — compact (default) vs wrap ──
   Compact: truncate long values with ellipsis + show full value in the
   title tooltip. Wrap: multi-line cells reveal everything without hover.
   Headers get resize:horizontal so the user can drag a column header wider
   for a little extra breathing room — native CSS, no JS; affects only the
   header but gives a visible handle the user can grip. */
/* !important needed to beat the base .ds-data-cell { white-space: nowrap } that
   ships from the original data-browser styling — without it, the base rule
   applied even when the parent class was .ds-data-table-wrap because of an
   unrelated rule's higher source-order position. Explicit override keeps the
   toggle predictable: wrap mode always wraps, compact always truncates. */
.ds-data-table-compact .ds-data-cell {
    max-width: 260px;
    white-space: nowrap !important;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ds-data-table-wrap .ds-data-cell {
    white-space: normal !important;
    word-break: break-word;
    max-width: none;
    text-overflow: clip;
    overflow: visible;
}
/* Column resize — real drag-to-resize via JSResizableColumnsService. Native CSS
   `resize: horizontal` on a <th> doesn't propagate to the column (table-layout:auto
   overrides it). The JS module flips table-layout to fixed and appends a
   .ds-col-resize-grip to each <th> that dispatches mousedown/move/up to update
   th.style.width. Grip positioned absolute at the right edge. */
.ds-data-table-compact > thead th,
.ds-data-table-wrap    > thead th {
    position: relative;
    min-width: 90px;
    overflow: hidden;
}

/* Subtle vertical separator between columns — indicates where column boundaries are
   so the user can target the resize grip. Uses the theme border variable so it stays
   visible in both light and dark themes. Right-edge placement keeps the last column
   clean (no trailing divider). */
.ds-data-table-compact > thead > tr > th:not(:last-child),
.ds-data-table-wrap    > thead > tr > th:not(:last-child),
.ds-data-table-compact > tbody > tr > td:not(:last-child),
.ds-data-table-wrap    > tbody > tr > td:not(:last-child) {
    border-right: 1px solid var(--theme-border);
}

.ds-col-resize-grip {
    position: absolute;
    top: 0;
    right: 0;
    width: 6px;
    height: 100%;
    cursor: col-resize;
    user-select: none;
    background: transparent;
    transition: background-color 0.1s ease;
    z-index: 2;
}
.ds-col-resize-grip:hover {
    background: var(--theme-primary);
    opacity: 0.45;
}
.ds-col-resize-grip:active {
    background: var(--theme-primary);
    opacity: 0.8;
}

/* Property chip picker — drives the per-property filter above the snapshot grid.
   "Drifted" chips (properties that actually changed) get the primary theme accent so the
   user's eye lands on them; "stable" chips are dimmer. Selected chips invert. */
.ds-variance-chips {
    padding: 0.5rem 0.75rem;
    background: color-mix(in srgb, var(--theme-primary) 3%, transparent);
    border: 1px solid var(--theme-border);
    border-radius: 6px;
}
.ds-variance-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--theme-primary) 30%, transparent);
    background: var(--theme-surface);
    color: var(--theme-text);
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease, transform 0.08s ease;
}
.ds-variance-chip:hover {
    border-color: var(--theme-primary);
    background: color-mix(in srgb, var(--theme-primary) 6%, transparent);
}
.ds-variance-chip:active {
    transform: scale(0.97);
}
.ds-variance-chip.selected {
    background: var(--theme-primary);
    color: var(--theme-primary-contrast, #fff);
    border-color: var(--theme-primary);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--theme-primary) 30%, transparent);
}
.ds-variance-chip.stable {
    opacity: 0.55;
    border-style: dashed;
}
.ds-variance-chip.stable:hover {
    opacity: 1;
}
.ds-variance-chip-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.3rem;
    height: 1.15rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: color-mix(in srgb, currentColor 15%, transparent);
    color: inherit;
    font-size: 0.7rem;
    font-weight: 700;
}
.ds-variance-chip-range {
    font-size: 0.7rem;
    font-weight: 500;
    color: color-mix(in srgb, currentColor 65%, transparent);
    font-family: var(--bs-font-monospace);
}
.ds-variance-quick {
    font-size: 0.78rem;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
}

/* Phase P2.2 — per-row action cell in EntityDataBrowser grid. Narrow column with a
   subtle hover state so the time-travel button reads as "available but not shouty". */
.ds-row-actions {
    width: 40px;
    text-align: center;
    vertical-align: middle;
}

.ds-row-actions .btn {
    opacity: 0.55;
    transition: opacity 120ms ease-in-out;
}

.ds-row-actions .btn:hover,
.ds-row-actions .btn:focus {
    opacity: 1;
}

/* Phase P2.2 — VarianceView modal table styling. The property-×-snapshot grid can get
   wide quickly; this pin strategy keeps the property name column visible while the user
   scrolls snapshots horizontally inside the .table-responsive wrapper. */
.modal-dialog-scrollable .table thead.sticky-top th {
    position: sticky;
    top: 0;
    background-color: var(--bs-table-bg, #f8f9fa);
    z-index: 2;
}

.modal-dialog-scrollable .table tbody th[scope="row"] {
    position: sticky;
    left: 0;
    background-color: var(--bs-body-bg, #fff);
    z-index: 1;
    min-width: 180px;
}

/* =================================================================
   Phase P2.3 — Group By & Aggregate section polish
   ================================================================= */

/* Section accent — subtle coloured left border ties it visually to the help text. */
.ds-groupby-section {
    border-left: 3px solid var(--bs-info-bg-subtle, #cff4fc);
    padding-left: 0.75rem;
}

/* Inline help block shown when the section is empty — builds the mental model. */
.ds-groupby-help {
    padding: 0.5rem 0.75rem;
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    border-radius: 0.375rem;
}

/* Subhead label between dimension rows and measure rows. */
.ds-groupby-subhead {
    padding-left: 0.25rem;
    letter-spacing: 0.02em;
}

/* Dimension rows — primary (blue) accent on left edge. */
.ds-groupby-dim-row {
    border-left: 3px solid var(--bs-primary, #0d6efd);
    padding-left: 0.5rem;
    background-color: var(--bs-primary-bg-subtle, rgba(13, 110, 253, 0.04));
    border-radius: 0.25rem;
}

/* Measure rows — success (green) accent. Different colour than dimensions so the
   eye instantly distinguishes "what groups the rows" from "what gets computed." */
.ds-groupby-measure-row {
    border-left: 3px solid var(--bs-success, #198754);
    padding-left: 0.5rem;
    background-color: var(--bs-success-bg-subtle, rgba(25, 135, 84, 0.04));
    border-radius: 0.25rem;
}

/* Error state — warning border when the row has an incomplete or invalid config. */
.ds-groupby-row-error {
    border-left-color: var(--bs-warning, #ffc107) !important;
    background-color: var(--bs-warning-bg-subtle, rgba(255, 193, 7, 0.08)) !important;
}

/* Up/down reorder buttons — compact, muted, only active colour on hover. */
.ds-groupby-move {
    width: 1.25rem;
    opacity: 0.5;
    transition: opacity 120ms ease-in-out;
}

.ds-groupby-move:hover,
.ds-groupby-move:focus {
    opacity: 1;
}

.ds-groupby-move:disabled {
    opacity: 0.15;
    cursor: default;
}

/* Aggregate-kind dropdown width (was inline style — moving to class for consistency). */
.ds-groupby-kind {
    max-width: 8rem;
}

.ds-groupby-alias {
    max-width: 12rem;
}

/* ── Add Relationship Dialog + Relationships tab affordances ────────────
   The "+ Add parent" / "+ Add child" buttons live in each section header.
   Empty-state CTA turns the "no relationships yet" message into a clickable
   call-to-action so users on a blank entity aren't left wondering where
   to start.
*/
.ds-rel-section-header {
    position: relative;
}

.ds-rel-add-btn {
    position: absolute;
    top: 0;
    right: 0;
    font-size: 0.75rem;
    padding: 0.15rem 0.5rem;
}

.ds-rel-empty-cta {
    display: block;
    width: 100%;
    text-align: left;
    background: var(--theme-background-subtle);
    border: 1px dashed var(--theme-border);
    border-radius: var(--theme-radius);
    padding: 0.75rem 1rem;
    color: var(--theme-text-muted);
    font-size: 0.85rem;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s;
}

.ds-rel-empty-cta:hover {
    background: var(--theme-background);
    border-color: var(--theme-primary);
    color: var(--theme-primary);
}

/* Dialog entity picker — scrollable list of selectable entities */
.ds-add-rel-picker {
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    background: var(--theme-background);
}

.ds-add-rel-picker-item {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0.4rem 0.75rem;
    border: 0;
    background: transparent;
    text-align: left;
    font-size: 0.85rem;
    border-bottom: 1px solid var(--theme-border-subtle, var(--theme-border));
    cursor: pointer;
}

.ds-add-rel-picker-item:last-child {
    border-bottom: 0;
}

.ds-add-rel-picker-item:hover {
    background: var(--theme-background-subtle);
}

.ds-add-rel-picker-item.selected {
    background: var(--theme-primary);
    color: var(--theme-primary-contrast, #fff);
}

.ds-add-rel-picker-item.selected .text-muted {
    color: var(--theme-primary-contrast, #fff) !important;
    opacity: 0.8;
}

.ds-add-rel-name {
    font-weight: 600;
}

/* Cardinality radios as tile-cards */
.ds-add-rel-cardinality {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
}

.ds-add-rel-card {
    display: flex;
    flex-direction: column;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    cursor: pointer;
    transition: background-color 0.1s, border-color 0.1s;
}

.ds-add-rel-card:hover {
    background: var(--theme-background-subtle);
}

.ds-add-rel-card.selected {
    border-color: var(--theme-primary);
    background: var(--theme-primary-subtle, rgba(13,110,253,0.08));
}

.ds-add-rel-card input[type=radio] {
    display: none;
}

.ds-add-rel-card-label {
    font-weight: 600;
    font-size: 0.85rem;
}

.ds-add-rel-card-hint {
    font-size: 0.75rem;
    color: var(--theme-text-muted);
    margin-top: 0.15rem;
}

/* ── Forms tab (Phase 2c.2) ──────────────────────────────────────────── */

.ds-forms-tab {
    padding: 0.5rem 0;
}

.ds-forms-toolbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0;
    margin-bottom: 0.75rem;
}

.ds-forms-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 3rem 1rem;
    text-align: center;
    color: var(--theme-text-muted);
    border: 2px dashed var(--theme-border);
    border-radius: var(--theme-radius);
}

.ds-forms-empty i {
    font-size: 2rem;
    color: var(--theme-text-muted);
}

.ds-forms-list {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.ds-forms-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 0.75rem;
    background-color: var(--theme-background-raised);
    border: 1px solid var(--theme-border);
    border-radius: var(--theme-radius);
    transition: var(--theme-transition);
}

.ds-forms-row:hover {
    background-color: var(--theme-background-hover);
    border-color: var(--theme-border);
}

/* Deep-link highlight — flashes a row that was just landed via
   ?formId=X / ?reportId=X / ?viewId=X so the user's eye lands on the
   target immediately. Animation runs once; the CSS class is removed
   by the page's consume-once logic after the flash completes. Works
   on any .ds-*-row — re-used for forms, reports, views lists. */
.ds-row-highlighted,
.ds-forms-row.ds-row-highlighted {
    animation: ds-row-flash 2.5s ease-in-out;
    background-color: var(--theme-primary-muted);
    border-color: var(--theme-primary);
}

@keyframes ds-row-flash {
    0%   { background-color: var(--theme-primary); border-color: var(--theme-primary); }
    15%  { background-color: var(--theme-primary); border-color: var(--theme-primary); }
    100% { background-color: var(--theme-primary-muted); border-color: var(--theme-primary); }
}

.ds-forms-row-name {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--theme-text);
}

.ds-forms-row-desc {
    font-size: var(--theme-font-size-xs);
    color: var(--theme-text-muted);
    margin-top: 0.125rem;
}

.ds-forms-row-badges {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    flex-shrink: 0;
}

.ds-forms-row-actions {
    display: flex;
    gap: 0.25rem;
    flex-shrink: 0;
}

/* =================================================================
   Phase 3.4 binary-cell rendering — thumbnails + click-to-zoom
   -----------------------------------------------------------------
   Tokens that drive sizing (default values below; override on a
   container for photo-focused reports):
     --ds-thumb-max-h   max thumbnail height — KEEPS ROWS COMPACT
     --ds-thumb-max-w   max thumbnail width
     --ds-thumb-radius  corner rounding
   A tenant with a "Fleet Photos of the Day" report can set
   .photo-report { --ds-thumb-max-h: 240px; --ds-thumb-max-w: 320px; }
   on their report container and suddenly every binary column becomes
   a full-bleed photo grid without touching this file.
   ================================================================= */

.ds-thumb {
    max-height: var(--ds-thumb-max-h, 40px);
    max-width:  var(--ds-thumb-max-w, 80px);
    border-radius: var(--ds-thumb-radius, 4px);
    object-fit: contain;
    cursor: zoom-in;
    display: inline-block;
    vertical-align: middle;
    background-color: var(--theme-background-subtle);
    transition: transform 80ms ease, box-shadow 120ms ease;
}
.ds-thumb:hover {
    transform: scale(1.04);
    box-shadow: 0 0 0 2px var(--theme-primary-muted);
}

.ds-file-link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: var(--theme-primary);
    text-decoration: none;
    font-size: var(--theme-font-size-sm);
    max-width: 100%;
    min-width: 0;
}
.ds-file-link:hover {
    text-decoration: underline;
}
.ds-file-link .bi {
    flex-shrink: 0;
    font-size: 1rem;
}
.ds-file-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* ── Fullscreen zoom overlay ─────────────────────────────────────
   Rendered only when a thumbnail is clicked. Fixed-position with
   generous z-index so it sits over every dialog / modal the Portal
   can throw at it. Click the dim backdrop to close — the image
   itself stops propagation so clicking on the image doesn't dismiss.
   ================================================================= */

.ds-zoom-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: rgba(0, 0, 0, 0.82);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    cursor: zoom-out;
    animation: ds-zoom-fade-in 140ms ease-out;
}

@keyframes ds-zoom-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.ds-zoom-image {
    max-width: 96vw;
    max-height: 88vh;
    object-fit: contain;
    border-radius: 8px;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6);
    cursor: default;
    background-color: #fff;
}

.ds-zoom-caption {
    margin-top: 1rem;
    color: rgba(255, 255, 255, 0.85);
    font-size: var(--theme-font-size-sm);
    max-width: 90vw;
    text-align: center;
    cursor: default;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}

.ds-zoom-close {
    position: absolute;
    top: 1rem;
    right: 1rem;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    font-size: 1.2rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 120ms ease;
}
.ds-zoom-close:hover {
    background: rgba(255, 255, 255, 0.25);
}

/* ── Print media — clamp thumbnails further + hide zoom UI ───────
   Print mode is where "no matter what they hand us" most bites.
   A grid with 10 photo columns can easily blow a page apart. We
   clamp to a page-safe size by default; photo-focused reports
   with .photo-report can still override via their own @media print.
   The zoom overlay gets hidden regardless — it's a UI affordance,
   not content. If the print column is too narrow to show a usable
   thumbnail, the filename-only file-icon path still works.
   ================================================================= */

@media print {
    .ds-thumb {
        max-height: var(--ds-thumb-print-max-h, 60px);
        max-width:  var(--ds-thumb-print-max-w, 100px);
        cursor: default;
        box-shadow: none;
    }
    .ds-thumb:hover {
        transform: none;
        box-shadow: none;
    }
    .ds-zoom-overlay,
    .ds-zoom-close,
    .ds-zoom-image,
    .ds-zoom-caption {
        display: none !important;
    }
    .ds-file-link {
        color: #000;
        text-decoration: none;
    }
}

/* ────────────────────────────────────────────────────────────────────
   SQL Query tab — Logic.SqlConverter playground
   Two-column layout: SQL textarea on the left, JSON output on the right.
   On narrow viewports they stack vertically.
   ──────────────────────────────────────────────────────────────────── */
.ds-sql-query-panel {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    padding: 1rem 0;
}

.ds-sql-query-toolbar {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}

.ds-sql-query-eyebrow {
    font-family: 'DM Mono', ui-monospace, monospace;
    font-size: .7rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--theme-muted);
    margin-right: auto;
}

.ds-sql-query-context {
    font-size: .8rem;
    color: var(--theme-text);
    background: var(--theme-bg-alt);
    padding: .2rem .55rem;
    border-radius: 999px;
    border: 1px solid var(--theme-border);
}

.ds-sql-query-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
    /* Compact authoring area — leaves room for the results grid below.
       Previously was 60vh which pushed Run results below the fold; the
       editor + JSON pane don't need that much height to be useful. */
    min-height: 14rem;
}

@media (max-width: 980px) {
    .ds-sql-query-grid {
        grid-template-columns: 1fr;
    }
}

.ds-sql-query-input,
.ds-sql-query-output {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    min-height: 0;
}

.ds-sql-query-label {
    font-family: 'DM Mono', ui-monospace, monospace;
    font-size: .7rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--theme-muted);
}

.ds-sql-query-textarea {
    flex: 1;
    min-height: 12rem;
    font-family: 'DM Mono', ui-monospace, Consolas, monospace;
    font-size: .85rem;
    line-height: 1.55;
    padding: 1rem;
    background: var(--theme-code-bg, #0d0e10);
    color: var(--theme-code-fg, #e1e1e1);
    border: 1px solid var(--theme-border);
    border-radius: 6px;
    resize: vertical;
    tab-size: 2;
}

.ds-sql-query-textarea:focus {
    outline: 2px solid var(--theme-accent);
    outline-offset: -2px;
}

.ds-sql-query-hint {
    font-size: .75rem;
    color: var(--theme-muted);
}

.ds-sql-query-placeholder {
    flex: 1;
    display: grid;
    place-items: center;
    border: 1px dashed var(--theme-border);
    border-radius: 6px;
    font-size: .9rem;
    padding: 2rem;
    text-align: center;
}

.ds-sql-query-issues {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-bottom: .25rem;
}

.ds-sql-query-issue {
    font-size: .82rem;
    padding: .45rem .65rem;
    border-radius: 4px;
    border: 1px solid var(--theme-border);
    background: var(--theme-bg-alt);
}

.ds-sql-query-issue strong {
    font-family: 'DM Mono', ui-monospace, monospace;
    font-size: .68rem;
    letter-spacing: .12em;
    margin-right: .35rem;
}

.ds-sql-query-issue-error {
    border-color: var(--theme-danger, #c0392b);
    background: rgba(192, 57, 43, .08);
}

.ds-sql-query-issue-warning {
    border-color: var(--theme-warning, #c08a2c);
    background: rgba(192, 138, 44, .08);
}

.ds-sql-query-issue-info {
    border-color: var(--theme-info, #3498db);
    background: rgba(52, 152, 219, .06);
}

.ds-sql-query-issue-pos {
    font-family: 'DM Mono', ui-monospace, monospace;
    font-size: .7rem;
    color: var(--theme-muted);
}

.ds-sql-query-json {
    flex: 1;
    margin: 0;
    padding: 1rem;
    font-family: 'DM Mono', ui-monospace, Consolas, monospace;
    font-size: .8rem;
    line-height: 1.55;
    background: var(--theme-code-bg, #0d0e10);
    color: var(--theme-code-fg, #e1e1e1);
    border: 1px solid var(--theme-border);
    border-radius: 6px;
    overflow: auto;
    white-space: pre;
    max-height: 18rem;
}

.ds-sql-query-actions {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
    margin-top: .5rem;
}

/* Per-pane action row — Convert buttons sit immediately under their
   own textarea (right edge of SQL pane, left edge of JSON pane) so the
   button is in the logical place for what it acts on. */
.ds-sql-query-pane-actions {
    display: flex;
    margin-top: .35rem;
}
.ds-sql-query-input  .ds-sql-query-pane-actions { justify-content: flex-end; }
.ds-sql-query-output .ds-sql-query-pane-actions { justify-content: flex-start; }

/* Load dropdown — compact + sits next to the Sample button. */
.ds-sql-query-load {
    max-width: 18rem;
    margin-right: .5rem;
}

/* JSON textarea variant — same monospace + dark background as SQL but
   shorter (no need for full editor real-estate; users mostly paste). */
.ds-sql-query-textarea-json {
    min-height: 10rem;
}
