/* ──────────────────────────────────────────────────────────────────────────
   OQLIS modern component styles
   Shared by home.php and spaces/index.php. Token-driven: works in light and
   dark themes and with whitelabel --main_color accents.
   ────────────────────────────────────────────────────────────────────────── */

/* Softer, lower-contrast border for cards/panels: the theme border blended
   toward the surface so edges read as a quiet hairline, not a hard line.
   Accent/hover borders keep the full --oqlis-border for definition. */
body {
    --oqlis-border-soft: color-mix(in srgb, var(--oqlis-border) 45%, var(--oqlis-surface));
    /* Single horizontal page gutter — the distance page content (headers, grids,
       sections) is inset from the content-column edge. Reference this token
       instead of hardcoding 15px so aligned edges can't drift apart. */
    --oqlis-page-gutter: 10px;
}
/* Drive every content column's horizontal inset from the gutter token so all
   pages share one value. Higher specificity than the theme's `.main-content`
   padding (loaded later) so it wins at every breakpoint; vertical padding stays
   with the theme, and `.am-no-padding .main-content` (2 classes) still wins. */
body .main-content {
    padding-left: var(--oqlis-page-gutter);
    padding-right: var(--oqlis-page-gutter);
}

/* ── Motion ───────────────────────────────────────────────────────────── */
@keyframes home-rise {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}

/* Cards and chips stagger in as each grid arrives */
.home-card-grid .home-card,
.home-carousel > .home-card,
.home-space-row .home-chip {
    animation: home-rise .45s cubic-bezier(.22,.9,.3,1) both;
}
.home-card:nth-child(2), .home-chip:nth-child(2) { animation-delay: .05s; }
.home-card:nth-child(3), .home-chip:nth-child(3) { animation-delay: .10s; }
.home-card:nth-child(4), .home-chip:nth-child(4) { animation-delay: .15s; }
.home-card:nth-child(5), .home-chip:nth-child(5) { animation-delay: .20s; }
.home-card:nth-child(6), .home-chip:nth-child(6) { animation-delay: .25s; }
.home-card:nth-child(7), .home-chip:nth-child(7) { animation-delay: .30s; }
.home-card:nth-child(n+8), .home-chip:nth-child(n+8) { animation-delay: .35s; }

/* Accessibility: honour reduced-motion preference */
@media (prefers-reduced-motion: reduce) {
    .home-page, .home-page *,
    .home-page *::before, .home-page *::after {
        animation: none !important;
        transition: none !important;
    }
}

/* Accessibility: visible keyboard focus everywhere */
.home-page a:focus-visible,
.home-page [role="button"]:focus-visible,
.home-page button:focus-visible,
.home-accordion-head:focus-visible {
    outline: 2px solid var(--main_color);
    outline-offset: 2px;
    border-radius: 6px;
}
.home-card:has(.home-card-cover:focus-visible) {
    border-color: color-mix(in srgb, var(--main_color) 55%, var(--oqlis-border));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--main_color) 30%, transparent);
}
.home-card .home-card-cover:focus-visible { outline: none; }

/* ── Skeletons ────────────────────────────────────────────────────────── */
@keyframes skeleton-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}
.home-skel {
    display: inline-block;
    border-radius: 6px;
    background: linear-gradient(90deg,
        var(--oqlis-surface-muted) 25%,
        var(--oqlis-input-disabled) 50%,
        var(--oqlis-surface-muted) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.5s ease-in-out infinite;
}
img[data-dashboard-id],
img[data-explore-id] {
    display: block;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg,
        var(--oqlis-surface-muted) 25%,
        var(--oqlis-input-disabled) 50%,
        var(--oqlis-surface-muted) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.5s ease-in-out infinite;
}
[data-dashboard-bg-id],
[data-explore-bg-id] {
    background: linear-gradient(90deg,
        var(--oqlis-surface-muted) 25%,
        var(--oqlis-input-disabled) 50%,
        var(--oqlis-surface-muted) 75%) !important;
    background-size: 200% 100% !important;
    animation: skeleton-shimmer 1.5s ease-in-out infinite;
    /* .grow carries the invert filter for the loaded screenshot; suppress it
       on the placeholder so the dark skeleton isn't flipped to light grey */
    filter: none !important;
}
.home-card-thumbskel {
    width: 100%;
    aspect-ratio: 16 / 10;
    border-radius: 0;
}
/* Lazy images fade in after off-DOM decode so the dark-mode invert filter
   never misses a frame (prevents a white flash per thumbnail) */
@keyframes home-img-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}
img.home-img-in { animation: home-img-fade .3s ease both; }

/* ── Layout ───────────────────────────────────────────────────────────── */
/* Start content inline with the top of the first sidebar menu item (the
   theme default padding pushes it ~15px below the header bar) */
.main-content.home-page { padding-top: 0; }
/* The theme reserves a full 45px band to clear the fixed top bar, but that bar
   is now transparent (only the avatar + Cmd-K sit in its top-right), so on the
   redesigned pages the breadcrumb/title can rise into it. Shift the content up
   ~16px for a tighter top; the right-aligned action chips still clear the
   avatar. Uses position/top rather than a negative margin — a negative margin
   here collapses up through .am-content and drags the fixed header/logo up.
   Desktop only (mobile has its own top spacing); scoped to .home-page so pages
   still using the legacy fixed header keep their needed clearance. */
@media (min-width: 767px) {
    .main-content.home-page { position: relative; top: -16px; }
}

/* ── Sections ─────────────────────────────────────────────────────────── */
.home-section { margin-bottom: 30px; }
.home-section-title {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 16px;
    font-size: 12.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .11em;
    color: var(--oqlis-text-muted);
}
.home-section-title::after {
    content: "";
    flex: 1;
    height: 1px;
    /* Full --oqlis-border (not border-soft): the divider sits on the tinted
       shell, and border-soft mixed 45% with white was invisible in light mode. */
    background: var(--oqlis-border);
}

/* ── Chips ────────────────────────────────────────────────────────────── */
.home-space-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.home-chip {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 9px 18px;
    border: 1px solid var(--oqlis-border);
    border-radius: 999px;
    background: var(--oqlis-surface);
    color: var(--oqlis-text);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.home-chip .icon,
.home-chip .fa {
    font-size: 15px;
    color: color-mix(in srgb, var(--main_color) 75%, var(--oqlis-text));
}
.home-chip:hover,
.home-chip:focus {
    color: var(--oqlis-text);
    text-decoration: none;
    border-color: color-mix(in srgb, var(--main_color) 70%, var(--oqlis-border));
    box-shadow: 0 3px 12px color-mix(in srgb, var(--main_color) 22%, transparent);
    transform: translateY(-1px);
}
.home-chip:active { transform: translateY(0) scale(.97); }
/* Space navigation chips: transparent main-color tint */
#spaceList .home-chip:not(.home-chip-add),
#spaceholder .home-chip {
    background: color-mix(in srgb, var(--main_color) 12%, transparent);
    border-color: color-mix(in srgb, var(--main_color) 35%, var(--oqlis-border));
}
#spaceList .home-chip:not(.home-chip-add):hover,
#spaceholder .home-chip:hover {
    background: color-mix(in srgb, var(--main_color) 22%, transparent);
    border-color: color-mix(in srgb, var(--main_color) 70%, var(--oqlis-border));
}
.home-chip-add {
    border-style: dashed;
    /* White fill + an accent-tinted dashed border so the "add" chips read on the
       tinted page background instead of vanishing (transparent dashed on grey). */
    border-color: color-mix(in srgb, var(--main_color) 45%, var(--oqlis-border));
    background: var(--oqlis-surface);
    /* Full-strength label (not muted) — it's a create action, so it should read
       as a confident button; the dashed border + accent icon carry the
       lightweight "add" character. */
    color: var(--oqlis-text);
}
.home-chip-add:hover,
.home-chip-add:focus {
    border-color: color-mix(in srgb, var(--main_color) 70%, var(--oqlis-border));
    color: var(--oqlis-text);
}
/* Prominent primary chip for frequently-used actions like the explore Run
   button — uses the existing btn-info colour. */
.home-chip-primary {
    background: var(--oqlis-btn-info-hover);
    border-color: var(--oqlis-btn-info-border);
    color: var(--oqlis-text);
}
.home-chip-primary .icon,
.home-chip-primary .fa { color: var(--oqlis-text); }
.home-chip-primary:hover,
.home-chip-primary:focus {
    color: var(--oqlis-text);
    background: var(--oqlis-btn-info-hover);
    border-color: var(--oqlis-btn-info-border);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--oqlis-btn-info) 45%, transparent);
    transform: translateY(-1px);
}
/* Green "go / execute" chip for Run-style actions — uses the existing
   btn-success colour. */
.home-chip-success {
    background: var(--oqlis-btn-success-hover);
    border-color: var(--oqlis-btn-success-border);
    color: var(--oqlis-text);
}
.home-chip-success .icon,
.home-chip-success .fa { color: var(--oqlis-text); }
.home-chip-success:hover,
.home-chip-success:focus {
    color: var(--oqlis-text);
    background: var(--oqlis-btn-success-hover);
    border-color: var(--oqlis-btn-success-border);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--oqlis-btn-success) 45%, transparent);
    transform: translateY(-1px);
}
/* Modern sliding toggle switch (e.g. the unify Monitor control). Track turns
   green (success) when on, and the knob slides across. */
.oqlis-switch {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    user-select: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--oqlis-text);
    margin: 0;
}
.oqlis-switch input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}
.oqlis-switch .oqlis-switch-track {
    position: relative;
    flex: 0 0 auto;
    width: 42px;
    height: 22px;
    border-radius: 999px;
    background: var(--oqlis-surface-alt);
    border: 1px solid var(--oqlis-border);
    transition: background .2s ease, border-color .2s ease;
}
.oqlis-switch .oqlis-switch-track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
    transition: transform .2s ease;
}
.oqlis-switch input:checked + .oqlis-switch-track {
    background: var(--oqlis-btn-success-hover);
    border-color: var(--oqlis-btn-success-border);
}
.oqlis-switch input:checked + .oqlis-switch-track::after { transform: translateX(20px); }
.oqlis-switch input:disabled + .oqlis-switch-track { opacity: .5; cursor: not-allowed; }
.oqlis-switch input:focus-visible + .oqlis-switch-track {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--oqlis-btn-success) 40%, transparent);
}
/* Icon-only Lumin chip (shows the Lumin mark instead of a text label). Uses the
   dark logo + the theme filter so it inverts to white in dark mode. */
.home-chip-lumin { padding: 8px 11px; }
.home-chip-lumin img {
    width: 18px;
    height: 18px;
    display: block;
    filter: var(--oqlis-filter);
}

/* ── Cards ────────────────────────────────────────────────────────────── */
.home-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(205px, 1fr));
    gap: 16px;
}
.home-card {
    position: relative;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 14px;
    background: var(--oqlis-surface);
    overflow: hidden;
    transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
    /* Accent glow matching .home-chip:hover, scaled for the larger surface;
       the tinted glow reads in both light and dark themes */
    --home-card-shadow-hover:
        0 2px 6px color-mix(in srgb, var(--main_color) 14%, transparent),
        0 12px 28px -8px color-mix(in srgb, var(--main_color) 30%, transparent);
}
.home-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--main_color) 55%, var(--oqlis-border));
    box-shadow: var(--home-card-shadow-hover);
}
/* Resting elevation (light mode): lift the white cards / hero / stat panel off
   the tinted page background so they read as raised surfaces rather than flat
   grey panels — the main thing that made light mode look "dull". Dark mode
   keeps its flat, border-defined surfaces (shadow cleared below). */
.home-card,
.home-hero,
.home-stats,
.home-table-wrap {
    /* Apple-style light surfaces: borderless white cards defined by a soft
       layered shadow against the tinted shell (not a 1px box). */
    border-color: transparent;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 8px 24px -10px rgba(16, 24, 40, .13);
}
[data-theme="dark"] .home-card,
[data-theme="dark"] .home-hero,
[data-theme="dark"] .home-stats,
[data-theme="dark"] .home-table-wrap {
    /* Dark keeps flat, border-defined surfaces (no light shadow). */
    border-color: var(--oqlis-border-soft);
    box-shadow: none;
}
.home-card-cover { position: absolute; inset: 0; z-index: 1; }
.home-card-thumb {
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-bottom: 1px solid var(--oqlis-border-soft);
    background: var(--oqlis-surface-alt);
}
.home-card-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    display: block;
    /* Dark mode inverts light dashboard screenshots via the theme token */
    filter: var(--oqlis-filter);
    transition: transform .25s ease;
}
/* While still a placeholder the img shows the (already dark) skeleton gradient;
   don't invert it or dark-mode's skeleton flips to light grey. */
.home-card-thumb img[data-dashboard-id],
.home-card-thumb img[data-explore-id] {
    filter: none;
}
.home-card:hover .home-card-thumb img { transform: scale(1.03); }

/* Open affordance: corner chip revealed on hover/focus */
.home-card::after {
    content: "\2197";
    position: absolute;
    top: 10px;
    right: 10px;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    border: 1px solid var(--oqlis-border-soft);
    background: color-mix(in srgb, var(--oqlis-surface) 82%, transparent);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    color: var(--oqlis-text);
    font-size: 14px;
    line-height: 1;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity .18s ease, transform .18s ease;
    pointer-events: none;
    z-index: 2;
}
.home-card:hover::after,
.home-card:has(.home-card-cover:focus-visible)::after {
    opacity: 1;
    transform: none;
}
.home-card-skel::after { content: none; }
.home-card-meta {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 14px 13px;
}
.home-card-title {
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.35;
    color: var(--oqlis-text);
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.home-card-space {
    position: relative;
    z-index: 2;
    align-self: flex-start;
    padding: 3px 11px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--main_color) 16%, transparent);
    color: color-mix(in srgb, var(--main_color) 40%, var(--oqlis-text));
    font-size: 11px;
    font-weight: 600;
    text-decoration: none;
    transition: background .15s ease;
}
.home-card-space:hover {
    background: color-mix(in srgb, var(--main_color) 32%, transparent);
    color: color-mix(in srgb, var(--main_color) 30%, var(--oqlis-text));
    text-decoration: none;
}
/* Footer row: space badge on the left, last-accessed time on the right —
   shows why the card sits where it does in the recency order */
.home-card-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.home-card-time {
    position: relative;
    z-index: 2;
    margin-left: auto;
    flex: 0 0 auto;
    font-size: 10.5px;
    color: var(--oqlis-text-muted);
    white-space: nowrap;
}
.home-card-skel { pointer-events: none; }

/* ── Empty states ─────────────────────────────────────────────────────── */
.home-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 44px 20px;
    border: 1px dashed var(--oqlis-border-soft);
    border-radius: 14px;
    color: var(--oqlis-text-muted);
    text-align: center;
}
.home-empty .icon { font-size: 36px; }
.home-empty p { margin: 0; font-size: 14px; }
.home-empty-action {
    font-weight: 600;
    color: color-mix(in srgb, var(--main_color) 40%, var(--oqlis-text));
}

/* ── Accordion ────────────────────────────────────────────────────────── */
.home-accordion-head { display: block; text-decoration: none; cursor: pointer; }
.home-accordion-head:hover, .home-accordion-head:focus { text-decoration: none; }
.home-accordion-head:hover .home-section-title { color: var(--oqlis-text); }
.home-accordion-icon {
    display: inline-block;
    font-size: 15px;
    font-weight: 800;
    transition: transform .25s cubic-bezier(.22,.9,.3,1);
}
.home-accordion-head.active .home-accordion-icon { transform: rotate(90deg); }
.home-accordion-body { padding-top: 2px; }

/* ── Page header strips (all non-home pages) ──────────────────────────── */
/* Page controls are absolutely positioned against the strip's right edge,
   so inset the content box clear of the avatar + Cmd+K trigger zone and
   paint the gap with a pseudo-element so the background runs to the edge. */
body .page-head,
body .page-head-breadcrumb {
    right: 180px;
}
body .page-head::after,
body .page-head-breadcrumb::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 100%;
    width: 180px;
    background: inherit;
}
/* Lift the top nav above the strips so the avatar stays clickable; the nav
   is transparent, so let clicks pass through everywhere except its controls */
body .am-top-header {
    z-index: 1031;
    pointer-events: none;
}

/* ── Dashboard viewer header (modern big title + toolbar) ─────────────────
   The dashboard viewer adopts the modern look: a prominent title on the left
   and its control toolbar on the right (no breadcrumb — dashboards don't have
   a simple one). The header is transparent and NON-fixed (in-flow), so it reads
   like the other pages. It stays a sibling of .main-content (the fullscreen
   target), so fullscreen still excludes it. Shift this page's .am-content up to
   top:29 (scoped via :has) so the header starts at the SAME height as every
   other page — the rest of the app nets 29 via .am-content top:45 + .home-page
   top:-16; the dashboard isn't a .home-page, so we apply the 29 directly. */
@media (min-width: 767px) {
    .am-content:has(.page-head.oqlis-dash-head) { top: 29px !important; }
}
/* Mobile: the desktop 29px am-content offset above doesn't apply and the header
   only pads its sides (padding: 0 gutter), so the breadcrumb/title sit flush
   against the top edge. Give the header a top inset equal to the side gutter so
   it's evenly framed. (This header is dashboard-only.) */
@media (max-width: 766px) {
    /* !important beats the base rule's `padding: 0 gutter` shorthand below,
       which otherwise resets padding-top to 0 (later in source, same specificity). */
    body .page-head.oqlis-dash-head { padding-top: var(--oqlis-page-gutter) !important; }
}
body .page-head.oqlis-dash-head {
    position: relative !important;
    left: auto !important;
    right: auto !important;
    top: auto !important;
    height: auto;
    min-height: 54px;
    background: transparent;
    box-shadow: none !important;
    border: 0 !important;
    /* Above the filter pane (z-index 21) so the settings ⋮ dropdown, which lives
       in this header, renders over the pane instead of behind it. */
    z-index: 30;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 0 var(--oqlis-page-gutter);
}
body .page-head.oqlis-dash-head::after { display: none; }
body .page-head.oqlis-dash-head .dash-head-info {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
}
/* Drop the toolbar down so it lines up with the dashboard title (rather than
   the full header height) — this also gives it breathing room clear of the
   fixed top-header/avatar. */
body .page-head.oqlis-dash-head .dash-head-actions { align-self: flex-start; margin-top: 16px; }
body .page-head.oqlis-dash-head .spaces-breadcrumb {
    margin: 0;
    padding: 0;
    background: transparent;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .08em;
    line-height: 1.2;
}
body .page-head.oqlis-dash-head .spaces-breadcrumb > li > a { color: var(--oqlis-text-muted); }
body .page-head.oqlis-dash-head .spaces-breadcrumb > li > a:hover { color: var(--main_color); text-decoration: none; }
body .page-head.oqlis-dash-head .spaces-breadcrumb > li.active { color: var(--oqlis-text-muted); }
body .page-head.oqlis-dash-head .spaces-breadcrumb > li + li:before { color: var(--oqlis-text-muted); }
body .page-head.oqlis-dash-head .dashboard-title {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    margin: 0;
    line-height: 1.15;
    overflow: hidden;
    /* Lock the title row height so appending a filter pill can never change it
       (otherwise the centred title jumps down when a click-filter is added). */
    height: 24px;
}
body .page-head.oqlis-dash-head #updateTitle {
    flex: 0 0 auto;
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--oqlis-text);
    white-space: nowrap;
}
body .page-head.oqlis-dash-head .dash-title-badges {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
}
/* Filter/click-filter/parameter pills inherit a tall line-height (36px) from a
   theme .label rule, which inflated the title row and made the title jump down
   when a click-filter was appended. Normalise it so a pill sits within the
   title's own line height and appending one doesn't change the row height. */
body .page-head.oqlis-dash-head .dash-title-badges .label {
    line-height: 1.4;
    display: inline-flex;
    align-items: center;
}
body .page-head.oqlis-dash-head .dash-head-actions {
    flex: 0 0 auto;
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
}
/* Toolbar icon buttons — match the chip visual language (rounded, bordered,
   surface bg) but icon-only and compact. Covers the plain s7 icons, the Lumin
   background-image span, and the settings dropdown toggle. */
body .page-head.oqlis-dash-head .dash-head-actions > .icon,
body .page-head.oqlis-dash-head .dash-head-actions > .textarea-icon,
body .page-head.oqlis-dash-head .dash-head-actions > .dropdown > .dropdown-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    box-sizing: border-box;
    border-radius: 9px;
    border: 1px solid var(--oqlis-border);
    background-color: var(--oqlis-surface);
    color: var(--oqlis-text-muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
    transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}
body .page-head.oqlis-dash-head .dash-head-actions > .icon:hover,
body .page-head.oqlis-dash-head .dash-head-actions > .textarea-icon:hover,
body .page-head.oqlis-dash-head .dash-head-actions > .dropdown > .dropdown-toggle:hover,
body .page-head.oqlis-dash-head .dash-head-actions > .dropdown.open > .dropdown-toggle {
    background-color: var(--oqlis-surface-alt);
    border-color: color-mix(in srgb, var(--main_color) 45%, var(--oqlis-border));
    color: var(--main_color);
}
/* Lumin insight icon keeps its background-image mark; centre it in the button */
body .page-head.oqlis-dash-head .dash-head-actions > .textarea-icon {
    background-repeat: no-repeat;
    background-position: center;
    background-size: 17px auto;
}
/* more-options glyph inherits the button's colour and sits at the button size */
body .page-head.oqlis-dash-head .dash-head-actions > .dropdown > .dropdown-toggle > .icon,
body .page-head.oqlis-dash-head .dash-head-actions > .dropdown > .dropdown-toggle > .fa {
    color: inherit;
    font-size: 16px;
}

/* Embedded / API dashboards and the standalone app renderer keep their own
   (non-oqlis-dash-head) header markup, but their action icons — the filter
   toggle and the settings ⋯ — should use the same modern bordered icon-button
   style as dashboard/index. Scoped to the embed + app body classes. */
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    /* Sit above chart content (chart hover tools carry z-index:1000) so the
       settings ⋯ dropdown isn't overlapped by KPI values / chart icons; stays
       below modals (1040+). */
    z-index: 1030;
}
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions .dropdown-menu { z-index: 1031; }
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .icon,
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .dropdown > .dropdown-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    box-sizing: border-box;
    border-radius: 9px;
    border: 1px solid var(--oqlis-border);
    background-color: var(--oqlis-surface);
    color: var(--oqlis-text-muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
    transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .icon:hover,
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .dropdown > .dropdown-toggle:hover,
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .dropdown.open > .dropdown-toggle {
    background-color: var(--oqlis-surface-alt);
    border-color: color-mix(in srgb, var(--main_color) 45%, var(--oqlis-border));
    color: var(--main_color);
}
body:is(.oqlis-embed, .oqlis-app) .dash-head-actions > .dropdown > .dropdown-toggle > svg { display: block; }

/* Dashboard vertical filter panel — persistent, collapsible side panel. It's
   fixed to the right below the header; when shown it PUSHES the grid left (no
   overlay, no scrim), so filters and data stay visible together. The toggle
   button collapses it (grid reclaims full width). Slides via translateX for a
   clean show/hide, in sync with the grid's margin transition. */
@media (min-width: 767px) {
    body #filterpane {
        position: fixed !important;
        top: 84px !important;
        right: 0 !important;
        bottom: 0 !important;
        height: auto !important;
        max-height: none !important;
        opacity: 1 !important;
        overflow-y: auto !important;
        width: 300px;
        padding-top: 0 !important;
        z-index: 21;
        transform: translateX(100%);
        transition: transform .3s cubic-bezier(.22, .9, .3, 1);
        box-shadow: -10px 0 24px -20px rgba(0, 0, 0, .20);
    }
    body #filterpane.page-aside-filter { transform: translateX(0); }
    /* Embedded / API dashboards have no fixed app top-bar — their header ends at
       45px, so anchor the vertical filter pane there instead of 84px. */
    body.oqlis-embed #filterpane { top: 45px !important; }
    /* Push the grid left by the panel width when shown. The width change is
       INSTANT (no transition) so the grid reflows once and the charts are
       resized a single time — no mid-transition jitter. The panel slides in
       over the reserved space via its own transform transition. */
    body .am-aside-filter .main-content { margin-right: 300px !important; }
}

/* Mobile-only close (X) button that lives in the vertical pane head — hidden on
   desktop (closed via the header funnel), revealed inside the mobile sheet
   below where that funnel is covered. Base rule first so the max-width reveal,
   coming later in source, wins on mobile at equal specificity. */
.dash-filter-close-mobile {
    display: none;
    align-items: center;
    justify-content: center;
}
.dash-filter-close-mobile .icon { font-size: 16px; }

/* ── Mobile: vertical filter pane → full-screen sheet ─────────────────────
   Below 767px there's no room to reserve/push space for the aside, and a legacy
   OQLIS-dark rule (.filterpane { position:fixed; top:-35px } under BOTH
   min-width:768px and min-height:768px — the height one fires on tall phones)
   drags the pane 35px above the viewport with no stacking context, so the
   header paints over it and the fields are unreachable. Override with a proper
   full-screen sheet: fixed inset:0, z-index above the app header (30) and
   bottom nav, scrollable, sticky head, and a visible close button (the header
   funnel is covered by the sheet). #filterpane (id) outranks the legacy class. */
@media (max-width: 766px) {
    body #filterpane.page-aside-filter {
        position: fixed !important;
        top: 0 !important;
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;
        width: 100% !important;
        height: 100% !important;
        max-height: 100% !important;
        padding-top: 0 !important;
        border: 0 !important;
        z-index: 1060;
        background: var(--oqlis-surface) !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
    }
    /* Keep title / Apply / close reachable while the filter list scrolls. */
    body #filterpane.page-aside-filter .dash-filter-head {
        position: sticky;
        top: 0;
        z-index: 2;
        background: var(--oqlis-surface);
    }
    .dash-filter-close-mobile { display: inline-flex; }
}

/* ── Dashboard filter panes (vertical aside + horizontal accordion) ───────
   Clean, gutter-aligned layout: a section-label heading with pill-chip action
   buttons, generous field spacing, and the app's surface tokens. Scoped to the
   two filter panes so nothing else is affected. */
/* Danger chip (Clear Default) — mirrors home-chip-success with the danger token */
.home-chip-danger {
    background: var(--oqlis-btn-danger);
    border-color: var(--oqlis-btn-danger-border);
    color: var(--oqlis-text);
}
.home-chip-danger .icon, .home-chip-danger .fa { color: var(--oqlis-text); }
.home-chip-danger:hover, .home-chip-danger:focus {
    color: var(--oqlis-text);
    background: var(--oqlis-btn-danger-hover);
    border-color: var(--oqlis-btn-danger-border);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--oqlis-btn-danger) 45%, transparent);
    transform: translateY(-1px);
}

#filterpane.page-aside-filter,
#horizontal_filter_pane {
    background: var(--oqlis-surface) !important;
}
/* Persistent horizontal filter bar: always visible, in-flow above the grid (no
   toggle, no scrim, no reflow — the grid simply flows below it). A slim,
   gutter-aligned bar so filters and data are visible at the same time. */
#horizontal_filter_pane.dash-filter-bar {
    padding: 0 !important;
    position: static;
    margin: 0 0 6px;
    background: var(--oqlis-surface);
    border-bottom: 1px solid var(--oqlis-border-soft);
    box-shadow: 0 8px 18px -16px rgba(0,0,0,.20);
}
/* Snap the bar open/closed instantly. Bootstrap's .collapsing animates height
   from 0 → auto, which reflows the whole chart grid below the in-flow bar on
   every frame → visible stutter. An instant height change reflows once (matches
   the vertical pane's instant reflow). */
#horizontal_filter_pane.collapsing {
    transition: none !important;
}
/* When collapsed via the toggle, Bootstrap's .collapse (display:none) hides it
   and the grid flows up; .collapse.in shows it (grid flows down). */
/* Slim the bar: the field panels carry an inline min-height:80px that makes the
   persistent bar unnecessarily tall — let them size to their content. */
#horizontal_filter_pane.dash-filter-bar .card-block .panel-body {
    min-height: 0 !important;
    padding-bottom: 2px !important;
}
#horizontal_filter_pane.dash-filter-bar .card-block { padding-bottom: 10px; }
#horizontal_filter_pane.dash-filter-bar .dash-filter-head { padding-top: 6px !important; padding-bottom: 0 !important; }
/* Filter field labels — body colour, medium (not heavy) weight, compact. */
#horizontal_filter_pane.dash-filter-bar .card-block label,
#filterpane .dash-filter-body label {
    font-size: 12px;
    font-weight: 500;
    line-height: 1.2;
    margin-bottom: 3px;
    color: var(--oqlis-text);
}
/* Hide the Parameters sub-section entirely when the dashboard has no parameters
   (JS toggles .dash-params-empty on both panes). Without this the "Parameters"
   heading is forced visible by .dash-filter-head's display:flex. The horizontal
   bar has the heading/body as direct children; the vertical panel wraps them in
   a bordered .dash-filter-section, so hide the whole section (divider included). */
#horizontal_filter_pane.dash-params-empty .panel-heading.params,
#horizontal_filter_pane.dash-params-empty .card-block.params,
#filterpane.dash-params-empty .dash-filter-section { display: none !important; }
#filterpane .panel-default, #horizontal_filter_pane .panel-default {
    background: transparent; border: 0; box-shadow: none; margin: 0;
}

/* Heading row: "Filters" / "Parameters" label left, action chips right */
.dash-filter-head {
    display: flex !important;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    padding: 8px var(--oqlis-page-gutter) 5px !important;
    min-height: 0 !important;
}
.dash-filter-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--oqlis-text-muted);
}
.dash-filter-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
.dash-filter-actions .home-chip { padding: 6px 13px; font-size: 12px; }

/* Overflow (⋯) menu holding the rare default-management actions (Save/Clear
   default), so Apply stays the single prominent action in the filter pane. */
.dash-filter-more { display: inline-flex; }
.dash-filter-more > .dropdown-toggle.home-chip {
    padding: 7px 11px;
    display: inline-flex;
    align-items: center;
    color: var(--oqlis-text-muted);
}
.dash-filter-more > .dropdown-toggle.home-chip:hover,
.dash-filter-more.open > .dropdown-toggle.home-chip {
    color: var(--oqlis-text);
    background-color: var(--oqlis-surface-alt);
}
.dash-filter-more > .dropdown-toggle.home-chip > svg { display: block; }
/* Modern filter overflow menu — rounded surface card, comfortable rows with a
   rounded hover, aligned icons, and modern type to match the rest of the UI. */
body .dash-filter-menu {
    min-width: 216px;
    padding: 6px;
    margin-top: 6px;
    border-radius: 12px;
    border: 1px solid var(--oqlis-border-soft);
    background: var(--oqlis-surface);
    box-shadow: 0 12px 30px -14px rgba(0, 0, 0, .35);
}
/* Drop the theme's fixed-position caret (left:142px) — it lands over the Apply
   button, not the ⋯. A modern floating card needs no pointer. */
body .dash-filter-menu:after,
body .dash-filter-menu:before { display: none !important; }
body .dash-filter-menu > li > a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.25;
    color: var(--oqlis-text);
    white-space: nowrap;
    transition: background-color .12s ease, color .12s ease;
}
body .dash-filter-menu > li > a:hover,
body .dash-filter-menu > li > a:focus {
    background-color: var(--oqlis-surface-alt);
    color: var(--oqlis-text);
}
body .dash-filter-menu > li > a > .icon {
    width: 16px;
    margin: 0;
    font-size: 15px;
    text-align: center;
    color: var(--oqlis-text-muted);
}
/* "Clear default" is destructive — red label + icon, subtle red hover */
body .dash-filter-menu > li > a.dash-filter-menu-danger,
body .dash-filter-menu > li > a.dash-filter-menu-danger:hover,
body .dash-filter-menu > li > a.dash-filter-menu-danger:focus,
body .dash-filter-menu > li > a.dash-filter-menu-danger > .icon { color: var(--oqlis-btn-danger-border); }
body .dash-filter-menu > li > a.dash-filter-menu-danger:hover {
    background-color: color-mix(in srgb, var(--oqlis-btn-danger-border) 12%, transparent);
}

/* Field area — align to the page gutter, give fields room */
.dash-filter-body { padding: 0 var(--oqlis-page-gutter) 6px; }
#horizontal_filter_pane .card-block { padding: 0 var(--oqlis-page-gutter) 6px; }
/* A little breathing room between individual filters (kept tight). Vertical
   filters stack, so they need the gap between each; the horizontal bar is a
   single row, so it only needs a small gap in case fields wrap. */
#filters_vertical > div { margin-bottom: 9px; }
#horizontalFilters > div { margin-bottom: 4px; }
#horizontal_filter_pane .card-block .panel,
#horizontal_filter_pane .card-block .panel-body {
    background: transparent !important; border: 0 !important; box-shadow: none !important; margin: 0 !important;
}

/* Parameters sub-section: separated by its uppercase heading + spacing (no
   divider line — the border read as a stray rule near the top of the pane). */
.dash-filter-section { margin-top: 2px; }
#horizontal_filter_pane .panel-heading.params { margin-top: 2px; }

/* Footer (Save / Clear default) */
.dash-filter-footer {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
    padding: 4px var(--oqlis-page-gutter) 8px;
}
.dash-filter-note {
    flex-basis: 100%;
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 10px;
    color: var(--oqlis-text-muted);
}
.dash-filter-note .icon { font-size: 12px; }
/* In the filter head the note sits inline right after the "Filters" title (not
   its own full-width row): auto width, and a little left gap from the title.
   Apply stays pinned right via .dash-filter-actions { margin-left:auto }. The
   vertical pane's footer note keeps the full-width basis above. */
.dash-filter-head > .dash-filter-note {
    flex: 0 1 auto;
    margin-left: 2px;
}

/* Grid panel resize grips (shown on hover via the lib's autohide). A corner
   bracket whose outer corner is curved at the panel's 12px radius instead of a
   sharp 90° point, so it traces the panel's rounded corner. Drawn as a masked
   SVG so it takes the muted theme colour. */
body .grid-stack > .grid-stack-item > .ui-resizable-se::before,
body .grid-stack > .grid-stack-item > .ui-resizable-sw::before { content: none !important; }
body .grid-stack > .grid-stack-item > .ui-resizable-se,
body .grid-stack > .grid-stack-item > .ui-resizable-sw {
    width: 10px;
    height: 10px;
    bottom: 3px;
    transform: none;
    text-indent: 0;
    background: var(--oqlis-text-muted);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: contain;
    mask-size: contain;
}
body .grid-stack > .grid-stack-item > .ui-resizable-se {
    right: 4px;
    -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'><path d='M2 18 L6 18 A12 12 0 0 0 18 6 L18 2'/></svg>");
    mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'><path d='M2 18 L6 18 A12 12 0 0 0 18 6 L18 2'/></svg>");
}
body .grid-stack > .grid-stack-item > .ui-resizable-sw {
    left: 4px;
    -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'><path d='M18 18 L14 18 A12 12 0 0 1 2 6 L2 2'/></svg>");
    mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'><path d='M18 18 L14 18 A12 12 0 0 1 2 6 L2 2'/></svg>");
}

/* ── Full stacked modern header for two-pane builder pages ────────────────
   Explore, Data Model edit/add, Connection add use an absolute two-pane
   layout (fixed-width .page-aside + margin-left .main-content), so the header
   can't be an inline block inside the content without overlapping the absolute
   aside. To keep the SAME stacked header as the rest of the app, we make the
   legacy header bar a taller, opaque, stacked header (uppercase breadcrumb
   above a large title) and push the whole content region down together via the
   .am-content offset — both panes shift as one, no per-pane surgery. Opaque
   surface-alt bg matches the page so it reads seamless and masks scroll.
   Opt in with `oqlis-stacked` on .page-head-breadcrumb + `spaces-breadcrumb`
   on its <ol class="breadcrumb">. Desktop only (mobile keeps the default). */
@media (min-width: 767px) {
    /* Shift the content region up to y=29 so the header's breadcrumb/title line
       up with the inline headers on the rest of the app. The header itself is
       in-flow (position:relative, below) so it scrolls with the content — NOT
       fixed — matching the spaces page and every other page. */
    .am-content:has(> .page-head-breadcrumb.oqlis-stacked) { top: 29px !important; }
    /* Make .am-aside the positioning context for the absolute .page-aside so
       both panes start together right below the in-flow header; drop the 15px
       top padding that only pushed the in-flow right pane down. */
    .am-content:has(> .page-head-breadcrumb.oqlis-stacked) .am-aside { padding-top: 0 !important; position: relative; }
    /* Bring the absolute .page-aside up flush with the stacked header. The
       theme's default top:-50px / padding-top:44px on .page-aside were tuned
       for the old tall fixed header and leave the aside floating below the new
       one. Zero them (and the right pane's top padding) so both panes start
       right under the header. Scoped to .oqlis-aside-flush so the explore aside
       (which uses its own inline padding-top) is untouched. */
    .oqlis-aside-flush .page-aside {
        top: 0 !important;
        padding-top: 0 !important;
    }
    .oqlis-aside-flush .page-aside > .content { padding-top: 0 !important; }
    .oqlis-aside-flush > .main-content { padding-top: 0 !important; }
    body .page-head-breadcrumb.oqlis-stacked {
        position: relative !important;   /* in-flow, scrolls with content (not fixed) */
        z-index: 5;                      /* above the absolute aside so the title sits on a clean bg */
        left: auto !important;
        right: auto !important;
        top: auto !important;
        height: auto;
        background: var(--oqlis-surface-alt);
        box-shadow: none !important;
        border: 0 !important;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        align-items: flex-start;
        gap: 8px;
        padding: 0 var(--oqlis-page-gutter) 12px;
    }
    body .page-head-breadcrumb.oqlis-stacked::after { display: none; }
    body .page-head-breadcrumb.oqlis-stacked h2 {
        order: 2;
        float: none;
        margin: 0;
        font-size: clamp(20px, 2.2vw, 26px);
        font-weight: 700;
        letter-spacing: -0.02em;
        line-height: 1.1;
        color: var(--oqlis-text);
    }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb {
        order: 1;
        float: none;
        margin: 0;
        padding: 0;
        background: transparent;
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .08em;
    }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li > a { color: var(--oqlis-text-muted); }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li > a:hover { color: var(--main_color); text-decoration: none; }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li.active { color: var(--oqlis-text-muted); }
    /* Page-level action chips (spaces/index.php style) placed on the right of
       the stacked builder header. The header is a fixed column bar, so anchor
       the chip group to the bottom-right, aligned with the title row. */
    body .page-head-breadcrumb.oqlis-stacked .spaces-actions {
        position: absolute;
        right: var(--oqlis-page-gutter);
        bottom: 8px;
        display: flex;
        align-items: center;
        gap: 10px;
    }
    body .page-head-breadcrumb.oqlis-stacked .spaces-actions .add-to-dashboard { position: relative; }
    /* Data model edit has many action chips — too many for the absolute
       bottom-right anchor (they'd overlap the title on narrower desktops).
       Opt in with `oqlis-stacked-wrap` to flow them in a wrapped row below the
       title instead. */
    body .page-head-breadcrumb.oqlis-stacked-wrap { padding-bottom: 14px; }
    body .page-head-breadcrumb.oqlis-stacked-wrap .spaces-actions {
        position: static;
        right: auto;
        bottom: auto;
        order: 3;
        width: 100%;
        margin-top: 6px;
        flex-wrap: wrap;
    }
}
/* Mobile: the two-pane builder header (explore, data model edit) otherwise
   falls back to the legacy AdminMart layout (title floated left, breadcrumb
   inline, action chips floating over the panels below). Give it the same clean
   stacked column as desktop — uppercase breadcrumb above a big title — but let
   the action chips flow in a wrapped row below the title instead of being
   absolutely anchored, matching the spaces page mobile header. */
@media (max-width: 766px) {
    body .page-head-breadcrumb.oqlis-stacked {
        position: relative !important;
        left: auto !important;
        right: auto !important;
        top: auto !important;
        width: auto;
        height: auto;
        background: transparent;
        box-shadow: none !important;
        border: 0 !important;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
        padding: 14px 16px 12px;
    }
    body .page-head-breadcrumb.oqlis-stacked::after { display: none; }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb {
        order: 1;
        float: none;
        margin: 0;
        padding: 0;
        background: transparent;
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .08em;
    }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li > a { color: var(--oqlis-text-muted); }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li > a:hover { color: var(--main_color); text-decoration: none; }
    body .page-head-breadcrumb.oqlis-stacked .breadcrumb > li.active { color: var(--oqlis-text-muted); }
    body .page-head-breadcrumb.oqlis-stacked h2 {
        order: 2;
        float: none;
        margin: 0;
        font-size: 24px;
        font-weight: 700;
        letter-spacing: -0.02em;
        line-height: 1.1;
        color: var(--oqlis-text);
    }
    body .page-head-breadcrumb.oqlis-stacked .spaces-actions {
        order: 3;
        position: static;
        right: auto;
        bottom: auto;
        margin-top: 6px;
        width: 100%;
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 8px;
    }
    body .page-head-breadcrumb.oqlis-stacked .spaces-actions .add-to-dashboard { position: relative; }
}

/* Explore-page "+ Dashboard" dropdown (.add-to-dashboard) reuses the custom
   .dropdown-content menu, but it isn't inside .panel-heading .tool-buttons, so
   the modern menu styling never matched it — it fell back to the bare base rule
   (width:100% of the narrow button → cramped, unstyled bare-text items). Give it
   the same rounded surface-card menu treatment. */
.add-to-dashboard { position: relative; }
.add-to-dashboard .dropdown-content {
    right: 0;
    left: auto;
    top: calc(100% + 4px);
    width: max-content;
    min-width: 180px;
    padding: 6px;
    background-color: var(--oqlis-surface);
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 10px;
    box-shadow: 0 10px 18px rgba(0, 0, 0, 0.15);
}
.add-to-dashboard .dropdown-content .dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    font-size: 12px;
    line-height: 1.3;
    border-radius: 6px;
    color: var(--oqlis-text);
    text-decoration: none;
    white-space: nowrap;
}
.add-to-dashboard .dropdown-content .dropdown-item i {
    width: 15px;
    flex: 0 0 auto;
    text-align: center;
    font-size: 12px;
    color: var(--oqlis-text-muted);
}
.add-to-dashboard .dropdown-content .dropdown-item:hover {
    background-color: var(--oqlis-surface-muted);
}
.add-to-dashboard .dropdown-content .dropdown-item:hover i { color: var(--main_color); }
/* Panel-heading inline toolbar (e.g. QUERY panel Run/Save/Format chips on the
   data model edit page). Lay the accordion title and its action chips out on
   one row; chips wrap below the title on narrow screens. */
.panel-heading.oqlis-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.panel-heading.oqlis-panel-head .panel-title { margin: 0; }
.panel-heading.oqlis-panel-head .panel-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}
body .am-top-header a,
body .am-top-header .am-user-nav {
    pointer-events: auto;
}

/* ── Top-right user nav (avatar, workspace switcher, sign out) ────────── */
/* body-prefixed: these must out-rank the division theme CSS loaded later */
@media (min-width: 767px) {
    body .am-top-header .navbar-collapse .am-user-nav {
        background-color: transparent;
    }
}
body .am-top-header .navbar-collapse .am-user-nav > li.dropdown > a.dropdown-toggle {
    display: flex;
    align-items: center;
    gap: 7px;
    height: 45px;
    padding: 0 12px 0 10px;
    transition: background .15s ease;
}
body .am-top-header .navbar-collapse .am-user-nav > li.dropdown > a.dropdown-toggle:hover,
body .am-top-header .navbar-collapse .am-user-nav > li.dropdown.open > a.dropdown-toggle {
    background: color-mix(in srgb, var(--main_color) 20%, transparent);
}
body .am-top-header .navbar-collapse .am-user-nav > li.dropdown > a img {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--main_color) 85%, transparent);
}
body .am-user-nav .angle-down {
    font-size: 15px;
    color: var(--oqlis-text-muted);
    transition: transform .2s ease;
}
body .am-user-nav li.dropdown.open .angle-down { transform: rotate(180deg); }

/* Menu card */
body .am-top-header .navbar-collapse .am-user-nav .dropdown-menu {
    min-width: 250px;
    padding: 6px;
    margin-top: 8px;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 12px;
    background: var(--oqlis-surface);
    box-shadow: 0 4px 10px rgba(0, 0, 0, .08), 0 16px 40px -12px rgba(0, 0, 0, .28);
}
body .am-top-header .navbar-collapse .am-user-nav > li.dropdown .dropdown-menu:after {
    display: none;
}
body .am-user-nav .oqlis-menu-user {
    padding: 8px 12px 7px;
}
body .am-user-nav .oqlis-menu-user-name {
    display: block;
    font-size: 13px;
    font-weight: 600;
    color: var(--oqlis-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
body .am-user-nav .oqlis-menu-header {
    padding: 6px 12px 4px;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--oqlis-text-muted);
}
body .am-user-nav .dropdown-menu > li > a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--oqlis-text);
    transition: background .12s ease;
}
body .am-user-nav .dropdown-menu > li > a:hover,
body .am-user-nav .dropdown-menu > li > a:focus {
    background: color-mix(in srgb, var(--main_color) 12%, transparent);
    color: var(--oqlis-text);
}
body .am-user-nav > li.dropdown .dropdown-menu li a .icon {
    font-size: 16px;
    margin-right: 0;
    color: var(--oqlis-text-muted);
}
body .am-user-nav .oqlis-ws-check {
    width: 16px;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    opacity: 0;
}
body .am-user-nav .oqlis-ws.active .oqlis-ws-check { opacity: 1; }

/* Active workspace: pulsing status dot (theme success green) */
@keyframes oqlis-ws-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--oqlis-btn-success-border) 50%, transparent); }
    70%  { box-shadow: 0 0 0 6px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}
body .am-user-nav .oqlis-ws-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--oqlis-btn-success-border);
}
body .am-user-nav .oqlis-ws.active .oqlis-ws-dot {
    animation: oqlis-ws-pulse 2s cubic-bezier(.66, 0, 0, 1) infinite;
}
@media (prefers-reduced-motion: reduce) {
    body .am-user-nav .oqlis-ws-dot { animation: none !important; }
}
body .am-user-nav .oqlis-ws.active > a {
    background: color-mix(in srgb, var(--main_color) 10%, transparent);
    font-weight: 600;
}
body .am-user-nav .oqlis-menu-divider {
    height: 1px;
    margin: 6px 8px;
    background: var(--oqlis-border-soft);
    list-style: none;
}

/* ═══════════ Home page (moved from home.php body style for head loading) ═══════════ */
/* ── Entrance ─────────────────────────────────────────────────────────── */
/* @keyframes home-rise lives in the Motion section near the top of this file */
.home-page > .home-hero,
.home-page > .home-stats,
.home-page > .home-columns {
    animation: home-rise .55s cubic-bezier(.22,.9,.3,1) both;
}
.home-page > .home-stats   { animation-delay: .07s; }
.home-page > .home-columns { animation-delay: .14s; }

/* ── Two-column body: content left, activity rail right ──────────────── */
.home-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 26px;
    align-items: start;
}
/* minmax(0,...) (not bare 1fr, which is minmax(auto,1fr)) so the column can
   shrink below the carousel's content width — otherwise the recent rows blow
   the column out and the page overflows horizontally on mobile */
@media (max-width: 1100px) {
    .home-columns { grid-template-columns: minmax(0, 1fr); }
}
/* Grid children must also allow shrinking below their content */
.home-col-main { min-width: 0; }

/* ── Latest activity stream ───────────────────────────────────────────── */
.home-stream {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 14px;
    background: var(--oqlis-surface);
}
.home-stream-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    color: var(--oqlis-text);
    text-decoration: none;
    transition: background .12s ease;
    position: relative;
}
/* Timeline connector: a vertical line threading through the icon nodes */
.home-timeline .home-stream-item::before {
    content: "";
    position: absolute;
    left: 24px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--oqlis-border-soft);
}
.home-timeline .home-stream-item:first-child::before { top: 50%; }
.home-timeline .home-stream-item:last-child::before { bottom: 50%; }
.home-timeline .home-stream-item:only-child::before { display: none; }
.home-stream-item > * { position: relative; z-index: 1; }
a.home-stream-item:hover,
a.home-stream-item:focus {
    background: color-mix(in srgb, var(--main_color) 10%, transparent);
    color: var(--oqlis-text);
    text-decoration: none;
}
.home-stream-icon {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    display: grid;
    place-items: center;
    border-radius: 8px;
    /* Opaque (mixed over surface, not transparent) so it occludes the timeline line */
    background: color-mix(in srgb, var(--main_color) 14%, var(--oqlis-surface));
    border: 1px solid color-mix(in srgb, var(--main_color) 25%, var(--oqlis-border));
}
.home-stream-icon .icon {
    font-size: 15px;
    color: color-mix(in srgb, var(--main_color) 70%, var(--oqlis-text));
}
/* Sidebar SVGs are white-filled: darken for light theme, keep white in dark */
.home-stream-icon img {
    width: 16px;
    height: 16px;
    display: block;
    filter: brightness(.35);
}
[data-theme="dark"] .home-stream-icon img { filter: none; }
.home-stream-meta {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.home-stream-name {
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.home-stream-sub {
    font-size: 10.5px;
    color: var(--oqlis-text-muted);
}
.home-stream-empty {
    padding: 14px 10px;
    font-size: 12.5px;
    color: var(--oqlis-text-muted);
    text-align: center;
}
/* ── Hero ─────────────────────────────────────────────────────────────── */
.home-hero {
    position: relative;
    margin: 0 0 22px;
    padding: 32px 34px 34px;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 18px;
    background:
        radial-gradient(1100px 320px at 6% -50%, color-mix(in srgb, var(--main_color) 16%, transparent), transparent 62%),
        radial-gradient(900px 340px at 96% 140%, color-mix(in srgb, var(--main_color) 10%, transparent), transparent 58%),
        var(--oqlis-surface);
    overflow: hidden;
}
/* No Lumin ask bar: the greeting is the only content, so tighten the band
   instead of leaving a tall empty hero */
.home-hero--compact {
    padding: 20px 34px 22px;
}
.home-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: radial-gradient(color-mix(in srgb, var(--oqlis-text) 8%, transparent) 1px, transparent 1px);
    background-size: 22px 22px;
    -webkit-mask-image: linear-gradient(115deg, transparent 45%, #000);
    mask-image: linear-gradient(115deg, transparent 45%, #000);
    pointer-events: none;
}
.home-hero > * { position: relative; }
.home-hero-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}
.home-hero-date {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .12em;
    color: var(--oqlis-text-muted);
}
.home-hero-star {
    font-size: 20px;
    color: var(--oqlis-text-muted);
    cursor: pointer;
    transition: color .15s ease, transform .15s ease;
}
.home-hero-star:hover {
    color: var(--main_color);
    transform: scale(1.12);
}
.home-hero-greeting {
    margin: 0;
    font-size: clamp(24px, 3.2vw, 34px);
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: var(--oqlis-text);
    text-wrap: balance;
}
#greetingWord { font-weight: 300; color: var(--oqlis-text-muted); }
#userName     { font-weight: 700; }

/* ── Lumin ask bar (inside hero) ──────────────────────────────────────── */
.home-page #chatInputBar {
    position: relative;
    z-index: 6;
    margin: 24px auto 0;
    padding-bottom: 0;
    width: min(780px, 100%);
    border-top: none !important;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    border-color: var(--main_color);
}
.home-page #chatInputBar .input-group { width: 100%; }
.home-page #chatInputBar .chat-input-group {
    position: relative;
    display: block;
    min-width: 0;
}
.home-page #chatInputBar .lumin-input-shell {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 6px 8px 6px 6px;
    background: var(--oqlis-input);
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 26px;
    box-shadow: var(--lumin-shadow-md);
}
.home-page #chatInputBar .lumin-input-shell {
    transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
}
.home-page #chatInputBar .lumin-input-shell:focus-within {
    background: var(--oqlis-surface);
    border-color: color-mix(in srgb, var(--main_color) 65%, var(--oqlis-border));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--main_color) 20%, transparent);
}
.home-page #chartChatInput {
    flex: 1 1 auto;
    min-width: 0;
    width: 1%;
    height: auto;
    border: 0;
    background: transparent;
    color: var(--oqlis-text);
    padding: 6px 4px 6px 0;
    font-size: 15px;
    box-shadow: none;
}
.home-page #chartChatInput:focus { outline: none; box-shadow: none; }
.home-page #chartSendBtn {
    position: static;
    transform: none;
    height: 36px;
    width: 36px;
    padding: 0;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    line-height: 1;
    flex: 0 0 auto;
    z-index: 1;
    transition: transform .15s ease, box-shadow .15s ease;
}
.home-page #chartSendBtn:hover:not(:disabled) {
    transform: translateY(-1px) scale(1.06);
    box-shadow: 0 4px 12px color-mix(in srgb, var(--main_color) 35%, transparent);
}
.home-page #chartSendBtn:active:not(:disabled) { transform: scale(.94); }
.home-page #chatInputBar .lumin-dashboard-select-wrap {
    flex: 0 0 auto;
    min-width: min(240px, 34vw);
    max-width: min(240px, 34vw);
}
.home-page #chatInputBar .lumin-dashboard-select-wrap .select2-container { width: 100% !important; }
.home-page #chatInputBar .lumin-dashboard-select-wrap .select2-selection--single {
    display: flex !important;
    align-items: center !important;
    height: 36px !important;
    border: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 20px !important;
    background: var(--oqlis-surface) !important;
}
.home-page #chatInputBar .lumin-dashboard-select-wrap .select2-selection__rendered {
    display: flex !important;
    align-items: center !important;
    height: 100% !important;
    line-height: normal !important;
    padding-left: 10px !important;
    padding-right: 28px !important;
    font-size: 12px !important;
    font-weight: 600;
    color: var(--oqlis-text) !important;
}
.home-page #chatInputBar .lumin-dashboard-select-wrap .select2-selection__arrow {
    height: 34px !important;
    right: 6px !important;
}
.home-page #chatInputBar .dm-divider {
    width: 1px;
    height: 22px;
    background: var(--oqlis-border-soft);
    flex: 0 0 auto;
}
@media (max-width: 991px) {
    .home-page #chatInputBar { flex-wrap: wrap; }
}

/* ── Stats strip ──────────────────────────────────────────────────────── */
.home-stats {
    display: flex;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 14px;
    background: var(--oqlis-surface);
    margin-bottom: 28px;
    overflow: hidden;
}
.home-stat {
    display: flex;
    flex-direction: column;
    gap: 7px;
    padding: 13px 18px;
    border-left: 1px solid var(--oqlis-border-soft);
    min-width: 0;
    /* Flex so hiding a tile (e.g. Explores for readonly users) redistributes
       space instead of leaving a gap */
    flex: 1 1 0;
    /* The activity heatmap makes its cell the tallest; centre the others
       vertically so the strip reads as one balanced row */
    justify-content: center;
}
.home-stat:first-child { border-left: 0; flex-grow: 1.2; }
.home-stat-activity { flex-grow: 1.8; }
.home-stat-label {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--oqlis-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.home-stat-valuewrap {
    display: flex;
    align-items: flex-end;
    min-height: 26px;
}
.home-stat-value {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.1;
    color: var(--oqlis-text);
    font-variant-numeric: tabular-nums;
}
.home-stat-value-sm { font-size: 14px; font-weight: 600; padding-bottom: 3px; }
.home-stat-activity #activity { width: 100%; }

/* Calendar heatmap (GitHub-contributions style): rows = weeks, cols = Mon..Sun */
.home-heatmap {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
    width: 100%;
    padding: 2px 0;
}
.home-heatlabel {
    font-size: 8px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: .04em;
    text-align: center;
    color: var(--oqlis-text-muted);
}
.home-heatcell {
    height: 8px;
    border-radius: 2px;
    background: var(--oqlis-surface-muted);
}
.home-heatcell.empty { background: transparent; }
.home-heatcell.l1 { background: color-mix(in srgb, var(--main_color) 28%, var(--oqlis-surface-muted)); }
.home-heatcell.l2 { background: color-mix(in srgb, var(--main_color) 50%, var(--oqlis-surface-muted)); }
.home-heatcell.l3 { background: color-mix(in srgb, var(--main_color) 75%, var(--oqlis-surface-muted)); }
.home-heatcell.l4 { background: var(--main_color); }
.home-heatcell[data-day] { cursor: pointer; }
.home-heatcell:not(.empty):hover {
    outline: 1px solid color-mix(in srgb, var(--main_color) 70%, var(--oqlis-border));
    outline-offset: 0;
}
.home-heatcell.selected {
    outline: 2px solid var(--main_color);
    outline-offset: 1px;
}

/* Active day-filter chip in the Latest Activity title */
.home-stream-filterchip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--main_color) 45%, var(--oqlis-border));
    background: color-mix(in srgb, var(--main_color) 14%, transparent);
    color: var(--oqlis-text);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .05em;
    cursor: pointer;
    text-transform: none;
}
.home-stream-filterchip:hover {
    background: color-mix(in srgb, var(--main_color) 26%, transparent);
}
@media (max-width: 991px) {
    .home-stats { flex-wrap: wrap; }
    .home-stat { flex: 1 1 45%; border-left: 0; border-top: 1px solid var(--oqlis-border-soft); }
    .home-stat:nth-child(-n+2) { border-top: 0; }
    .home-stat:nth-child(2n) { border-left: 1px solid var(--oqlis-border-soft); }
    .home-stat-activity { flex: 1 1 100%; border-left: 0 !important; }
}

/* ── Recent carousels (dashboards / explores) ─────────────────────────── */
.home-carousel {
    display: flex;
    gap: 16px;
    overflow-x: auto;
    scroll-behavior: smooth;
    scroll-snap-type: x proximity;
    -ms-overflow-style: none;   /* hide scrollbar (IE/Edge) */
    scrollbar-width: none;      /* Firefox */
    /* room so the hover lift + shadow isn't clipped by overflow */
    padding: 4px 2px 6px;
    margin: 0 -2px;
}
.home-carousel::-webkit-scrollbar { display: none; }
.home-carousel > .home-card {
    flex: 0 0 calc((100% - 4 * 16px) / 5);   /* 5 per view */
    scroll-snap-align: start;
}
.home-carousel > .home-empty { flex: 1 1 100%; }
@media (max-width: 1250px) {
    .home-carousel > .home-card { flex-basis: calc((100% - 3 * 16px) / 4); }  /* 4 */
}
@media (max-width: 900px) {
    .home-carousel > .home-card { flex-basis: calc((100% - 16px) / 2); }       /* 2 */
}
@media (max-width: 560px) {
    .home-carousel > .home-card { flex-basis: 82%; }                            /* 1 + peek */
}

/* Section head with carousel arrows on the right */
.home-section-head {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}
.home-section-head .home-section-title { flex: 1; margin-bottom: 0; }
.home-carousel-nav {
    display: flex;
    gap: 6px;
    flex: 0 0 auto;
}
.home-carousel-nav[hidden] { display: none; }
.home-carousel-btn {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    border: 1px solid var(--oqlis-border-soft);
    background: var(--oqlis-surface);
    color: var(--oqlis-text-muted);
    display: grid;
    place-items: center;
    cursor: pointer;
    font-size: 17px;
    line-height: 1;
    transition: background .15s ease, border-color .15s ease, color .15s ease, opacity .15s ease;
}
.home-carousel-btn:hover:not(:disabled) {
    color: var(--oqlis-text);
    border-color: color-mix(in srgb, var(--main_color) 60%, var(--oqlis-border));
    background: color-mix(in srgb, var(--main_color) 10%, transparent);
}
.home-carousel-btn:disabled { opacity: .35; cursor: default; }


@media (max-width: 767px) {
    .home-hero { padding: 22px 20px 24px; }
}

/* ═══════════ Spaces page (moved from spaces/index.php body style) ═══════════ */
/* ── Page entrance ────────────────────────────────────────────────────── */
.home-page > .spaces-head,
.home-page > .home-section {
    animation: home-rise .55s cubic-bezier(.22,.9,.3,1) both;
}
.home-page > .home-section:nth-of-type(1) { animation-delay: .07s; }
.home-page > .home-section:nth-of-type(2) { animation-delay: .14s; }
.home-page > .home-section:nth-of-type(3) { animation-delay: .21s; }

/* The two-pane builder pages (explore, data model edit) don't use the
   .home-page > .home-section structure, so give their header + content region
   the same entrance rise as the rest of the app. */
/* NOTE: use `backwards` (not `both`) fill here. `both`/`forwards` leaves a
   retained identity-matrix transform on the element after the animation ends,
   which creates a stacking context + containing block. Because the explore
   modals are DOM children of .am-aside, that trapped their position:fixed
   layer below the body-level .modal-backdrop. `backwards` reverts transform to
   `none` once the rise completes, so modals layer above the backdrop again. */
.page-head-breadcrumb.oqlis-stacked {
    animation: home-rise .55s cubic-bezier(.22,.9,.3,1) backwards;
}
.am-content:has(> .page-head-breadcrumb.oqlis-stacked) .am-aside {
    animation: home-rise .55s cubic-bezier(.22,.9,.3,1) backwards;
    animation-delay: .08s;
}

/* ── Page head ────────────────────────────────────────────────────────── */
.spaces-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin: 0 0 26px;
}
.home-page .spaces-breadcrumb {
    background: transparent;
    padding: 0;
    margin: 0 0 8px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .08em;
}
.home-page .spaces-breadcrumb > li > a { color: var(--oqlis-text-muted); }
.home-page .spaces-breadcrumb > li > a:hover { color: var(--main_color); text-decoration: none; }
.home-page .spaces-breadcrumb > li + li:before { color: var(--oqlis-text-muted); }
.spaces-title {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0;
    font-size: clamp(22px, 2.6vw, 30px);
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--oqlis-text);
}
.spaces-avatar {
    display: inline-flex;
    border-radius: 10px;
    border: 1px solid var(--oqlis-border-soft);
    overflow: hidden;
    background: var(--oqlis-surface);
    flex: 0 0 auto;
}
.spaces-avatar img {
    width: 36px;
    height: 36px;
    display: block;
    object-fit: cover;
    border-radius: 0;
    /* Identicon/space images have a baked-in white background; the theme
       filter inverts it in dark mode like dashboard thumbnails */
    filter: var(--oqlis-filter);
}
.spaces-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.spaces-star {
    font-size: 20px;
    color: var(--oqlis-text-muted);
    cursor: pointer;
    padding-left: 4px;
    transition: color .15s ease, transform .15s ease;
}
.spaces-star:hover {
    color: var(--main_color);
    transform: scale(1.12);
}

/* ── Tables ───────────────────────────────────────────────────────────── */
.home-table-wrap {
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 14px;
    background: var(--oqlis-surface);
    padding: 8px 16px 12px;
    overflow-x: auto;
}
.home-table-wrap .table { margin-bottom: 8px; }

/* ── IoT page: multiple list sections in a responsive 2-column grid ────── */
.iot-lists {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 24px 22px;
}
.iot-lists .home-section { margin-bottom: 0; min-width: 0; }
.iot-lists .home-table-wrap { min-height: 88px; }
@media (max-width: 900px) {
    .iot-lists { grid-template-columns: 1fr; }
}
/* Action chips grouped on the right of a section header */
.home-section-head .home-section-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}
/* A KPI label that links to a detail page */
a.home-stat-label { text-decoration: none; transition: color .15s ease; }
a.home-stat-label:hover { color: var(--main_color); }

/* ── Activity stream day headers ──────────────────────────────────────── */
.home-stream-day {
    position: relative;
    padding: 7px 10px 3px 44px;
    font-size: 9.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--oqlis-text-muted);
}
.home-timeline .home-stream-day::before {
    content: "";
    position: absolute;
    left: 24px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--oqlis-border-soft);
}
/* Line starts/ends at the outermost entries, whatever their kind */
.home-timeline > :first-child::before { top: 50%; }
.home-timeline > :last-child::before { bottom: 50%; }

/* ── Entrance plays once per session (flag set by inline script) ──────── */
html.oqlis-no-entrance .home-page > .home-hero,
html.oqlis-no-entrance .home-page > .home-stats,
html.oqlis-no-entrance .home-page > .home-columns,
html.oqlis-no-entrance .home-card-grid .home-card,
html.oqlis-no-entrance .home-space-row .home-chip {
    animation: none !important;
}

/* ── Cmd/Ctrl+K quick switcher ────────────────────────────────────────── */
body .am-user-nav .oqlis-cmdk-trigger > a {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 45px;
    padding: 0 10px;
    color: var(--oqlis-text-muted);
    transition: background .15s ease;
}
body .am-user-nav .oqlis-cmdk-trigger > a:hover {
    background: color-mix(in srgb, var(--main_color) 20%, transparent);
    color: var(--oqlis-text);
}
body .am-user-nav .oqlis-cmdk-trigger .icon { font-size: 17px; }
.oqlis-cmdk-kbd,
.oqlis-cmdk kbd {
    padding: 2px 6px;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 5px;
    background: var(--oqlis-surface);
    color: var(--oqlis-text-muted);
    font-family: inherit;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .04em;
    line-height: 1.2;
}
.oqlis-cmdk {
    position: fixed;
    inset: 0;
    z-index: 2000;
}
.oqlis-cmdk-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, .45);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
.oqlis-cmdk-panel {
    position: relative;
    width: min(560px, 92vw);
    margin: 12vh auto 0;
    border: 1px solid var(--oqlis-border-soft);
    border-radius: 14px;
    background: var(--oqlis-surface);
    box-shadow: 0 8px 16px rgba(0, 0, 0, .18), 0 32px 80px -16px rgba(0, 0, 0, .5);
    overflow: hidden;
    animation: home-rise .18s ease both;
}
@media (prefers-reduced-motion: reduce) {
    .oqlis-cmdk-panel { animation: none; }
}
.oqlis-cmdk-inputrow {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 13px 15px;
    border-bottom: 1px solid var(--oqlis-border-soft);
}
.oqlis-cmdk-inputrow .icon {
    font-size: 17px;
    color: var(--oqlis-text-muted);
}
#oqlisCmdkInput {
    flex: 1;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--oqlis-text);
    font-size: 15px;
}
#oqlisCmdkInput::placeholder { color: var(--oqlis-text-muted); }
.oqlis-cmdk-results {
    max-height: 46vh;
    overflow-y: auto;
    padding: 6px;
}
.oqlis-cmdk-group {
    padding: 8px 10px 4px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--oqlis-text-muted);
}
.oqlis-cmdk-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    color: var(--oqlis-text);
    text-decoration: none;
}
.oqlis-cmdk-item:hover { text-decoration: none; color: var(--oqlis-text); }
.oqlis-cmdk-item.active {
    background: color-mix(in srgb, var(--main_color) 12%, transparent);
}
.oqlis-cmdk-icon {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    display: grid;
    place-items: center;
    border-radius: 7px;
    background: color-mix(in srgb, var(--main_color) 14%, var(--oqlis-surface));
    border: 1px solid color-mix(in srgb, var(--main_color) 25%, var(--oqlis-border));
}
.oqlis-cmdk-icon img {
    display: block;
    filter: brightness(.35);
}
[data-theme="dark"] .oqlis-cmdk-icon img { filter: none; }
.oqlis-cmdk-name {
    flex: 1;
    min-width: 0;
    font-size: 13.5px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.oqlis-cmdk-type {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--oqlis-text-muted);
}
.oqlis-cmdk-empty {
    padding: 22px;
    text-align: center;
    font-size: 13px;
    color: var(--oqlis-text-muted);
}


/* ── Legacy Bootstrap panels: match the home/spaces card language ──────── */
/* Applies app-wide (oqlis-modern.css is global). Rounds corners and gives a
   hairline border in the theme token so content panels read like the new
   cards. Selectors are body-prefixed because the theme CSS loads after this
   file and would otherwise win at equal specificity. No overflow:hidden
   (would clip select2/datatable/popover menus); corners are rounded on the
   panel plus its first/last children instead. */
body .panel {
    border-radius: 12px;
}
body .panel-default {
    border: 1px solid var(--oqlis-border-soft);
}
body .panel-default > .panel-heading {
    border-top-left-radius: 12px;
    border-top-right-radius: 12px;
    border-bottom-color: var(--oqlis-border-soft);
}
/* Round the outer corners of whichever child sits at the panel edges */
body .panel > .panel-heading:last-child,
body .panel > .panel-body:last-child,
body .panel > .panel-footer:last-child,
body .panel > .panel-collapse:last-child > .panel-body {
    border-bottom-left-radius: 12px;
    border-bottom-right-radius: 12px;
}
body .panel > .panel-body:first-child,
body .panel > .panel-collapse:first-child > .panel-body {
    border-top-left-radius: 12px;
    border-top-right-radius: 12px;
}
body .panel-footer {
    background-color: var(--oqlis-surface-alt);
    border-top-color: var(--oqlis-border-soft);
}

/* Accordion groups: keep the rounded outer frame on each stacked panel */
body .panel-group.accordion .panel {
    border-radius: 12px;
}

/* Panels nested inside tab content: drop the border so they read as flat
   regions separated by spacing/headings, not boxes-within-a-box. The outer
   tab card (which contains .tab-content) keeps its border. border-color kept
   transparent (not border:0) so no layout shift. */
body .tab-content .panel,
body .tab-content .panel-default {
    border-color: transparent;
    box-shadow: none;
}

/* ── Sidebar theme toggle (sun/moon) ──────────────────────────────────── */
/* Sits in the dark sidebar in both themes, so it is styled against the
   sidebar tokens. Knob slides between a sun (light) and moon (dark) icon;
   the active mode's icon lifts to full colour on the knob. */
.oqlis-theme-input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}
.oqlis-theme-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 8px 0 10px;
    cursor: pointer;
    /* Remove the mobile double-tap-zoom gesture wait so the toggle fires on the
       first tap (was contributing to a "double tap to switch theme" on iOS). */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}
/* The toggle's list item is not a nav link; suppress the sidebar hover fill */
body .am-left-sidebar .sidebar-elements > li.oqlis-theme-li:hover {
    background-color: transparent;
}
.oqlis-theme-track {
    position: relative;
    display: block;
    width: 54px;
    height: 26px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .08);
    border: 1px solid var(--oqlis-sidebar-border);
    transition: background .2s ease, border-color .2s ease;
}
.oqlis-theme-toggle:hover .oqlis-theme-track {
    background: rgba(255, 255, 255, .14);
    border-color: color-mix(in srgb, var(--main_color) 45%, var(--oqlis-sidebar-border));
}
.oqlis-theme-input:focus-visible + .oqlis-theme-toggle .oqlis-theme-track {
    outline: 2px solid var(--main_color);
    outline-offset: 2px;
}
.oqlis-theme-ico {
    position: absolute;
    top: 1px;
    z-index: 2;
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    font-size: 12px;
    color: var(--oqlis-sidebar-muted);
    transition: color .2s ease;
}
/* Each icon sits in a 22px box that overlaps the knob's two rest positions
   exactly. Offsets are inside the track's 1px border, so 1px here = 2px from the
   outer edge — a 22px knob in a 26px track then has an even 2px gap all round.
   Knob centre: left 1px → 13; +translateX(28) → 41; icons match those centres. */
.oqlis-theme-ico-sun { left: 1px; }
.oqlis-theme-ico-moon { left: 29px; }
.oqlis-theme-knob {
    position: absolute;
    top: 1px;
    left: 1px;
    z-index: 1;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .45);
    transition: transform .24s cubic-bezier(.34, 1.3, .4, 1);
}
.oqlis-theme-input:checked + .oqlis-theme-toggle .oqlis-theme-knob {
    transform: translateX(28px);
}
/* the active mode's icon rides on the knob in full colour */
.oqlis-theme-input:not(:checked) + .oqlis-theme-toggle .oqlis-theme-ico-sun {
    color: #e8a33d;
}
.oqlis-theme-input:checked + .oqlis-theme-toggle .oqlis-theme-ico-moon {
    color: #7d8cc4;
}

/* ══════════════════════════════════════════════════════════════════════════
   Mobile app-style header + navigation drawer (phones only)
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
    /* ── No app-name header bar — reclaim the top real estate ──────────── */
    body .am-top-header {
        background: transparent !important;
        border: 0 !important;
        box-shadow: none !important;
        height: 0 !important;
        min-height: 0 !important;
        position: relative;
        z-index: 1003;
    }
    /* Hide the app name + hamburger + collapse chevron entirely */
    body .am-top-header .navbar-header,
    body .am-top-header .am-toggle-top-header-menu { display: none !important; }
    /* Search lives in the bottom nav, not the header */
    body .am-top-header .oqlis-cmdk-trigger { display: none !important; }

    /* ── Account/workspace switcher is relocated into the bottom nav ─────── */
    /* JS moves the user <li.dropdown> into .oqlis-bottomnav as an "Account"
       cell; the now-empty top header collapse is hidden. */
    body .am-top-header .container-fluid { display: none !important; }

    /* Header bars now have a fully clear top-right (avatar moved to the bar):
       just drop the desktop full-width inset. */
    body .page-head,
    body .page-head-breadcrumb {
        right: 0 !important;
        left: 0 !important;
    }
    body .page-head::after,
    body .page-head-breadcrumb::after { display: none !important; }

    /* The relocated .am-user-nav <ul> behaves like a bottom-nav cell.
       It's moved intact so every .am-user-nav item style still matches. */
    body .oqlis-bottomnav .oqlis-bottomnav-account {
        flex: 1 1 0;
        min-width: 0;
        list-style: none;
        display: flex;
        position: relative;
        margin: 0;
        padding: 0;
        background: transparent !important;
    }
    /* the search li rode along inside the ul — keep it hidden here too */
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.oqlis-cmdk-trigger { display: none !important; }
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown {
        flex: 1 1 0;
        min-width: 0;
        display: flex;
        float: none;
        margin: 0;
        position: static;
    }
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown > a.dropdown-toggle {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        min-width: 0;
        padding: 8px 0;
        color: var(--oqlis-sidebar-muted);
        font-size: 10px;
        font-weight: 600;
        letter-spacing: .02em;
        text-decoration: none;
        background: none !important;
        border: 0 !important;
        box-shadow: none !important;
        -webkit-tap-highlight-color: transparent;
    }
    /* avatar becomes the cell "icon" */
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown > a.dropdown-toggle img {
        width: 22px !important;
        height: 22px !important;
        border-radius: 50%;
        object-fit: cover;
        display: block;
        margin: 0 !important;
    }
    body .oqlis-bottomnav .oqlis-bottomnav-account .user-name,
    body .oqlis-bottomnav .oqlis-bottomnav-account .angle-down { display: none !important; }
    /* compact "Account" label under the avatar */
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown > a.dropdown-toggle::after {
        content: "Account";
        line-height: 1;
    }
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown.open > a.dropdown-toggle { color: var(--main_color); }

    /* dropdown pushes UP from the cell (absolute — the mobile theme leaves it
       static, and it must escape the bar), right-aligned, height-capped */
    body .oqlis-bottomnav .oqlis-bottomnav-account > li.dropdown .dropdown-menu {
        position: absolute !important;
        top: auto !important;
        bottom: calc(100% + 10px) !important;
        right: 6px !important;
        left: auto !important;
        min-width: 240px;
        max-height: calc(100vh - 120px) !important;
        overflow-y: auto;
        transform-origin: bottom right;
        background: var(--oqlis-surface) !important;
    }

    /* ── Menu as a bottom sheet (rises from the bottom-nav Menu button) ─── */
    body .am-left-sidebar {
        top: auto !important;
        bottom: 0 !important;
        left: 0 !important;
        right: 0 !important;
        width: 100% !important;
        height: auto !important;
        max-height: 82vh;
        max-height: 82dvh;   /* dvh tracks the visible viewport (iOS address bar), so a tappable backdrop strip always remains above the sheet */
        transform: translateY(105%) !important;
        transition: transform .3s cubic-bezier(.22, .9, .3, 1) !important;
        border-radius: 20px 20px 0 0;
        box-shadow: 0 -10px 30px rgba(0, 0, 0, .5);
        overflow: hidden;
        z-index: 1002;
    }
    body.open-left-sidebar .am-left-sidebar { transform: translateY(0) !important; }
    /* Grab handle (force over the theme's own ::before) */
    body .am-left-sidebar::before {
        content: "" !important;
        position: absolute !important;
        top: 8px !important;
        left: 50% !important;
        bottom: auto !important;
        transform: translateX(-50%) !important;
        width: 40px !important;
        height: 4px !important;
        border-radius: 999px !important;
        background: var(--oqlis-sidebar-muted) !important;
        opacity: .5 !important;
        z-index: 3 !important;
    }
    /* Bypass the nanoScroller (it collapses to 0 height in this layout): let
       the scroller be a normal scrolling box and the content flow naturally */
    body .am-left-sidebar .am-scroller.nano {
        height: auto !important;
        max-height: 82vh;
        max-height: 82dvh;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }
    /* Submenu (nested) scrollers must expand fully inline — the OUTER sheet
       scroller above does the scrolling. Otherwise the nested scroller keeps the
       82vh cap but the theme forces overflow:visible on it, so a long submenu
       (e.g. all dashboards) overflows its capped box and paints ON TOP of the
       categories below it — the reported overlap. */
    body .am-left-sidebar .sidebar-elements .am-scroller.nano {
        max-height: none !important;
        overflow: visible !important;
    }
    body .am-left-sidebar .nano-content {
        position: static !important;
        height: auto !important;
        overflow: visible !important;
        padding-top: 22px;   /* clear the grab handle */
    }
    body .am-left-sidebar .nano > .nano-pane { display: none !important; }

    /* The theme shifts page content right for a left drawer; the sheet overlays
       from the bottom instead, so keep content in place */
    body.open-left-sidebar .am-content,
    body.open-left-sidebar .am-wrapper { transform: none !important; }
    /* Dim backdrop; taps close (theme handles outside-tap) */
    body.open-left-sidebar::after {
        content: "";
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, .5);
        -webkit-backdrop-filter: blur(1px);
        backdrop-filter: blur(1px);
        z-index: 1001;
        animation: home-img-fade .2s ease both;
    }

    /* App-style sheet rows: tighter, rounded hover/active */
    body .am-left-sidebar .sidebar-elements > li > a {
        display: flex;
        align-items: center;
        gap: 14px;
        margin: 2px 10px;
        padding: 13px 14px;
        border-radius: 12px;
    }
    body .am-left-sidebar .sidebar-elements > li > a:hover,
    body .am-left-sidebar .sidebar-elements > li > a:focus {
        background: color-mix(in srgb, var(--main_color) 18%, transparent);
    }
    body .am-left-sidebar .sidebar-elements > li > a img,
    body .am-left-sidebar .sidebar-elements > li > a .icon { margin: 0; }

    /* Balance the top inset with the side gutter so content sits evenly framed
       (the app-name bar is gone; the floating avatar sits in the top-right
       corner). !important overrides pages that carry an inline padding-top
       (e.g. the dashboard's 4px) so the top matches the sides on every page. */
    body .am-content { padding-top: 0; }
    body .main-content,
    body .main-content.home-page,
    body .main-content.spaces-page { padding-top: var(--oqlis-page-gutter) !important; }
}

/* ── Mobile bottom navigation (thumb-zone) ────────────────────────────── */
.oqlis-bottomnav { display: none; }
@media (max-width: 767px) {
    .oqlis-bottomnav {
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 999;   /* below the drawer backdrop (1000) so it's dimmed when the drawer opens */
        /* The safe-area inset is ADDITIVE to the height (not just padding on a
           fixed 58px). With box-sizing:border-box a bare `height:58px` +
           `padding-bottom:inset` let the inset eat into the bar — icons crammed
           to the top with an empty background band below (very visible in Chrome,
           whose bottom inset can be large). Now the 58px icon row is preserved
           and the inset extends the bar down over the home indicator. min() caps
           a browser that over-reports the inset so the bar can't balloon. */
        height: calc(58px + min(env(safe-area-inset-bottom, 0px), 40px));
        padding-bottom: min(env(safe-area-inset-bottom, 0px), 40px);
        background: var(--oqlis-sidebar-bg);
        border-top: 1px solid var(--oqlis-sidebar-border);
        box-shadow: 0 -2px 10px rgba(0, 0, 0, .28);
        overflow: visible;   /* let the Account switcher's upward dropdown escape the bar */
    }
    .oqlis-bottomnav-item {
        flex: 1 1 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        min-width: 0;
        background: none;
        border: 0;
        padding: 8px 0;
        color: var(--oqlis-sidebar-muted);
        font-size: 10px;
        font-weight: 600;
        letter-spacing: .02em;
        text-decoration: none;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        transition: color .15s ease;
    }
    .oqlis-bottomnav-item i { font-size: 18px; line-height: 1; }
    /* Inline-SVG icons: match the 18px glyph height; width follows each icon's
       viewBox aspect (house wider, bars narrower), as the FA glyphs did. */
    .oqlis-bottomnav-item svg { height: 18px; width: auto; display: block; }
    .oqlis-bottomnav-item:hover,
    .oqlis-bottomnav-item:active { color: var(--oqlis-sidebar-text); text-decoration: none; }
    .oqlis-bottomnav-item.active { color: var(--main_color); }

    /* keep page content clear of the fixed bar */
    body .am-content { padding-bottom: calc(64px + min(env(safe-area-inset-bottom, 0px), 40px)); }
}

/* ── Mobile UX refinements (phones only) ──────────────────────────────── */
@media (max-width: 767px) {
    /* Lumin ask bar: the side-by-side select + input is far too cramped at
       phone width. Stack the dashboard scope on its own row above a
       full-width input + send row. */
    .home-page #chatInputBar .lumin-input-shell {
        flex-wrap: wrap;
        border-radius: 18px;
        padding: 8px;
        gap: 8px;
    }
    .home-page #chatInputBar .lumin-dashboard-select-wrap {
        flex: 1 1 100%;
        min-width: 100%;
        max-width: 100%;
    }
    .home-page #chatInputBar .dm-divider { display: none; }
    .home-page #chartChatInput {
        flex: 1 1 auto;
        font-size: 16px;          /* >=16px stops iOS zooming on focus */
        padding: 8px 6px;
    }
    .home-page #chartSendBtn { height: 40px; width: 40px; }

    /* 16px palette input too (no focus-zoom) */
    #oqlisCmdkInput { font-size: 16px; }

    /* Touch: swipe drives the carousels, so drop the small arrow targets */
    .home-carousel-nav { display: none !important; }

    /* Comfortable tap height for chips (~44px, per touch-target guidance) */
    .home-chip { padding: 13px 18px; }

    /* Slightly larger hero heading breathing room */
    .home-hero--compact { padding: 18px 18px 20px; }
}

/* ── Modern loading spinner ───────────────────────────────────────────────
   Minimal single-ring spinner in the accent colour, replacing the old
   Materialize multi-arc .preloader-wrapper in the filter panes. */
.oqlis-spinner-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 30px 0;
}
.oqlis-spinner {
    width: 28px;
    height: 28px;
    border: 3px solid var(--oqlis-border);
    border-top-color: var(--main_color);
    border-radius: 50%;
    animation: oqlis-spin .7s linear infinite;
}
@keyframes oqlis-spin { to { transform: rotate(360deg); } }

/* ── Modern modals ────────────────────────────────────────────────────────
   Bring Bootstrap modals in line with the redesign: rounded corners, a soft
   deep shadow + subtle border, clean header/footer dividers, a modern rounded
   icon close button, and tidy footer buttons. Chrome-level only — the legacy
   .colored-header .success/.danger header tints are left intact. !important is
   used where the theme's deep .modal.colored-header selectors would otherwise
   win. */
body .modal .modal-content {
    border-radius: 14px !important;
    border: 1px solid var(--oqlis-border-soft) !important;
    box-shadow: 0 24px 64px -24px rgba(0, 0, 0, .45), 0 6px 18px -12px rgba(0, 0, 0, .28) !important;
    background-color: var(--oqlis-surface);
}
body .modal .modal-content .modal-header {
    border-bottom: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 14px 14px 0 0;
    padding: 15px 18px !important;
}
body .modal .modal-content .modal-header .modal-title,
body .modal .modal-content .modal-header h3,
body .modal .modal-content .modal-header h4 {
    font-size: 17px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--oqlis-text);
}
/* Close → modern rounded icon button */
body .modal .modal-content .modal-header .close {
    opacity: 1 !important;
    margin: 0 !important;
    padding: 0 !important;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 9px;
    background: transparent;
    color: var(--oqlis-text-muted) !important;
    text-shadow: none !important;
    font-size: 20px;
    transition: color .15s ease, background-color .15s ease;
}
body .modal .modal-content .modal-header .close:hover,
body .modal .modal-content .modal-header .close:focus {
    background: var(--oqlis-surface-alt);
    color: var(--oqlis-text) !important;
}
body .modal .modal-content .modal-header .close .icon { font-size: 18px; }
body .modal .modal-content .modal-body { padding: 20px 18px; }
body .modal .modal-content .modal-footer {
    background: transparent !important;
    border-top: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 0 0 14px 14px;
    padding: 14px 18px !important;
}
body .modal .modal-content .modal-footer .btn { border-radius: 9px; }

/* ── Data-model access modals — prefilter input row alignment ──────────────
   The Column / Type / Value·s fields sit in a bootstrap col row; the add (+) /
   remove (−) buttons lived in a column top-pinned by an inline padding hack, so
   they floated above the inputs. Flex each prefilter row and align its columns
   to the field baseline, and drop the buttons' top-padding offset. Scoped to
   the #inside_div_* prefilter containers so the Role/User select row above is
   untouched. */
#inside_div_user_add .form-group.row > div,
#inside_div_user_add_edit .form-group.row > div,
#inside_div_role_add .form-group.row > div,
#inside_div_role_add_edit .form-group.row > div {
    display: flex;
    align-items: flex-end;
}
#inside_div_user_add .form-group.row > div > .col-sm-1,
#inside_div_user_add_edit .form-group.row > div > .col-sm-1,
#inside_div_role_add .form-group.row > div > .col-sm-1,
#inside_div_role_add_edit .form-group.row > div > .col-sm-1 {
    padding-top: 0 !important;
    display: flex;
    align-items: flex-end;
}

/* ── Mobile: modals go full-width + wide config tables scroll ──────────────
   Many modals carry an inline width:60%/90% on .modal-dialog that computes to a
   tiny sliver on a phone (e.g. 60% of 375 ≈ 225px), which then clips their
   content. Force full-width on mobile. The Edit Filters config table has 7
   columns that can't fit any phone width, so give it a horizontal scroll
   container instead of letting the "Add Filter" column clip off-screen. */
@media (max-width: 766px) {
    body .modal .modal-dialog,
    body .modal .modal-dialog.custom-width {
        width: auto !important;
        max-width: none !important;
        margin: 10px !important;
    }
    #FilterModal #dashboard_filters {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        padding-left: 0 !important;
        padding-right: 0 !important;
    }
    #FilterModal #dashboard_filters > .table { min-width: 520px; }
}

/* ── Mobile: keep chart action icons pinned to the top-right corner ────────
   The theme flips `.panel-heading .tools` to position:relative + float + a
   10px margin below 767px, so on the redesigned dashboard chart cards the
   hover icons (AI / expand / ⋮) drift ~18px down into the header flow instead
   of staying anchored to the corner. Restore the desktop absolute pinning,
   scoped to grid-stack chart cards. */
@media (max-width: 766px) {
    body .grid-stack .panel-heading .tools {
        position: absolute !important;
        top: 0 !important;
        right: 0 !important;
        float: none !important;
        margin: 0 !important;
    }
}

/* ── Lumin chat modals ─────────────────────────────────────────────────────
   Shared chrome for every Lumin assistant modal (explore, unify, page, and the
   dashboard / preview insight modals — all ids begin "mod-lumin-"): a roomy
   width and a clean rounded input row. The legacy trio (.lumin-legacy —
   explore, unify, page) additionally gets a full reskin of its old hardcoded
   grey speaker bubbles, #ddd chat border and near-black #131313 code ring. The
   newer dashboard / preview modals keep their own themed --oq-lumin rendering
   and are intentionally left out of the .lumin-legacy reskin. */

/* All Lumin modals: comfortable width + clean input row (roomy rounded field
   beside a matching pill button, no gap-with-squared-corners). !important beats
   the per-page inline #…-chat-input / #send-button corner-flattening. */
[id^="mod-lumin-"] .modal-dialog { width: 720px; max-width: calc(100vw - 32px); }
[id^="mod-lumin-"] .lumin-chat-input-row { padding-top: 12px; }
[id^="mod-lumin-"] .input-group { display: flex; gap: 10px; }
[id^="mod-lumin-"] .input-group > input {
    flex: 1 1 auto;
    height: 42px;
    padding: 0 14px;
    border: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 10px !important;
    background: var(--oqlis-surface);
    color: var(--oqlis-text);
}
[id^="mod-lumin-"] .input-group .btn {
    flex: 0 0 auto;
    height: 42px;
    margin-left: 0 !important;
    padding: 0 20px;
    border-radius: 10px !important;
}

/* Legacy trio reskin (explore / unify / page) */
.lumin-legacy #chat-container {
    height: 460px;
    overflow-y: auto;
    border: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 12px;
    background: var(--oqlis-surface-alt);
    padding: 14px;
}
/* Speaker bubbles: assistant left on the surface, user right in a soft accent
   tint, so the two are distinguishable. Assistant stays full width for code. */
.lumin-legacy #chat-container .user-message,
.lumin-legacy #chat-container .assistant-message {
    font-family: inherit;
    font-size: 13px;
    line-height: 1.5;
    padding: 10px 13px;
    margin-bottom: 10px;
    border-radius: 12px;
    color: var(--oqlis-text);
}
.lumin-legacy #chat-container .user-message {
    max-width: 85%;
    margin-left: auto;
    background: rgba(107, 195, 176, .14);
    background: color-mix(in srgb, var(--main_color) 16%, var(--oqlis-surface));
    border: 1px solid rgba(107, 195, 176, .30);
    border: 1px solid color-mix(in srgb, var(--main_color) 32%, transparent);
}
.lumin-legacy #chat-container .assistant-message {
    background: var(--oqlis-surface);
    border: 1px solid var(--oqlis-border-soft);
}
.lumin-legacy #chat-container .user-message strong,
.lumin-legacy #chat-container .assistant-message strong {
    font-weight: 600;
    color: var(--oqlis-text-muted);
}
.lumin-legacy #chat-container .response-label {
    font-weight: 600;
    color: var(--oqlis-text-muted);
    margin-bottom: 6px;
}
/* Returned code block: the theme gives .code-container a near-black #131313
   background with 10px padding, which shows as a thick black ring around the
   editor. Drop it so the (softened, rounded) CodeMirror frames the code itself. */
.lumin-legacy #chat-container .code-container {
    margin: 4px 0 0;
    padding: 0;
    background: transparent !important;
    border-radius: 10px;
}
.lumin-legacy #chat-container .CodeMirror {
    border: 1px solid var(--oqlis-border-soft) !important;
    border-radius: 10px;
    overflow: hidden;
}
.lumin-legacy #chat-container .CodeMirror-gutters {
    border-right: 1px solid var(--oqlis-border-soft) !important;
}
/* Copy icon was white for the old black container — invisible on the light
   bubble; use a muted token that darkens on hover. */
.lumin-legacy #chat-container .copy-icon {
    color: var(--oqlis-text-muted);
    transition: color .15s ease;
}
.lumin-legacy #chat-container .copy-icon:hover { color: var(--oqlis-text); }

/* CodeMirror background (light mode): the base theme gives the editor body a
   blue-tinted grey (--oqlis-input #f5f7fb) but leaves the gutter pure white, so
   the line-number gutter reads as an odd bright stripe against a tinted canvas —
   the reverse of a conventional editor. Flip it to the modern light-editor look:
   a clean white canvas with a subtle neutral gutter tray, both on the cool-slate
   palette. Dark mode keeps its darker inset editor untouched. */
body:not([data-theme="dark"]) .CodeMirror {
    background: var(--oqlis-surface);
}
body:not([data-theme="dark"]) .CodeMirror-gutters {
    background-color: var(--oqlis-surface-alt);
    border-right-color: var(--oqlis-border-soft);
}
