/* =============================================================================
   OBaseFramework — component styles (Bootstrap 5 CSS-variable driven)
   Auto-adapts to whatever theme the consuming app applies via [data-bs-theme].
   ============================================================================= */

/* ----- Root + card wrap ----- */
.obase-table {
    --obase-row-h: 52px;
}

.obase-table .card-header {
    padding: 0.85rem 1.1rem;
    background: transparent;
    border-bottom: 1px solid var(--bs-border-color);
}
.obase-table .card-title {
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: -0.005em;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--bs-emphasis-color);
}
.obase-table .card-title .badge {
    font-weight: 600;
    font-size: 0.75rem;
    padding: 0.32em 0.55em;
    border-radius: 999px;
}
.obase-table .card-body { padding: 0; }

/* ----- Toolbar (search + refresh + columns + export) ----- */
.obase-toolbar       { gap: 0.5rem; flex-wrap: wrap; }
.obase-toolbar-btn   {
    border: 1px solid var(--bs-border-color);
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    width: 34px; height: 34px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
    font-size: 0.95rem;
    padding: 0;
}
.obase-toolbar-btn:hover { background: var(--bs-tertiary-bg); border-color: var(--bs-primary); color: var(--bs-primary); }
.obase-toolbar-btn:active { transform: scale(0.94); }

/* Inline-SVG icons (Bi helper). Sized via the SVG's own width/height=1em so the
   icon inherits the surrounding font-size; vertical-align keeps text aligned. */
.obase-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
    flex-shrink: 0;
    fill: currentColor;
}

/* Filetype tints for the export menu — Excel green, CSV slate-blue. Override the
   default currentColor fill on .obase-icon so the menu rows show the format at a
   glance instead of two identical monochrome glyphs. */
.obase-icon-excel { color: #1d6f42; fill: #1d6f42; }
.obase-icon-csv   { color: #4f6478; fill: #4f6478; }
[data-bs-theme="dark"] .obase-icon-excel { color: #2ec27e; fill: #2ec27e; }
[data-bs-theme="dark"] .obase-icon-csv   { color: #9ab1c4; fill: #9ab1c4; }

/* Spinning icon while loading */
.obase-spin { animation: obase-rotate 0.9s linear infinite; display: inline-block; }
@keyframes obase-rotate { from { transform: rotate(0); } to { transform: rotate(360deg); } }

/* Dropdown menus (columns / export). Anchor with logical `inset-inline-end` so
   the panel hangs from the same logical side of the toggle button regardless of
   document direction — LTR drops left-of-right-edge, RTL drops right-of-left-edge.
   Physical `right: 0` would always anchor to the same visual side and push the
   panel off-screen in RTL. */
.obase-menu-wrap { position: relative; }
.obase-menu {
    position: absolute;
    top: calc(100% + 6px);
    inset-inline-end: 0;
    z-index: 60;
    min-width: 220px;
    background: var(--bs-card-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 0.35rem 0;
    font-size: 0.875rem;
    max-height: 380px;
    overflow-y: auto;
}
.obase-menu-header {
    padding: 0.35rem 0.85rem 0.45rem;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-body-color);
    opacity: 0.6;
    border-bottom: 1px solid var(--bs-border-color);
    margin-bottom: 0.25rem;
}
.obase-menu-item {
    display: flex;
    align-items: center;
    width: 100%;
    text-align: start;
    padding: 0.45rem 0.85rem;
    background: transparent;
    border: none;
    color: var(--bs-body-color);
    cursor: pointer;
    transition: background 0.12s;
}
.obase-menu-item:hover { background: var(--bs-tertiary-bg); }

/* ──────────────────────────────────────────────────────────────────────────
   Per-column filter (Excel-style)
   ────────────────────────────────────────────────────────────────────────── */
.obase-filter-wrap {
    position: relative;
    display: inline-block;
    margin-inline-start: 0.25rem;
    line-height: 1;
}
.obase-filter-icon {
    /* Suppress every default button affordance — we want a faint inline icon,
       not a button. Includes appearance reset for WebKit and the focus ring. */
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    outline: 0;
    box-shadow: none;
    background: transparent;
    padding: 0 0.15rem;
    color: var(--bs-body-color);
    opacity: 0.35;
    cursor: pointer;
    line-height: 1;
    transition: opacity 120ms ease, color 120ms ease;
}
.obase-filter-icon:hover { opacity: 0.9; }
.obase-filter-icon:focus,
.obase-filter-icon:focus-visible { outline: 0; box-shadow: none; }
.obase-filter-icon:active { background: transparent; }
.obase-filter-icon--active { color: var(--bs-primary); opacity: 1; }
.obase-filter-icon--active:hover { opacity: 1; }
/* The .obase-menu chain bumps specificity past any host-theme override; the
   ::before pseudo-element is the *actual* opaque surface — see comment on it. */
.obase-menu.obase-filter-popup {
    /* Fixed width — never inherit the (often narrow) column width. */
    width: 280px;
    min-width: 280px;
    max-width: calc(100vw - 2rem);
    /* Default anchor: extends from the inline-start (logical left edge of the
       funnel) outward. End-edge columns use .obase-filter-popup--right to flip.
       Logical properties keep both directions correct under [dir="rtl"]. */
    inset-inline-start: 0;
    inset-inline-end: auto;
    /* Flex column so only the value list scrolls — header / search / footer
       stay visible at all times. Overrides .obase-menu's overflow-y:auto so the
       whole menu doesn't scroll as one block (which was hiding Apply/Clear). */
    display: flex;
    flex-direction: column;
    max-height: 440px;
    overflow: hidden;
    padding: 0;
    /* Background-color alone wasn't enough on some hosts (rows behind ghosted
       through despite !important — almost certainly an ancestor opacity or
       backdrop-filter context affecting paint). The ::before below is the real
       opaque layer; this background-color is kept as a fallback. */
    background-color: #ffffff !important;
    background-image: none !important;
    opacity: 1 !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    isolation: isolate;                  /* new stacking context — required by the ::before z-index trick */
    z-index: 1080;                       /* above sticky table columns + Bootstrap modals */
    /* Opt this element out of Edge's "Auto Dark Mode for Web Contents" flag and
       any browser-extension forced-dark layer — they re-paint computed-white
       elements as translucent gray, which is what produces the ghosted-rows
       effect that no CSS rule on the popup can fix on its own. */
    color-scheme: light;
    forced-color-adjust: none;
    color: #0f172a;
    border: 1px solid rgba(15, 23, 42, 0.12);
    box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18), 0 4px 10px rgba(15, 23, 42, 0.08);
}
/* The opaque surface. A positioned pseudo-element with solid color cannot be
   made see-through by ancestor opacity / backdrop-filter — it draws its own
   pixels at its own compositing layer. `z-index: 0` keeps it behind the popup
   content but in front of *outside* elements (the `isolation: isolate` on the
   popup container scopes z-index inside the popup). */
.obase-menu.obase-filter-popup::before {
    content: '';
    position: absolute;
    inset: 0;
    background-color: #ffffff;
    border-radius: inherit;
    z-index: 0;
    pointer-events: none;
}
/* Lift every direct child above the ::before surface so the content shows on
   top of the opaque layer, not behind it. */
.obase-menu.obase-filter-popup > * {
    position: relative;
    z-index: 1;
}
/* The popup lives inside <th>, which is `text-transform: uppercase` for header
   styling. That inherits into every child node — including the value labels,
   search placeholder, and footer buttons — and made the dropdown shout in caps.
   Reset it for everything inside the popup so values render in their natural
   case. The .obase-menu-header (".Filter by COLUMN") gets the uppercase look
   back explicitly because its own rule needs to win. */
.obase-menu.obase-filter-popup,
.obase-menu.obase-filter-popup * {
    text-transform: none;
    letter-spacing: normal;
}
.obase-menu.obase-filter-popup > .obase-menu-header {
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
[data-bs-theme="dark"] .obase-menu.obase-filter-popup {
    background-color: #1f2937 !important;
    color: #f1f5f9;
    border-color: rgba(255, 255, 255, 0.12);
    color-scheme: dark;
}
[data-bs-theme="dark"] .obase-menu.obase-filter-popup::before {
    background-color: #1f2937;
}
/* Right-anchor variant — used by header-render logic for the last 2 visible
   columns so the popup grows leftward and stays on-screen.
   IMPORTANT: must chain `.obase-menu` to match the (0,2,0) specificity of the
   `.obase-menu.obase-filter-popup` base rule above. Without the chain this
   modifier (0,1,0) loses to the base rule and the flip never takes effect. */
.obase-menu.obase-filter-popup--right {
    inset-inline-start: auto;
    inset-inline-end: 0;
}
.obase-filter-popup > .obase-menu-header {
    flex: 0 0 auto;
    background: rgba(15, 23, 42, 0.03);
    margin-bottom: 0;
}
[data-bs-theme="dark"] .obase-filter-popup > .obase-menu-header {
    background: rgba(255, 255, 255, 0.04);
}
.obase-filter-search {
    flex: 0 0 auto;
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid rgba(15, 23, 42, 0.08);
}
.obase-filter-search__icon {
    background: transparent;
    border-color: rgba(15, 23, 42, 0.15);
    color: rgba(15, 23, 42, 0.5);
}
[data-bs-theme="dark"] .obase-filter-search__icon {
    border-color: rgba(255, 255, 255, 0.15);
    color: rgba(255, 255, 255, 0.5);
}
/* NOTE: the selector is intentionally chained through .obase-menu.obase-filter-popup
   so the rule has specificity (0,3,0). Without that, the popup's dark-mode
   `color: #f1f5f9` rule (also (0,3,0)) wins by being later in source order via
   inheritance and the hint shows up white instead of blue. */
.obase-menu.obase-filter-popup .obase-filter-hint {
    flex: 0 0 auto;
    padding: 0.35rem 0.85rem;
    background: rgba(15, 23, 42, 0.03);
    color: var(--bs-primary);           /* accent blue on light theme */
    font-size: 0.65rem;
    font-style: normal;
    font-weight: 500;
    letter-spacing: 0.02em;
    border-bottom: 1px solid rgba(15, 23, 42, 0.08);
}
[data-bs-theme="dark"] .obase-menu.obase-filter-popup .obase-filter-hint {
    background: rgba(255, 255, 255, 0.03);
    color: #60a5fa;                     /* lighter blue — readable on the #1f2937 dark panel */
    border-bottom-color: rgba(255, 255, 255, 0.08);
}
.obase-filter-list {
    flex: 1 1 auto;
    min-height: 0;             /* required so the flex child can shrink + scroll */
    max-height: 280px;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 0.25rem 0;
}
/* Long value names should truncate with an ellipsis rather than overflow the
   popup width (the full text is exposed via the title= attribute on hover). */
.obase-filter-popup .obase-filter-item {
    align-items: center;
    width: 100%;
    min-width: 0;
    color: #0f172a;
    background: transparent;
}
.obase-filter-popup .obase-filter-item:hover { background: rgba(15, 23, 42, 0.04); }
[data-bs-theme="dark"] .obase-filter-popup .obase-filter-item { color: #f1f5f9; }
[data-bs-theme="dark"] .obase-filter-popup .obase-filter-item:hover { background: rgba(255, 255, 255, 0.06); }
/* "Select All" row sits at the top of the value list and is visually separated
   from the per-value rows below it. .is-partial mimics the native :indeterminate
   look (blue box + white dash) without needing a JS interop call. */
.obase-filter-popup .obase-filter-select-all {
    font-weight: 600;
    background: rgba(15, 23, 42, 0.025);
    border-bottom: 1px solid rgba(15, 23, 42, 0.08);
}
.obase-filter-popup .obase-filter-select-all:hover { background: rgba(15, 23, 42, 0.06); }
[data-bs-theme="dark"] .obase-filter-popup .obase-filter-select-all {
    background: rgba(255, 255, 255, 0.04);
    border-bottom-color: rgba(255, 255, 255, 0.08);
}
[data-bs-theme="dark"] .obase-filter-popup .obase-filter-select-all:hover { background: rgba(255, 255, 255, 0.08); }
.obase-filter-popup .obase-filter-select-all.is-partial .form-check-input {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
    /* white dash on blue — matches Bootstrap's native :indeterminate appearance */
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
.obase-filter-item__label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: inherit;             /* don't let a global `a {}` or theme rule recolor it */
}
.obase-filter-footer {
    flex: 0 0 auto;
    display: flex;
    justify-content: flex-end;
    padding: 0.55rem 0.6rem;
    border-top: 1px solid rgba(15, 23, 42, 0.08);
    background: inherit;            /* match the popup body — stays solid */
}
[data-bs-theme="dark"] .obase-filter-footer {
    border-top-color: rgba(255, 255, 255, 0.08);
}
.obase-filter-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.85rem;
    border-bottom: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
}
.obase-filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.5rem 0.2rem 0.6rem;
    background: var(--bs-card-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    font-size: 0.78rem;
    line-height: 1.2;
    color: var(--bs-body-color);
}
.obase-filter-chip__label { font-weight: 600; opacity: 0.75; }
.obase-filter-chip__values { color: var(--bs-primary); }
.obase-filter-chip__remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    margin-inline-start: 0.15rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: currentColor;
    opacity: 0.55;
    cursor: pointer;
    border-radius: 0.25rem;
    font-size: 0.95rem;
    line-height: 1;
    transition: opacity 100ms ease, background-color 100ms ease;
}
.obase-filter-chip__remove:hover { opacity: 1; background: var(--bs-tertiary-bg); }
.obase-filter-clear-all {
    margin-inline-start: 0.25rem;
    padding: 0.2rem 0.5rem;
    background: transparent;
    border: 0;
    color: var(--bs-danger);
    font-size: 0.78rem;
    cursor: pointer;
    border-radius: 0.25rem;
}
.obase-filter-clear-all:hover { background: var(--bs-tertiary-bg); }

/* ----- Search box ----- */
.obase-search { max-width: 340px; min-width: 220px; }
.obase-search .input-group-text {
    background: var(--bs-tertiary-bg);
    border-color: var(--bs-border-color);
    color: var(--bs-body-color);
    opacity: 0.7;
}
.obase-search .form-control {
    border-color: var(--bs-border-color);
    background: var(--bs-secondary-bg);
}
.obase-search .form-control:focus {
    box-shadow: 0 0 0 0.18rem rgba(var(--bs-primary-rgb), 0.18);
    border-color: var(--bs-primary);
}

/* =============================================================================
   Data table — modernized look
   ============================================================================= */
.obase-data-table {
    --bs-table-bg:            transparent;
    --bs-table-color:         var(--bs-body-color);
    --bs-table-border-color:  var(--bs-border-color);
    --bs-table-hover-bg:      rgba(var(--bs-primary-rgb), 0.06);
    margin-bottom: 0;
    font-size: 0.875rem;
    border-collapse: separate;
    border-spacing: 0;
    width: 100%;
}
.obase-data-table thead th {
    position: sticky;
    top: 0;
    background: var(--bs-tertiary-bg);
    color: var(--bs-body-color);
    font-weight: 600;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.7rem 1rem;
    border-bottom: 1px solid var(--bs-border-color);
    border-top: none;
    text-align: start;
    white-space: nowrap;
    opacity: 0.85;
    z-index: 1;
}
/* When a filter popup is rendered inside this <th>, drop the muted-opacity so
   the popup descendant isn't composited at 85% opacity (which made everything
   behind it bleed through, despite the popup's own computed opacity being 1).
   Also lift the <th>'s own stacking context above its sibling sticky <th>s —
   without this, sibling columns to the right (later DOM order, same z-index)
   paint on top of the popup's upper portion.
   :has() limits the override to only the column whose filter is currently open. */
.obase-data-table thead th:has(.obase-filter-popup) {
    opacity: 1;
    z-index: 1080;
}
/* Bootstrap's .table-responsive sets overflow-x: auto, which (per CSS spec)
   forces overflow-y to clip too — so an absolutely-positioned popup inside is
   cut off when it extends past the table's edges. While the popup is open,
   release the overflow so the popup can render at its full height. The :has()
   scopes the override to the specific card that contains the open popup, so
   horizontal scrolling on other tables on the page is unaffected. */
.obase-table:has(.obase-filter-popup) .table-responsive {
    overflow: visible;
}
/* Same precaution on the card-body wrapper — in case Bootstrap or theme.css
   ever adds overflow there. Cheap to keep, no visual change when not needed. */
.obase-table:has(.obase-filter-popup) .card-body {
    overflow: visible;
}
[data-bs-theme="dark"] .obase-data-table thead th {
    background: rgba(255, 255, 255, 0.02);
}
.obase-data-table tbody td {
    padding: 0.7rem 1rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--bs-border-color);
    height: var(--obase-row-h);
    background-color: var(--bs-card-bg);
    transition: background-color 0.12s ease;
}
.obase-data-table tbody tr:hover td          { background-color: var(--bs-table-hover-bg); }
.obase-data-table tbody tr:last-child td     { border-bottom: none; }

/* Single-line + ellipsis cells (Wrap="false", default).
   The component sets max-width via inline style; this class adds the truncation
   behavior + cursor hint. Browsers show the native title= tooltip on hover. */
.obase-data-table td.obase-nowrap {
    white-space:   nowrap;
    overflow:      hidden;
    text-overflow: ellipsis;
}
.obase-data-table td.obase-nowrap:hover { cursor: help; }

.obase-data-table th.obase-th-action {
    width: 1%;
    white-space: nowrap;
    padding-inline: 0.65rem;
}

/* Sortable header */
.obase-data-table th.obase-sortable { user-select: none; }
/* The actual click target — a real <button> (keyboard-operable, announces via
   aria-sort on the <th>) styled to read as plain header text. Filter funnel
   sits beside it as a sibling and doesn't share this hover/cursor. */
.obase-data-table th .obase-th-sort-trigger {
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    transition: color 0.15s;
    /* button reset — inherit the header's look */
    background: transparent;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    text-align: inherit;
}
.obase-data-table th .obase-th-sort-trigger:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
    border-radius: 2px;
}
.obase-data-table th .obase-th-sort-trigger:hover { color: var(--bs-primary); }
.obase-data-table th .obase-th-label { display: inline-block; vertical-align: middle; }
.obase-data-table th .obase-sort-indicator {
    display: inline-block;
    margin-inline-start: 0.4rem;
    opacity: 0.35;
    font-size: 0.7rem;
    transition: opacity 0.15s, color 0.15s;
    vertical-align: middle;
}
.obase-data-table th .obase-th-sort-trigger:hover .obase-sort-indicator { opacity: 0.8; }
.obase-data-table th.obase-sort-asc  .obase-sort-indicator,
.obase-data-table th.obase-sort-desc .obase-sort-indicator {
    opacity: 1;
    color: var(--bs-primary);
}

/* Clickable rows */
.obase-data-table tbody tr.obase-row-clickable { cursor: pointer; }

/* Sticky columns — see the consolidated rules near the bottom of this file
   (.obase-data-table th.obase-sticky-col / td.obase-sticky-col). The older
   .obase-sticky-first block lived here historically; it's now handled by the
   unified rules that work for both single- and multi-column freezing. */

/* Header select-all checkbox alignment */
.obase-select-all { margin: 0; vertical-align: middle; }

/* Image cells (byte[] → base64 thumbnail) get a soft frame */
.obase-data-table tbody td img {
    border-radius: 6px;
    border: 1px solid var(--bs-border-color);
    background: var(--bs-tertiary-bg);
    padding: 2px;
}

.table-responsive { border-radius: 0; }

/* Make the table card a well-behaved flex child: when the parent layout is
   flex (TestOBaseComponent + DCMS both have a flex page row with a sidebar),
   `min-width: 0` lets this card shrink to its parent's content width so the
   inner `.table-responsive` gets a real overflow-x scrollbar instead of
   pushing the entire page wider than the viewport. */
.obase-table {
    min-width: 0;
    max-width: 100%;
}
.obase-table .card-body {
    min-width: 0;
    /* card-body itself doesn't scroll — table-responsive inside it does. */
}
.obase-table .table-responsive {
    width: 100%;
    max-width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
}

/* ----- Empty state ----- */
.obase-empty {
    text-align: center;
    padding: 3.5rem 1rem 3rem;
    color: var(--bs-body-color);
    opacity: 0.55;
}
.obase-empty i { font-size: 3rem; margin-bottom: 0.65rem; display: block; opacity: 0.7; }
.obase-empty div { font-size: 0.95rem; }

/* ----- Row action button (neutral default) ----- */
.obase-row-action {
    border: 1px solid var(--bs-border-color);
    background: var(--bs-secondary-bg, var(--bs-body-bg));
    color: var(--bs-body-color);
    padding: 0.25rem 0.5rem;
    border-radius: var(--bs-border-radius-sm, 6px);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
    font-size: 0.875rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 28px;
}
.obase-row-action + .obase-row-action { margin-inline-start: 0.25rem; }
.obase-row-action:hover {
    background: var(--bs-tertiary-bg, #f1f5f9);
    border-color: var(--bs-primary);
    color: var(--bs-primary);
}
.obase-row-action.text-danger:hover { border-color: var(--bs-danger); color: var(--bs-danger); }
.obase-row-action:active { transform: scale(0.97); }
[data-bs-theme="dark"] .obase-row-action { background: var(--bs-tertiary-bg, #1f2937); }

/* ----- Skeleton shimmer ----- */
.obase-skel {
    display: inline-block;
    height: 12px;
    border-radius: 4px;
    background: linear-gradient(90deg,
                var(--bs-tertiary-bg, #f1f5f9) 25%,
                var(--bs-border-color, #e2e8f0) 50%,
                var(--bs-tertiary-bg, #f1f5f9) 75%);
    background-size: 200% 100%;
    animation: obase-shimmer 1.4s infinite;
    min-width: 60px;
}
@keyframes obase-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ----- Optional row-highlight classes ----- */
/* Generic flag (warning tint) — used for general "needs attention" rows */
.obase-row-flag td {
    background-color: rgba(245, 158, 11, 0.12) !important;
    font-weight: 500;
}
[data-bs-theme="dark"] .obase-row-flag td {
    background-color: rgba(245, 158, 11, 0.18) !important;
}

/* Diff highlight (red tint) — used by track-changes log to mark mutated rows */
.obase-row-diff td {
    background-color: rgba(220, 38, 38, 0.10) !important;
    color: #b91c1c !important;
    font-weight: 600;
}
.obase-row-diff td strong,
.obase-row-diff td b { color: #7f1d1d !important; }
[data-bs-theme="dark"] .obase-row-diff td {
    background-color: rgba(248, 113, 113, 0.14) !important;
    color: #fca5a5 !important;
}
[data-bs-theme="dark"] .obase-row-diff td strong,
[data-bs-theme="dark"] .obase-row-diff td b { color: #fecaca !important; }

/* =============================================================================
   Pager — refined typography
   ============================================================================= */
.obase-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    padding: 0.85rem 1.1rem;
    background: var(--bs-tertiary-bg);
    border-top: 1px solid var(--bs-border-color);
    font-family: var(--bs-body-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
    font-size: 0.8125rem;
    font-feature-settings: "tnum" 1, "lnum" 1;   /* tabular lining numerals — counters don't shimmy */
    color: var(--bs-body-color);
}
[data-bs-theme="dark"] .obase-pager {
    background: rgba(255, 255, 255, 0.025);
}
.obase-pager-info {
    opacity: 0.75;
    letter-spacing: 0.005em;
}
.obase-pager-info strong {
    color: var(--bs-emphasis-color);
    font-weight: 600;
    opacity: 1;
    font-variant-numeric: tabular-nums;
}

/* The current-page indicator — the visual anchor of the pager */
.obase-pager-indicator {
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    padding: 0.32rem 0.85rem;
    border-radius: 999px;
    background: var(--bs-secondary-bg);
    border: 1px solid var(--bs-border-color);
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--bs-body-color);
    min-width: 64px;
    justify-content: center;
    font-variant-numeric: tabular-nums;
}
.obase-pager-indicator strong {
    font-weight: 700;
    color: var(--bs-primary);
    font-size: 0.9375rem;
    line-height: 1;
}
.obase-pager-divider {
    color: var(--bs-body-color);
    opacity: 0.5;
    font-size: 0.75rem;
    font-weight: 400;
}

/* Page-size selector beside the pager */
.obase-pager-size {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--bs-body-color);
    opacity: 0.85;
}
.obase-pager-size-label { margin: 0; opacity: 0.7; }
.obase-pager-size .form-select-sm {
    width: auto;
    min-width: 70px;
    padding-right: 1.75rem;
    font-variant-numeric: tabular-nums;
}

/* Pager nav buttons */
.obase-pager-buttons {
    display: flex;
    gap: 0.25rem;
    align-items: center;
}
.obase-pager-btn {
    border: 1px solid var(--bs-border-color);
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    width: 34px;
    height: 34px;
    border-radius: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
    font-size: 0.95rem;
    padding: 0;
}
.obase-pager-btn:hover:not(:disabled) {
    background: var(--bs-tertiary-bg);
    border-color: var(--bs-primary);
    color: var(--bs-primary);
}
.obase-pager-btn:active:not(:disabled) { transform: scale(0.94); }
.obase-pager-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* =========================================================================
   OBaseModal
   ========================================================================= */
.obase-modal-host { background: transparent; }

/* =========================================================================
   OBaseMultiSelect
   ========================================================================= */
.obase-multi-select { position: relative; display: inline-block; min-width: 200px; }

/* Double-frame fix.
   The inner <button class="form-control obase-ms-display"> already carries
   form-control and paints the visible input border. When the wrapper ALSO
   receives form-control as a CssClass (the form host does this — it forwards
   DefaultFieldClass="form-control" to OBaseDropDown), Bootstrap paints a
   SECOND border on the wrapper and the field renders with two stacked frames
   (visible on the SearchAsset / AppData pages in DCMS + AdminCP).

   We can't strip the class at the form-host or component level without also
   killing the layout properties form-control provides (display:block on
   wrapper-less callers, width:100% so the dropdown fills its column).

   So: neutralise ONLY the visual properties on the wrapper when both classes
   coexist. Layout (width:100%) survives. Inner button is the only visible
   frame. Sibling tokens like form-control-sm are not affected — they don't
   match this selector. */
.obase-multi-select.form-control {
    border:        0;
    background:    transparent;
    padding:       0;
    box-shadow:    none;
    height:        auto;
    min-height:    0;
}

.obase-ms-display {
    cursor: pointer;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px;
    min-height: 38px;
    padding: 4px 8px;
    width: 100%;
    background-color: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
}
.obase-ms-display:focus { outline: none; border-color: var(--bs-primary); box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); }
.obase-ms-display.obase-ms-open { border-color: var(--bs-primary); }
.obase-ms-caret { margin-inline-start: auto; opacity: 0.6; }
.obase-ms-chip { font-weight: 500; display: inline-flex; align-items: center; }
.obase-ms-chip-close { cursor: pointer; opacity: 0.85; font-weight: 700; line-height: 1; padding: 0 .25rem; }
.obase-ms-chip-close:hover { opacity: 1; }
.obase-ms-popover {
    position: absolute;
    top: calc(100% + 4px);
    inset-inline-start: 0;
    z-index: 1080;
    min-width: 240px;
    max-width: 360px;
}
.obase-ms-list { max-height: 240px; overflow-y: auto; }

/* =========================================================================
   OBaseDropDown — popover row states
   - keyboard-navigated highlight (clearly visible primary tint)
   - hover cue
   - "this multi row is selected" treatment so the user actually sees their picks
   ========================================================================= */

/* Selected (multi) row — tinted background + bold primary label. */
.obase-ms-list .obase-dd-row-selected {
    background-color: rgba(13, 110, 253, 0.08);
    border-radius: 4px;
}
.obase-ms-list .obase-dd-row-selected .form-check-label {
    font-weight: 600;
    color: var(--bs-primary);
}

/* Hover — subtle grey on both multi (form-check) and single (dropdown-item) rows. */
.obase-ms-list .form-check:hover,
.obase-ms-list .dropdown-item:hover {
    background-color: rgba(0, 0, 0, 0.04);
    border-radius: 4px;
}
/* Hovering a selected multi row stays in the primary palette. */
.obase-ms-list .obase-dd-row-selected:hover {
    background-color: rgba(13, 110, 253, 0.12);
}

/* Keyboard-navigation highlight — distinct, never invisible.
   Multi rows: tint the whole form-check.
   Single rows: skip .active items so Bootstrap's bold blue still wins there. */
.obase-ms-list .obase-dd-hl,
.obase-ms-list .dropdown-item.obase-dd-hl:not(.active) {
    background-color: rgba(13, 110, 253, 0.18);
    border-radius: 4px;
}
/* Highlight + selected (multi) — slightly stronger so both states read together. */
.obase-ms-list .obase-dd-row-selected.obase-dd-hl {
    background-color: rgba(13, 110, 253, 0.22);
}

/* =========================================================================
   OBaseDateRangePicker
   ========================================================================= */
.obase-date-range-picker { position: relative; display: inline-block; }
.obase-date-range-popover {
    position: absolute;
    top: calc(100% + 4px);
    inset-inline-start: 0;
    z-index: 1080;
    min-width: 360px;
}
.obase-date-range-presets { min-width: 130px; }
.obase-date-range-custom  { min-width: 200px; }

/* =========================================================================
   OBaseTableComponent — sticky multi-column (Phase 2 #1).

   Sticky cells MUST have an opaque background or scrolled content shows
   through. Two things conspire to make this hard:
     1. Bootstrap's `.table` paints cell color via `--bs-table-bg-type` /
        `--bs-table-bg-state` CSS variables and an inset box-shadow, not via
        the plain `background-color`. Setting only background-color leaves
        the inset shadow leaking the scrolled content through.
     2. `.table-striped` toggles those variables on odd rows.

   Fix: on every sticky cell variant (header / data / hover, light / dark)
   set background-color AND both Bootstrap table-bg variables to the same
   opaque value. The sticky column intentionally renders as a solid card-bg
   strip even when the rest of the row is Bootstrap-striped — that visual
   "different colour" reinforces that this column is frozen. Inline
   style="left:Npx" on each cell provides the per-column offset for
   multi-column freezing.
   ========================================================================= */
/* Data sticky cells — always paint a solid card-bg. We use !important
   to defeat Bootstrap's `.table-hover` / `.table-striped` rules that set
   --bs-table-bg-state / --bs-table-bg-type with higher specificity than
   ours. Without !important, hovering a row makes Bootstrap repaint the
   cell via its inset box-shadow using rgba(...,0.06) — only 6% opaque —
   and scrolled-under content shows through. */
/* The fill + divider are driven by two overridable hooks, each defaulting to a
   theme-aware Bootstrap token so light/dark stay correct out of the box:
       --obase-sticky-bg      → frozen-column fill   (default: light-gray strip)
       --obase-sticky-shadow  → frozen-cell divider  (default: 1px hairline)
   The component sets these via the StickyBackground / StickyElevation presets;
   consumers can also override them (theme-scoped) in their own stylesheet. */
.obase-data-table td.obase-sticky-col {
    position: sticky;
    z-index: 2;
    background-color: var(--obase-sticky-bg, var(--bs-secondary-bg, #eceef1)) !important;
    box-shadow: var(--obase-sticky-shadow, 1px 0 0 var(--bs-border-color)) !important;
}
/* Hover row: solid tint so the user gets a visual hover cue (opaque, no leak). */
.obase-data-table tbody tr:hover td.obase-sticky-col {
    background-color: var(--obase-sticky-hover-bg, var(--bs-tertiary-bg, #eef2f7)) !important;
}
/* Header sticky cells: same fill hook (falls back to tertiary so the header reads
   distinct from the body in Auto mode), higher z-index so they layer above data
   sticky cells when the table is vertically scrolled. */
.obase-data-table thead th.obase-sticky-col {
    position: sticky;
    z-index: 3;
    background-color: var(--obase-sticky-bg, var(--bs-tertiary-bg, #eef2f7)) !important;
    box-shadow: var(--obase-sticky-shadow, 1px 0 0 var(--bs-border-color)) !important;
}
/* Elevated preset — soft drop-shadow on the frozen/scroll boundary only (the last
   sticky column), so scrolled-under content reads as layered beneath the freeze.
   Higher specificity than the base hairline rule above, so it wins on that cell. */
.obase-data-table.obase-sticky-elevated td.obase-sticky-last,
.obase-data-table.obase-sticky-elevated th.obase-sticky-last {
    box-shadow: 6px 0 6px -4px rgba(0, 0, 0, .18) !important;
}
[data-bs-theme="dark"] .obase-data-table.obase-sticky-elevated td.obase-sticky-last,
[data-bs-theme="dark"] .obase-data-table.obase-sticky-elevated th.obase-sticky-last {
    box-shadow: 8px 0 10px -4px rgba(0, 0, 0, .55) !important;
}
/* RTL: shadows have no logical-property form — flip the X offsets so the
   hairline/elevation sits at the frozen/scroll boundary (now on the LEFT). */
[dir="rtl"] .obase-data-table td.obase-sticky-col,
[dir="rtl"] .obase-data-table thead th.obase-sticky-col {
    box-shadow: var(--obase-sticky-shadow-rtl, -1px 0 0 var(--bs-border-color)) !important;
}
[dir="rtl"] .obase-data-table.obase-sticky-elevated td.obase-sticky-last,
[dir="rtl"] .obase-data-table.obase-sticky-elevated th.obase-sticky-last {
    box-shadow: -6px 0 6px -4px rgba(0, 0, 0, .18) !important;
}
[dir="rtl"][data-bs-theme="dark"] .obase-data-table.obase-sticky-elevated td.obase-sticky-last,
[dir="rtl"][data-bs-theme="dark"] .obase-data-table.obase-sticky-elevated th.obase-sticky-last {
    box-shadow: -8px 0 10px -4px rgba(0, 0, 0, .55) !important;
}

/* RTL: mirror directional chevrons (pager first/prev/next/last, row-open). */
[dir="rtl"] .obase-pager-btn .obase-icon,
[dir="rtl"] .obase-row-action .obase-icon {
    transform: scaleX(-1);
}

/* =========================================================================
   Column resize (EnableColumnResize) — drag handle + fixed-layout mode
   ========================================================================= */
/* Handle straddles the header's trailing edge (logical end ⇒ correct side in
   RTL). touch-action:none lets pointer events drive touch drags. */
.obase-data-table th { position: relative; }
.obase-col-resizer {
    position: absolute;
    top: 0;
    bottom: 0;
    inset-inline-end: -4px;
    width: 9px;
    cursor: col-resize;
    touch-action: none;
    user-select: none;
    z-index: 4;
}
.obase-col-resizer::after {
    content: '';
    position: absolute;
    top: 20%;
    bottom: 20%;
    inset-inline-end: 4px;
    width: 2px;
    border-radius: 1px;
    background: transparent;
    transition: background 0.12s;
}
.obase-col-resizer:hover::after,
.obase-col-resizer.is-active::after { background: var(--bs-primary); }

/* Active after the first drag (or restored widths): col widths become exact. */
.obase-data-table.obase-colw-fixed { table-layout: fixed; }
.obase-data-table.obase-colw-fixed thead th { overflow: hidden; text-overflow: ellipsis; }
/* Wins over .obase-virtual-scroll table { table-layout: auto } below. */
.obase-virtual-scroll table.obase-colw-fixed { table-layout: fixed; }

body.obase-col-dragging { cursor: col-resize !important; user-select: none !important; }
body.obase-col-dragging .obase-th-sort-trigger { pointer-events: none; }

/* Reset-widths entry in the columns menu reads as an action, not a checkbox row. */
.obase-menu-reset {
    width: 100%;
    border: 0;
    background: transparent;
    color: var(--bs-primary);
    border-top: 1px solid var(--bs-border-color);
    margin-top: 0.25rem;
    padding-top: 0.45rem;
}

/* =========================================================================
   Virtualized table — keep <tbody> scrollable inside .table-responsive
   ========================================================================= */
.obase-virtual-scroll { position: relative; }
.obase-virtual-scroll table { table-layout: auto; }

/* =========================================================================
   OBase Tabs — thin Bootstrap-5 nav-tabs / nav-pills polish.
   ========================================================================= */
.obase-tabs { margin-bottom: 1rem; }
.obase-tabs > .tab-content { padding-top: 1rem; }
.obase-tabs .nav-link { cursor: pointer; }
.obase-tabs .nav-link.disabled { cursor: not-allowed; opacity: 0.55; }
.obase-tabs .nav-link i { vertical-align: -1px; }

