/* style.css */
/* Visual Design System for Customizable SCM Sales Dashboard */

:root {
    --bg-base: #080C14;
    --bg-surface: rgba(10, 15, 28, 0.55);
    --border-color: rgba(255, 255, 255, 0.07);
    /* Was 0.015 white; the extra weight replaces the hardcoded <tr> stripe
       removed from .widget-table. Both must stay literal: a var() inside a
       custom property is substituted against the element the DECLARATION sits
       on, so an accent-derived value here would resolve against :root's own
       palette for every theme (tests/test_theme_css_integrity.py enforces
       this). A theme that wants an accent-tinted hover overrides it on body. */
    --table-row-stripe: rgba(255, 255, 255, 0.04);
    --table-row-hover: rgba(255, 255, 255, 0.07);
    --glass-blur: 20px;

    /* Spacing scale (4px base) used by the widget source / visual builder panel */
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;

    /* Glass surfaces used by login card and parameter panels before a theme class is applied */
    --glass-bg: rgba(14, 21, 38, 0.70);
    --glass-border: rgba(255, 255, 255, 0.08);

    /* Radial Gradient Backgrounds */
    --radial-color-1: rgba(0, 217, 160, 0.10);
    /* Neon Green */
    --radial-color-2: rgba(59, 130, 246, 0.10);
    /* Blue */
    --radial-color-3: rgba(139, 92, 246, 0.08);
    /* Purple */

    /* Chart.js Config Variables */
    --chart-grid: rgba(255, 255, 255, 0.05);
    --chart-label: #94A3B8;
    --chart-tooltip-bg: #080C14;
    --chart-tooltip-title: #F8FAFC;
    --chart-tooltip-body: #E2E8F0;
    --chart-tooltip-border: rgba(255, 255, 255, 0.18);
    --chart-heatmap-low: rgba(15, 23, 42, 0.9);

    --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;

    /* Operational type roles: metadata, controls, body, and card titles. */
    --type-meta: 0.8125rem;
    --type-compact: 0.875rem;
    --type-body: 0.9375rem;
    --type-title: 1.125rem;
    --type-hero: clamp(1.5rem, 2.5vw, 1.75rem);

    /* SCM Pro Accent Palette */
    --accent-primary: #00D9A0;
    /* Neon Teal-Green */
    --accent-primary-rgb: 0, 217, 160;
    --accent-indigo: #8B5CF6;
    /* Vivid Purple */
    --accent-indigo-rgb: 139, 92, 246;
    --accent-emerald: #10B981;
    /* Emerald 500 */
    --accent-amber: #F59E0B;
    --accent-amber-rgb: 245, 158, 11;
    /* Amber/Gold */
    --accent-red: #F43F5E;
    --accent-red-rgb: 244, 63, 94;
    /* Rose 500 */
    /* Table highlight-rule tones. Four reuse the accent colors above (so a
       theme that retints those retints the highlights too); the rest are
       defined here and overridden in the light-theme blocks below, where the
       500-weight versions would not have enough contrast on white. */
    --rule-orange: #FB923C;
    --rule-teal: #2DD4BF;
    --rule-pink: #F472B6;
    --rule-slate: #94A3B8;
    --accent-blue: #3B82F6;
    /* Chart Blue */

    /* Muted Slate Text */
    --text-main: #F0F4FF;
    /* Near-white */
    --text-primary: #F0F4FF;
    /* Alias — same as text-main */
    --text-secondary: #A8B4CC;
    /* Mid between main and muted */
    --text-muted: #8A9BC4;
    /* Slate-blue muted */
    --text-dark: #3D4F70;
    /* Slate 600 */

    --transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
    --card-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
}

/* ---------------------------------------------------------
   GLOBAL & LAYOUT SETUP
   --------------------------------------------------------- */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    height: 100%;
}

body {
    font-family: var(--font-primary);
    -webkit-font-smoothing: antialiased;
    background: var(--bg-base);
    background-image:
        radial-gradient(ellipse 60% 50% at 10% 85%, var(--radial-color-1) 0%, transparent 70%),
        radial-gradient(ellipse 50% 50% at 88% 12%, var(--radial-color-2) 0%, transparent 70%),
        radial-gradient(ellipse 55% 60% at 50% 50%, var(--radial-color-3) 0%, transparent 80%);
    color: var(--text-main);
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    padding: 8px 24px 24px;
    overflow-x: hidden;
}

body.locale-ar {
    --font-primary: "IBM Plex Sans Arabic", Tahoma, Arial, sans-serif;
}

/* Table direction describes data content, never the page chrome. */
.widget-table {
    direction: ltr;
}

/* Canvas inherits html[dir=rtl] in Arabic, which flips ctx.direction and makes
   chartjs-plugin-datalabels draw every value label shifted left by its own width
   (Chart.js handles its own text direction internally; the plugin does not). */
.widget-card canvas {
    direction: ltr;
}

.sql-editor,
.param-sql-textarea,
.migration-log-console,
.log-block {
    direction: ltr;
    unicode-bidi: isolate;
}

/* .app-shell (topbar + glass-app column) fills the remaining viewport height
   below body's padding, so the app frame stretches edge-to-edge instead of
   leaving a dead gap at the bottom of the page. */
.app-shell {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
}

.glass-app {
    width: 100%;
    max-width: 100%;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 16px;
    display: flex;
    flex-direction: row;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

/* ---------------------------------------------------------
   SIDEBAR & NAVIGATION
   --------------------------------------------------------- */
.app-sidebar {
    width: var(--dashboard-sidebar-width, 280px);
    flex-shrink: 0;
    border-inline-end: 1px solid var(--border-color);
    background-color: var(--chrome-tint-soft);
    /* _applySidebarGroupTint() composes its group colour ON TOP of this by
       re-declaring background-image as "<tint>, var(--sidebar-gradient)". */
    background-image: var(--sidebar-gradient);
    padding: 25px;
    display: flex;
    flex-direction: column;
    gap: 30px;
    /* Animate width only; padding snaps. contain: layout scopes the
       layout invalidation so the main column isn't re-laid-out every
       frame. will-change promotes the sidebar to its own layer. */
    transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1), background-image 0.25s ease;
    contain: layout paint;
    will-change: width;
    overflow: hidden;
}

.sidebar-resize-handle {
    flex: 0 0 6px;
    cursor: col-resize;
    touch-action: none;
}

.sidebar-resize-handle:hover,
.sidebar-resize-handle:focus-visible {
    background: var(--accent-primary);
    outline: none;
}

.app-sidebar.is-resizing {
    transition: none;
}

.app-sidebar.collapsed+.sidebar-resize-handle {
    display: none;
}

@media (max-width: 768px) {
    .sidebar-resize-handle {
        display: none;
    }
}

.app-sidebar.collapsed {
    width: 62px;
    padding: 25px 12px;
}

.app-sidebar.collapsed .sidebar-section-header,
.app-sidebar.collapsed .nav-btn-label {
    display: none;
}

.app-sidebar.collapsed .nav-btn {
    justify-content: center;
    padding: 10px;
    width: 100%;
    text-align: center;
}

.app-sidebar.collapsed .app-nav {
    align-items: center;
}

.app-sidebar.collapsed #btn-new-dashboard {
    padding: 8px;
    min-width: unset;
    width: 100%;
}

.app-sidebar.collapsed .user-profile-info {
    display: none;
}

.app-sidebar.collapsed .user-profile-badge {
    justify-content: center;
    padding: 6px;
}

.sidebar-toggle-btn {
    margin-inline-start: auto;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 4px;
    font-size: var(--type-compact);
    transition: color 0.2s, background 0.2s;
    flex-shrink: 0;
}

.sidebar-toggle-btn:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.06);
}

/* Rotated to point the other way while the sidebar is collapsed. The button
   now lives in .system-topbar (moved alongside the brand block), not inside
   #app-sidebar, so this can't be a ".app-sidebar.collapsed ..." descendant
   selector -- initSidebarToggle() (themes.js) toggles this class on the
   button itself in lockstep with #app-sidebar's own "collapsed" class. */
.sidebar-toggle-btn.collapsed i {
    transform: rotate(180deg);
}

.brand {
    display: flex;
    align-items: center;
    gap: 12px;
}

.logo-icon {
    font-size: 24px;
    color: var(--accent-primary);
    filter: drop-shadow(0 0 8px rgba(var(--accent-primary-rgb), 0.5));
}

.brand-text h1 {
    font-family: var(--font-primary);
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -0.5px;
    line-height: 1.1;
}

.sub-brand {
    font-size: var(--type-meta);
    color: var(--text-muted);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 1px;
}

.sidebar-section {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.sidebar-section-header {
    font-size: var(--type-meta);
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: var(--text-muted);
    font-weight: 600;
    margin-bottom: 5px;
}

.app-nav {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.nav-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    padding: 10px 18px;
    border-radius: 10px;
    font-size: var(--type-compact);
    font-weight: 500;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: var(--transition-smooth);
}

.nav-btn:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.12);
}

.nav-btn.active {
    color: var(--text-main);
    background: rgba(var(--accent-primary-rgb), 0.12);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.30);
    box-shadow: 0 0 18px -4px rgba(var(--accent-primary-rgb), 0.45);
}

.app-main-wrapper {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    max-height: 100%;
    overflow-y: auto;
}

/* App shell: stacks the outside top bar above the framed app */
.app-shell {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* System controls bar OUTSIDE the app frame (in the page background) */
.system-topbar {
    display: flex;
    justify-content: flex-start;
    align-items: center;
    gap: 12px;
    padding: 0 6px;
    background: transparent;
    border: none;
}

/* Workspace controls stay together so navigation wraps as a unit, rather than
   competing with account utilities for the last available inline space. */
.system-topbar-workspace {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 1 1 24rem;
    min-width: 0;
}

/* Dashboard group pills (see renderGroupPills() in dashboards.js) are hidden
   entirely via the "hidden" class until at least one group exists. */
.topbar-group-pills {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.group-pill {
    background: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    padding: 10px 20px;
    border-radius: 9999px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.group-pill:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.06);
}

.group-pill.active {
    color: var(--bg-base);
    background: var(--accent-primary);
    border-color: var(--accent-primary);
}

/* Account utilities stay on the far edge, separate from workspace navigation. */
.system-topbar-user {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-inline-start: auto;
}

.system-topbar-user .user-profile-badge {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 0;
    padding-inline-start: 4px;
    padding-inline-end: 12px;
    padding-top: 4px;
    padding-bottom: 4px;
}

.system-topbar-user .user-profile-avatar {
    width: 30px;
    height: 30px;
    font-size: var(--type-compact);
}

.system-topbar-user #btn-logout {
    width: auto;
    margin-top: 0;
    white-space: nowrap;
}

.system-topbar-account {
    position: relative;
}

.system-topbar-account>summary {
    list-style: none;
}

.system-topbar-account>summary::-webkit-details-marker {
    display: none;
}

.system-topbar-account-menu {
    position: absolute;
    z-index: 20;
    inset-block-start: calc(100% + var(--space-2));
    inset-inline-end: 0;
    display: grid;
    gap: var(--space-2);
    min-inline-size: 14rem;
    padding: var(--space-2);
    /* var(--bg-card) was never defined in any theme, so this menu rendered
       with NO background (fully transparent -- the text was unreadable).
       Stack the translucent elevated surface over the opaque --bg-base so the
       composited result is always opaque and legible on both dark and light
       themes. */
    background-color: var(--bg-base);
    background-image: linear-gradient(var(--bg-elevated, var(--bg-surface)), var(--bg-elevated, var(--bg-surface)));
    border: 1px solid var(--border-color);
    border-radius: 8px;
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.18);
}

.system-topbar-account:not([open]) .system-topbar-account-menu {
    display: none;
}

.system-topbar-account-menu .lang-switch,
.system-topbar-account-menu .btn {
    width: 100%;
}

/* ---------------------------------------------------------
   APP CONTENT PANELS
   --------------------------------------------------------- */
.app-content {
    padding: 30px;
    flex-grow: 1;
    min-height: 70vh;
}

.tab-panel {
    display: none;
}

.tab-panel.active {
    display: block;
    animation: fadeIn 0.4s ease-out;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(5px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.tab-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
}

.title-block h2 {
    font-family: var(--font-primary);
    font-size: 24px;
    font-weight: 600;
    color: var(--text-main);
}

.title-block p {
    font-size: var(--type-compact);
    color: var(--text-muted);
}

/* ---------------------------------------------------------
   WIDGETS GRID & CARDS
   --------------------------------------------------------- */
/* align-items: start -- without this, CSS Grid's default (stretch) makes
   every card in an auto-placed row match the TALLEST card sharing that row,
   silently overriding each widget's own .h-1/.h-2/.h-3 (min-height 200/420/
   640px). A short widget next to a tall one balloons up to the tall one's
   height -- and since .widget-skeleton is height:100% of .widget-body, its
   loading skeleton visibly stretches to match too, looking like the
   skeleton ignores the widget's configured size when really the whole card
   does. align-items: start makes each card size to its own content/
   min-height and sit at the top of its grid cell instead. Confirmed via a
   Playwright harness: a w-2/h-1 next to a w-2/h-3 rendered at 640px (matching
   the h-3 neighbor) before this change, 356px (matching its own isolated
   h-1 height) after. */
.widgets-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
    align-items: start;
}

.widget-card {
    background: rgba(12, 18, 34, 0.70);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    padding: 20px;
    display: flex;
    flex-direction: column;
    min-height: 150px;
    transition: var(--transition-smooth);
    position: relative;
    overflow: hidden;
}

.widget-card:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.35) !important;
    background: rgba(12, 18, 34, 0.80);
    transform: translateY(-2px);
    box-shadow: 0 4px 10px rgba(var(--accent-primary-rgb), 0.12);
}

/* Width spans */
.w-1 {
    grid-column: span 1;
}

.w-2 {
    grid-column: span 2;
}

.w-3 {
    grid-column: span 3;
}

.w-4 {
    grid-column: span 4;
}

/* Height spans */
.h-1 {
    min-height: 200px;
}

.h-2 {
    min-height: 420px;
}

.h-3 {
    min-height: 640px;
}

/* Canvas adjustment depending on widget height */
.widget-card.h-1 canvas {
    max-height: 140px !important;
}

.widget-card.h-2 canvas {
    max-height: 360px !important;
}

.widget-card.h-3 canvas {
    max-height: 580px !important;
}

/* Table max-height scaling depending on widget height */
.widget-card.h-1 .widget-table-container {
    max-height: 130px;
}

.widget-card.h-2 .widget-table-container {
    max-height: 350px;
}

.widget-card.h-3 .widget-table-container {
    max-height: 570px;
}

/* Responsive adjustments */
@media (max-width: 1024px) {
    .widgets-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .w-3,
    .w-4 {
        grid-column: span 2;
    }
}

@media (max-width: 768px) {
    .widgets-grid {
        grid-template-columns: 1fr;
    }

    .w-1,
    .w-2,
    .w-3,
    .w-4 {
        grid-column: span 1;
    }
}

/* Widget Header */
.widget-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 16px;
    gap: 8px;
}

.widget-drag-handle {
    color: var(--text-muted);
    font-size: 12px;
    cursor: grab;
    padding: 2px 4px;
    border-radius: 4px;
    opacity: 0;
    transition: opacity 0.2s, color 0.2s;
    flex-shrink: 0;
    margin-top: 1px;
}

.widget-card:hover .widget-drag-handle {
    opacity: 1;
}

.widget-drag-handle:hover {
    color: var(--text-main);
}

.widget-card.drag-source {
    opacity: 0.35;
    cursor: grabbing;
    box-shadow: 0 0 0 2px var(--accent-primary), 0 12px 40px -8px rgba(0, 0, 0, 0.5);
    transform: scale(0.98);
}

.widget-card.drag-over {
    outline: 2px dashed var(--accent-primary);
    outline-offset: 3px;
    background: rgba(var(--accent-primary-rgb), 0.06);
    transform: scale(1.01);
    transition: transform 0.1s ease, background 0.1s ease;
}

.widget-card[draggable="true"]:not(.drag-source) {
    transition: transform 0.15s ease, box-shadow 0.15s ease, outline 0.1s ease;
}

.widget-header h3 {
    font-size: var(--type-title);
    font-weight: 700;
    color: var(--text-main);
    text-transform: uppercase;
    letter-spacing: 0;
}

html:lang(en) .widget-header h3 {
    letter-spacing: 0.5px;
}

.widget-actions {
    display: flex;
    gap: 4px;
    opacity: 0.75;
    transition: var(--transition-smooth);
}

.widget-card:hover .widget-actions,
.widget-card:focus-within .widget-actions {
    opacity: 1;
}

@media (hover: none) {
    .widget-actions {
        opacity: 1;
    }
}

.widget-action-btn {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: var(--type-compact);
    width: 24px;
    height: 24px;
    border-radius: 4px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: var(--transition-smooth);
}

.widget-action-btn:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.05);
}

/* Widget Content Types */
.widget-body {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Table widgets top-align instead of vertically centering -- a short table
   in a tall card (h-2/h-3) would otherwise float in the middle with a big
   empty gap between the widget title and the table header. */
.widget-body.widget-body-table {
    justify-content: flex-start;
}

/* KPI Card Style */
.kpi-value-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    text-align: center;
}

.kpi-value {
    font-family: var(--font-primary);
    font-size: 38px;
    font-weight: 800;
    color: var(--accent-primary);
    line-height: 1.05;
    margin-bottom: 4px;
}

/* KPI Spotlight Card Style -- headline entity + big value + metric list
   (e.g. a "Top Performer" OEE card). Deliberately theme-neutral (no glow,
   no per-theme overrides like .kpi-value has) to keep this simple. */
.kpi-spotlight {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    padding: 2px;
}

.kpi-spotlight-name {
    font-family: var(--font-primary);
    font-size: 20px;
    font-weight: 700;
    color: var(--text-main);
    line-height: 1.2;
    margin-bottom: 4px;
}

.kpi-spotlight-value {
    font-family: var(--font-primary);
    font-size: 38px;
    font-weight: 800;
    color: var(--accent-primary);
    line-height: 1.05;
    margin-bottom: 4px;
}

.kpi-spotlight-subtitle {
    font-size: 12px;
    color: var(--text-secondary);
    margin-bottom: 14px;
}

.kpi-spotlight-metrics {
    display: flex;
    flex-direction: column;
    gap: 8px;
    border-top: 1px solid var(--border-color);
    padding-top: 10px;
    margin-top: auto;
}

.kpi-spotlight-metric-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 13px;
}

.kpi-spotlight-metric-label {
    color: var(--text-secondary);
}

.kpi-spotlight-metric-value {
    color: var(--text-main);
    font-weight: 700;
}

/* Table Style in Widget */
.widget-table-container {
    overflow: auto;
    max-height: 300px;
    border: 1px solid var(--border-color);
    border-radius: 10px;
}

.table-tools,
.table-pager {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-block: 6px;
}

.table-tools {
    justify-content: space-between;
}

.table-search {
    min-width: 0;
    width: min(260px, 100%);
    padding: 7px 10px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    background: var(--bg-input, var(--bg-surface));
    color: var(--text-main);
    font: inherit;
}

.table-search::placeholder {
    color: var(--text-secondary);
}

.table-pager {
    justify-content: flex-end;
    color: var(--text-secondary);
    font-size: 11px;
}

.table-page-status {
    margin-inline: auto 0;
}

.widget-table th[role="button"] {
    cursor: pointer;
    user-select: none;
}

.widget-table th .th-content {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    max-width: 100%;
    vertical-align: middle;
}

.widget-table th .th-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.widget-table th .sort-icon {
    flex-shrink: 0;
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1;
}

.widget-table th .sort-icon::after {
    content: "↕";
}

.widget-table th[aria-sort="ascending"] .sort-icon::after {
    content: "↑";
}

.widget-table th[aria-sort="descending"] .sort-icon::after {
    content: "↓";
}

.widget-table th[aria-sort="ascending"] .sort-icon,
.widget-table th[aria-sort="descending"] .sort-icon {
    color: var(--accent-primary);
}

/* Zebra striping and row hover are driven by --table-row-stripe /
   --table-row-hover on the <td> (see further down), NOT here. There used to be
   a second, hardcoded `rgba(255, 255, 255, 0.025)` stripe on the <tr> as well:
   white on white, so on a light theme the table had no visible banding at all,
   and on the dark themes it silently doubled up with the variable one. */
.widget-table tbody tr {
    cursor: pointer;
}

/* The pinned/sticky column is always the logical FIRST column -- but for
   RTL tables, renderTableWidget() physically reverses the column order in
   the DOM (see the .widget-table.rtl comment above) so the table can stay
   a normal left-to-right box model while reading right-to-left. That means
   the logical first column ends up as the DOM's *last* child, displayed on
   the right, so it's :last-child + right:0 that needs to be pinned for
   RTL, not :first-child + left:0 -- otherwise the wrong (logically last)
   column gets stuck to the wrong side. */
.widget-table:not(.rtl) th:first-child,
.widget-table:not(.rtl) td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
}

.widget-table:not(.rtl) td:first-child {
    background: var(--bg-elevated, var(--bg-surface));
}

.widget-table:not(.rtl) th:first-child {
    z-index: 3;
    background: var(--bg-surface);
}

.widget-table.rtl th:last-child,
.widget-table.rtl td:last-child {
    position: sticky;
    right: 0;
    z-index: 1;
}

.widget-table.rtl td:last-child {
    background: var(--bg-elevated, var(--bg-surface));
}

.widget-table.rtl th:last-child {
    z-index: 3;
    background: var(--bg-surface);
}

/* Grouping band: a second header row above the column names, where
   consecutive columns sharing a group merge into one spanning cell. Both
   header rows are sticky at the top of the scroll container, so the
   column-name row is offset by the band's measured height (--thead-group-h,
   set in renderTableWidget) instead of stacking on top of it. */
.widget-table thead tr.table-group-header th {
    top: 0;
    font-size: 12px;
    letter-spacing: 0.08em;
    padding: 9px 14px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-secondary);
}

/* A spanning group cell must not also pin itself to the left/right edge --
   the first-column sticky rule above would otherwise freeze a cell that
   covers several columns. A single-column group keeps the pin. */
.widget-table thead tr.table-group-header th[colspan]:not([colspan="1"]) {
    left: auto;
    right: auto;
    z-index: 2;
}

.widget-table thead tr.table-group-header th.table-group-empty {
    border-bottom-color: transparent;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.widget-table thead tr:not(.table-group-header) th {
    top: var(--thead-group-h, 0px);
}

/* Spacer column: a deliberately blank narrow column used to separate groups.
   No data, no sort, no export -- purely visual breathing room. */
col.table-spacer-col {
    width: 16px;
}

.widget-table th.table-spacer-col,
.widget-table td.table-spacer-col {
    padding: 0;
    min-width: 16px;
    max-width: 16px;
    background-image: none;
    cursor: default;
}

.widget-table td.table-data-bar {
    /* No pseudo-element is used for the bar (it's a plain background-image
       gradient on the cell), so `position: relative` isn't needed here --
       and it was actively harmful: it has the same specificity as, and is
       declared after, `.widget-table td:first-child`'s `position: sticky`,
       so enabling Bar on the first (pinned) column silently broke that
       column's horizontal-scroll stickiness. */
    background-image: linear-gradient(90deg, rgba(var(--accent-primary-rgb), 0.18) var(--table-bar-size), transparent var(--table-bar-size));
}

/* `tbody tr` is load-bearing, not decoration: the zebra rule further down
   (.widget-table tbody tr:nth-child(even) td) is both more specific than a
   plain `.widget-table td.table-rule-*` AND later in the file, so without it
   every highlighted cell on an even row lost its tint to the stripe. */
.widget-table tbody tr td.table-rule-red {
    color: var(--accent-red);
    background-color: rgba(244, 63, 94, 0.1);
}

.widget-table tbody tr td.table-rule-orange {
    color: var(--rule-orange);
    background-color: rgba(249, 115, 22, 0.1);
}

.widget-table tbody tr td.table-rule-amber {
    color: var(--accent-amber);
    background-color: rgba(245, 158, 11, 0.1);
}

.widget-table tbody tr td.table-rule-green {
    color: var(--accent-emerald);
    background-color: rgba(16, 185, 129, 0.1);
}

.widget-table tbody tr td.table-rule-teal {
    color: var(--rule-teal);
    background-color: rgba(20, 184, 166, 0.1);
}

.widget-table tbody tr td.table-rule-blue {
    color: var(--accent-blue);
    background-color: rgba(59, 130, 246, 0.1);
}

.widget-table tbody tr td.table-rule-purple {
    color: var(--accent-indigo);
    background-color: rgba(139, 92, 246, 0.1);
}

.widget-table tbody tr td.table-rule-pink {
    color: var(--rule-pink);
    background-color: rgba(236, 72, 153, 0.1);
}

.widget-table tbody tr td.table-rule-slate {
    color: var(--rule-slate);
    background-color: rgba(148, 163, 184, 0.12);
}

/* Sign arrow beside a cell's number, drawn only for columns whose config
   sets trend_arrow (see renderTableWidget). Three things here are load-
   bearing rather than taste:

   It is drawn in CSS, not with a Font Awesome glyph. fa-caret's ink is
   only ~0.37em tall, so any real size meant an absurd font-size -- and CSS
   renders even where the icon CDN is blocked, which happens on customer
   networks. The shape is a clip-path, not the classic transparent-border
   triangle, because a border triangle can only ever be one flat colour and
   the bevel here is a gradient plus a cast shadow.

   The element is `height: 0` and the shape is drawn in an absolutely
   positioned ::before, so a triangle far taller than the 12px cell text
   still cannot grow the row. `position: relative` sits on the arrow and
   never on the td -- a relative td silently breaks the sticky first column
   (the same trap documented on .widget-table td.table-data-bar above).

   The colour is on the arrow, not the cell, so a highlight rule's own
   `color` still owns the number. */
.widget-table td .table-trend-arrow {
    /* Height of the triangle -- the one knob. Width and the bevel both
       derive from it. ~45% of the 36px row. */
    --table-trend-arrow-h: 16px;
    position: relative;
    display: inline-block;
    width: calc(var(--table-trend-arrow-h) * 0.92);
    height: 0;
    margin-inline-start: 7px;
    vertical-align: middle;
}

.widget-table td .table-trend-arrow::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 100%;
    height: var(--table-trend-arrow-h);
    transform: translateY(-50%);
    /* Cast shadow, which is what separates the arrow from the cell fill.
       drop-shadow, not box-shadow: box-shadow traces the element's box,
       which is still a rectangle behind the clip. */
    filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.35));
}

/* Both gradients run light at the top to dark at the bottom, so the up and
   down arrows read as lit from the same direction. Shades are mixed from
   the theme's own accent, so every theme bevels its own colour. */
.widget-table td .table-trend-arrow.up::before {
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
    background: linear-gradient(155deg,
        color-mix(in srgb, var(--accent-emerald) 45%, white) 0%,
        var(--accent-emerald) 48%,
        color-mix(in srgb, var(--accent-emerald) 62%, black) 100%);
}

.widget-table td .table-trend-arrow.down::before {
    clip-path: polygon(0 0, 100% 0, 50% 100%);
    background: linear-gradient(155deg,
        color-mix(in srgb, var(--accent-red) 45%, white) 0%,
        var(--accent-red) 48%,
        color-mix(in srgb, var(--accent-red) 62%, black) 100%);
}

/* Totals row, styled as the header's mirror image: same weight and size, same
   2px accent rule (on top instead of bottom), and sticky to the bottom edge so
   the totals stay on screen while the body scrolls -- the container is
   max-height: 300px with overflow: auto, so on any real table they used to
   scroll out of view. The blur + --bg-surface pairing is copied from
   .widget-table th rather than an opaque colour: --bg-surface is translucent in
   every theme, so a sticky row needs the same backdrop treatment the sticky
   header already uses or the scrolling rows show through it. */
.widget-table tfoot td {
    position: sticky;
    bottom: 0;
    z-index: 1;
    /* The tint is a background-IMAGE layered over the theme's own surface
       colour, not a background-color -- one property can't hold both, and
       doing it this way means the totals band picks up every theme's accent
       with no per-theme rule (same trick as the sidebar group tint). */
    background-color: var(--bg-surface);
    background-image: linear-gradient(rgba(var(--accent-primary-rgb), 0.16), rgba(var(--accent-primary-rgb), 0.16));
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-top: 2px solid rgba(var(--accent-primary-rgb), 0.75);
    color: var(--text-main);
    font-size: 13px;
    font-weight: 700;
    padding: 13px 14px;
    font-variant-numeric: tabular-nums;
}

/* The frozen first column is sticky on both axes in the totals row, so it has
   to outrank the rest of the row it sits in. It also has to repaint the tint:
   `.widget-table:not(.rtl) td:first-child` (0,3,1) outranks
   `.widget-table tfoot td` (0,1,2) on the pinned cell, so without this the
   totals band would stop short of its own first column. */
.widget-table:not(.rtl) tfoot td:first-child,
.widget-table.rtl tfoot td:last-child {
    z-index: 2;
    background-color: var(--bg-elevated, var(--bg-surface));
    background-image: linear-gradient(rgba(var(--accent-primary-rgb), 0.16), rgba(var(--accent-primary-rgb), 0.16));
}

/* Only the label-ONLY cell. renderTableWidget gives the cell that merges the
   label with a value (`Total - 1,234`) the .numeric-cell class instead, and
   greying that would grey a real total -- the first column is not always the
   label column. */
.widget-table .table-total-label {
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.widget-table-column-flags,
.widget-table-column-move,
.widget-table-column-rule {
    display: flex;
    align-items: center;
    gap: 6px;
}

.widget-table-column-flags label {
    color: var(--text-secondary);
    font-size: 11px;
    white-space: nowrap;
}

.widget-table-column-rule input {
    min-width: 90px;
}

@media (max-width: 768px) {

    .table-tools,
    .table-pager {
        flex-wrap: wrap;
    }

    .table-search {
        width: 100%;
    }
}

.widget-table-container::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

.widget-table-container::-webkit-scrollbar-track {
    background: transparent;
}

.widget-table-container::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.12);
    border-radius: 4px;
}

.widget-table-container::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.22);
}

/* The thumb above is a white tint,
which vanishes on a light background --
   same issue .btn-secondary and .sub-tab-btn already carry overrides for. */
body.theme-daylight .widget-table-container::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.18);
}

body.theme-daylight .widget-table-container::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.30);
}

.widget-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
    text-align: left;
    table-layout: auto;
}

.widget-table.rtl {
    /* Column order is physically reversed in JS (see renderTableWidget) --
       the table itself stays a normal left-to-right box model, only text
       alignment flips here, to avoid a browser table-layout bug where
       direction: rtl combined with a dynamic <colgroup> and a sticky
       <thead> collapses column widths and drops borders/backgrounds. */
    text-align: right;
}

.widget-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    color: var(--text-main);
    font-weight: 700;
    font-size: 14px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 15px 14px;
    line-height: 1.3;
    background: var(--bg-surface);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    /* 2px accented rule so the header reads as a distinct band rather than
       just another row -- body rows use a 1px --border-color line. */
    border-bottom: 2px solid rgba(var(--accent-primary-rgb), 0.45);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 320px;
    user-select: none;
    /* Header text is always centered regardless of LTR/RTL data alignment --
       more specific than .widget-table/.widget-table.rtl's left/right rule,
       so it applies to header cells only. */
    text-align: center;
}

.widget-table th span {
    pointer-events: none;
}

.widget-table th .col-resize-handle {
    position: absolute;
    top: 0;
    right: -3px;
    width: 7px;
    height: 100%;
    cursor: col-resize;
    z-index: 2;
    border-radius: 3px;
}

.widget-table th .col-resize-handle:hover,
.widget-table th .col-resize-handle.resizing {
    background: rgba(var(--accent-primary-rgb), 0.5);
}

.widget-table td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 320px;
}

.widget-table td.numeric-cell {
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.widget-table tbody tr:nth-child(even) td {
    background: var(--table-row-stripe);
}

.widget-table tbody tr:hover td {
    background: var(--table-row-hover);
}

/* The pinned first column needs an OPAQUE background (rows must not show
   through it during horizontal scroll), which also painted over the stripe and
   left column 1 as the only unbanded one. Layering the translucent stripe as a
   background-image over that opaque colour keeps both. */
.widget-table:not(.rtl) tbody tr:nth-child(even) td:first-child,
.widget-table.rtl tbody tr:nth-child(even) td:last-child {
    background-image: linear-gradient(var(--table-row-stripe), var(--table-row-stripe));
}

.widget-table:not(.rtl) tbody tr:hover td:first-child,
.widget-table.rtl tbody tr:hover td:last-child {
    background-image: linear-gradient(var(--table-row-hover), var(--table-row-hover));
}

.widget-table tbody tr:last-child td {
    border-bottom: none;
}

/* Vertical column separators. The table only ever drew horizontal rules, so a
   wide numeric table read as one undifferentiated block. Declared after the
   sticky-column rules above so the pinned column's stronger edge wins.
   .rtl reverses column order in JS but keeps an LTR box model (see the
   .widget-table.rtl comment), so the pinned column sits on the right there and
   needs its divider on the opposite side. */
.widget-table th:not(:last-child),
.widget-table td:not(:last-child) {
    border-inline-end: 1px solid var(--border-color);
}

.widget-table:not(.rtl) th:first-child,
.widget-table:not(.rtl) td:first-child {
    border-inline-end: 2px solid rgba(var(--accent-primary-rgb), 0.30);
}

.widget-table.rtl th:last-child,
.widget-table.rtl td:last-child {
    border-inline-start: 2px solid rgba(var(--accent-primary-rgb), 0.30);
}

/* ---------------------------------------------------------
   BUTTONS & INPUT CONTROLS
   --------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 16px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition-smooth);
    border: 1px solid transparent;
}

.btn-primary {
    background: var(--accent-primary);
    color: var(--bg-base);
    font-weight: 600;
}

.btn-primary:hover {
    filter: brightness(1.12);
    box-shadow: 0 0 20px -3px rgba(var(--accent-primary-rgb), 0.55);
}

.btn-secondary {
    background: rgba(255, 255, 255, 0.05);
    border-color: var(--border-color);
    color: var(--text-main);
}

.btn-secondary:hover {
    background: rgba(255, 255, 255, 0.08);
}

.btn:disabled,
.btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    filter: none;
    box-shadow: none;
    transform: none;
}

.btn-danger-icon {
    background: transparent;
    color: var(--text-muted);
    font-size: 14px;
    padding: 10px;
}

.btn-danger-icon:hover {
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
}

.btn-large {
    padding: 14px 24px;
    font-size: 14px;
    border-radius: 10px;
}

/* Select and Form Inputs */
.custom-select,
input[type="text"],
input[type="password"],
input[type="number"],
input[type="url"],
textarea {
    background: rgba(15, 23, 42, 0.6);
    border: 1px solid var(--border-color);
    color: var(--text-main);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 13px;
    outline: none;
    transition: var(--transition-smooth);
    width: 100%;
}

.custom-select:focus,
input:focus,
textarea:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 10px -3px rgba(var(--accent-primary-rgb), 0.4);
}

.custom-select {
    cursor: pointer;
}

/* ---------------------------------------------------------
   SYNC TAB OPERATION CARDS
   --------------------------------------------------------- */
.sync-panel-layout {
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 30px;
}

@media (max-width: 1024px) {
    .sync-panel-layout {
        grid-template-columns: 1fr;
    }
}

.sync-card {
    background: rgba(12, 18, 34, 0.65);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    padding: 24px;
    display: flex;
    flex-direction: column;
}

.section-desc {
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 20px;
}

.sync-stats {
    margin: 20px 0 30px 0;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.stat-item {
    display: flex;
    justify-content: space-between;
    border-bottom: 1px solid rgba(255, 255, 255, 0.03);
    padding-bottom: 10px;
}

.stat-label {
    font-size: 12px;
    color: var(--text-muted);
}

.stat-value {
    font-size: 13px;
    font-weight: 600;
}

/* Code log displays */
.logs-container {
    display: flex;
    flex-direction: column;
    gap: 16px;
    flex-grow: 1;
}

.log-block h4 {
    font-size: 11px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    margin-bottom: 6px;
}

.log-block pre {
    background: rgba(15, 23, 42, 0.9);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 12px;
    font-family: var(--font-mono);
    font-size: 11px;
    color: #38BDF8;
    /* Sky 400 */
    overflow-wrap: break-word;
    overflow-x: auto;
    max-height: 150px;
    white-space: pre-wrap;
}

/* ---------------------------------------------------------
   SETTINGS FORM LAYOUT
   --------------------------------------------------------- */
.settings-form-layout {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 30px;
}

@media (max-width: 1024px) {
    .settings-form-layout {
        grid-template-columns: 1fr;
    }
}

.settings-column {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.settings-group {
    background: rgba(12, 18, 34, 0.65);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
    padding: 24px;
}

.settings-group h3 {
    font-family: var(--font-primary);
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 10px;
}

.form-group {
    margin-bottom: 16px;
}

.form-group label {
    display: block;
    font-size: 12px;
    color: var(--text-muted);
    margin-bottom: 6px;
    font-weight: 500;
}

.form-row {
    display: flex;
    gap: 16px;
}

.form-row .form-group {
    flex: 1;
}

.form-tip {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
}

.query-preview-results {
    margin-top: 10px;
}

.query-preview-results .widget-table-container,
.query-preview-results table {
    max-height: 260px;
    overflow: auto;
    display: block;
}

.query-preview-loading,
.query-preview-empty {
    font-size: 12px;
    color: var(--text-muted);
    padding: 8px 0;
}

.error-msg.text-danger {
    font-size: 12px;
    color: var(--accent-red);
    padding: 8px 0;
}

.settings-actions {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Connection Status Card */
.connection-status-card {
    background: rgba(30, 41, 59, 0.4);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 16px;
}

.status-indicator {
    display: flex;
    gap: 14px;
    align-items: center;
}

.status-indicator i {
    font-size: 20px;
}

.status-indicator.success i {
    color: var(--accent-emerald);
}

.status-indicator.error i {
    color: var(--accent-red);
}

.status-text h4,
.status-text-title {
    font-size: 13px;
    font-weight: 600;
}

.status-text p {
    font-size: 11px;
    color: var(--text-muted);
}

/* ---------------------------------------------------------
   MODALS LAYOUT
   --------------------------------------------------------- */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.8);
    backdrop-filter: blur(8px);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 200;
    animation: modalFadeIn 0.3s ease-out;
}

@keyframes modalFadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

.modal-card {
    background: #0E1525;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 12px;
    width: 100%;
    max-width: 500px;
    overflow: hidden;
    animation: modalSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
    display: flex;
    flex-direction: column;
    max-height: 90vh;
}

.modal-large {
    max-width: 650px;
}

/* The widget editor carries the per-column config lists, which need real
   horizontal room; at .modal-large's 650px their controls were clipped by
   .modal-card's overflow: hidden and became unreachable. Scoped to this one
   modal so every other .modal-large keeps its narrower, calmer width. */
#modal-widget-editor .modal-card {
    max-width: 1040px;
    width: 94vw;
}

@keyframes modalSlideUp {
    from {
        transform: scale(0.9) translateY(15px);
    }

    to {
        transform: scale(1) translateY(0);
    }
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.modal-header h3 {
    font-family: var(--font-primary);
    font-size: 16px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 10px;
}

.modal-close {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 16px;
    cursor: pointer;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: var(--transition-smooth);
}

.modal-close:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.05);
}

.modal-card form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

.modal-body {
    padding: 20px;
    overflow-y: auto;
    flex: 1;
    min-height: 0;
}

.modal-footer {
    padding: 16px 20px;
    background: rgba(15, 23, 42, 0.3);
    border-top: 1px solid rgba(255, 255, 255, 0.05);
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

/* ---------------------------------------------------------
   MISCELLANEOUS / STATES
   --------------------------------------------------------- */
.hidden {
    display: none !important;
}

.empty-state {
    text-align: center;
    padding: 56px 40px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 12px;
}

.empty-icon {
    font-size: 48px;
    color: var(--accent-primary);
    margin-bottom: 18px;
}

.empty-state h3 {
    font-size: 18px;
    font-weight: 700;
    color: var(--text-main);
    margin-bottom: 8px;
}

.empty-state p {
    font-size: 13px;
    color: var(--text-secondary);
    max-width: 46ch;
    margin-inline: auto;
    margin-bottom: 20px;
}

/* Spinner */
.spinner {
    width: 16px;
    height: 16px;
    border: 2px solid rgba(255, 255, 255, 0.2);
    border-top-color: white;
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
    display: inline-block;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* Settings Sub-Tabs Styling */
.sub-tabs-nav {
    display: flex;
    gap: 16px;
    margin-bottom: 24px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    padding-bottom: 12px;
    flex-wrap: wrap;
}

.sub-tab-group {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    padding-inline-end: 16px;
    margin-inline-end: 4px;
    border-inline-end: 1px solid rgba(255, 255, 255, 0.08);
}

.sub-tab-group:last-child {
    border-inline-end: none;
    padding-inline-end: 0;
    margin-inline-end: 0;
}

/* Group labels ("Configuration", "Data Sources", ...) are section headings,
   not clickable controls -- kept visually flat/dim (no pill, no hover, no
   pointer cursor) so they read distinctly from the .sub-tab-btn buttons that
   sit right next to them at a similar text size. */
.sub-tab-group-label {
    color: var(--text-secondary);
    opacity: 0.85;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    cursor: default;
    pointer-events: none;
    margin-inline-end: 2px;
    white-space: nowrap;
}

/* Wayfinding: when one of a group's sub-tabs is active (the accent-filled
   pill), reinforce the group heading in accent so the three tiers read as a
   clear location in the Settings hierarchy instead of a flat row of dim
   micro-labels. :has() is supported in all modern browsers (Chrome 105+,
   Safari 15.4+, Firefox 121+); the accent-strengthen degrades to the plain
   label above in older engines. */
.sub-tab-group:has(.sub-tab-btn.active) .sub-tab-group-label {
    color: var(--accent-primary);
    opacity: 1;
}

/* Clickable sub-tab buttons always carry a pill outline, even when inactive,
   so they read as buttons rather than plain labels -- previously identical
   in color/weight to .sub-tab-group-label until hovered. */
.sub-tab-btn {
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    color: var(--text-secondary);
    padding: 10px 20px;
    font-size: 13px;
    font-family: var(--font-primary);
    font-weight: 500;
    cursor: pointer;
    border-radius: 8px;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
    display: flex;
    align-items: center;
    gap: 8px;
}

.sub-tab-btn:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.16);
}

.sub-tab-btn.active {
    color: var(--bg-base);
    background: var(--accent-primary);
    box-shadow: 0 4px 14px rgba(var(--accent-primary-rgb), 0.30);
    font-weight: 600;
}

.sub-tab-panel {
    display: none;
    animation: fadeInSubTab 0.3s ease;
}

.sub-tab-panel.active {
    display: block;
}

@keyframes fadeInSubTab {
    from {
        opacity: 0;
        transform: translateY(6px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Search bar and table controls */
.search-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    gap: 20px;
}

.search-input-wrapper {
    position: relative;
    flex: 1;
    max-width: 380px;
}

.search-input-wrapper i {
    position: absolute;
    inset-inline-start: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    font-size: 13px;
}

.search-input {
    width: 100%;
    background: rgba(15, 23, 42, 0.4);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 10px 14px 10px 38px;
    color: var(--text-main);
    font-size: 13px;
    transition: border-color 0.2s;
    outline: none;
}

.search-input:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb), 0.15);
}

/* Excel File Upload Drag-and-Drop Area */
.excel-upload-zone {
    border: 2px dashed var(--border-color);
    border-radius: 12px;
    padding: 35px 20px;
    text-align: center;
    background: rgba(255, 255, 255, 0.015);
    cursor: pointer;
    transition: var(--transition-smooth);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.excel-upload-zone:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.5);
    background: rgba(255, 255, 255, 0.03);
}

.excel-upload-zone.dragover {
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.08);
    box-shadow: 0 0 15px rgba(var(--accent-primary-rgb), 0.12);
}

/* One-shot attention pulse used by the guided empty-state CTA to draw the eye
   to the import zone after _focusExcelImport() scrolls it into view. Subtle,
   has prefers-reduced-motion support, and is removed on-hover. */
.excel-upload-zone--pulse {
    animation: excelZonePulse 1.1s ease-out 2;
    border-color: rgba(var(--accent-primary-rgb), 0.55);
}

.excel-upload-zone--pulse:hover {
    animation: none;
}

@keyframes excelZonePulse {
    0% {
        box-shadow: 0 0 0 0 rgba(var(--accent-primary-rgb), 0.0);
    }

    45% {
        box-shadow: 0 0 0 6px rgba(var(--accent-primary-rgb), 0.28);
    }

    100% {
        box-shadow: 0 0 0 14px rgba(var(--accent-primary-rgb), 0.0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .excel-upload-zone--pulse {
        animation: none;
    }
}

.upload-icon {
    font-size: 32px;
    color: var(--text-muted);
    margin-bottom: 4px;
    transition: var(--transition-smooth);
}

.excel-upload-zone:hover .upload-icon,
.excel-upload-zone.dragover .upload-icon {
    color: var(--accent-primary);
    transform: translateY(-2px);
    filter: drop-shadow(0 0 6px rgba(var(--accent-primary-rgb), 0.5));
}

.upload-text {
    font-size: 13px;
    color: var(--text-main);
    font-weight: 400;
}

.upload-text .highlight {
    color: var(--accent-primary);
    font-weight: 600;
    text-decoration: underline;
}

.upload-tip {
    font-size: 11px;
    color: var(--text-muted);
}

/* File Selected Preview Banner */
.file-info-preview {
    display: flex;
    align-items: center;
    gap: 12px;
    background: rgba(255, 255, 255, 0.035);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 12px 16px;
    margin-top: 15px;
    animation: fadeInSubTab 0.3s ease;
}

.file-info-preview .file-icon {
    font-size: 22px;
    color: var(--accent-emerald);
}

.file-details {
    flex-grow: 1;
    overflow: hidden;
}

.file-details h4,
.file-preview-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.file-details p {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 2px;
}

.btn-clear-file {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 6px;
    font-size: 14px;
    border-radius: 4px;
    transition: var(--transition-smooth);
    display: flex;
    align-items: center;
    justify-content: center;
}

.btn-clear-file:hover {
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
}

/* ── 1b. Brand Theme: FusionFlow (default for dark mode) ──
   Tinted neutrals lean toward the brand's blue-cyan family so the
   surface and the accent feel like one product, not a theme pack. */
body.theme-fusionflow {
    --bg-base: #070B14;
    --bg-surface: rgba(11, 17, 31, 0.62);
    --bg-elevated: rgba(14, 21, 38, 0.72);
    --bg-input: rgba(18, 26, 45, 0.66);
    --border-color: rgba(140, 170, 220, 0.10);
    --text-main: #F0F4FF;
    --text-primary: #F0F4FF;
    --text-secondary: #A8B4CC;
    --text-muted: #8A9BC4;
    --text-dark: #070B14;
    --text-on-accent: #061018;
    --accent-primary: #4FC3F7;
    --accent-primary-rgb: 79, 195, 247;
    --accent-indigo: #7C6CFF;
    --accent-indigo-rgb: 124, 108, 255;
    --accent-emerald: #10B981;
    --accent-amber: #F59E0B;
    --accent-amber-rgb: 245, 158, 11;
    --accent-red: #F43F5E;
    --accent-red-rgb: 244, 63, 94;
    --accent-blue: #60A5FA;
    /* was #4FC3F7, byte-identical to --accent-primary above */
    --chart-grid: rgba(140, 170, 220, 0.07);
    --chart-label: #94A3B8;
    --chart-tooltip-bg: #080C14;
    --chart-tooltip-title: #F8FAFC;
    --chart-tooltip-body: #E2E8F0;
    --chart-tooltip-border: rgba(140, 170, 220, 0.28);
    --chart-heatmap-low: rgba(15, 23, 42, 0.9);
    --card-shadow: 0 4px 18px rgba(0, 0, 0, 0.28);
    --glass-bg: rgba(11, 17, 31, 0.55);
    --glass-border: rgba(140, 170, 220, 0.10);
    --radial-color-1: rgba(79, 195, 247, 0.10);
    --radial-color-2: rgba(124, 108, 255, 0.08);
    --radial-color-3: rgba(11, 17, 31, 0.00);
}

/* Chrome surfaces (sidebar, filter bar), derived from whichever theme is
   active rather than hand-picked -- the sidebar used to carry four unrelated
   hardcoded colours and the filter bar a fixed slate that matched no theme.

   DECLARED ON body, NOT :root, and that is load-bearing. A var() inside a
   custom property is substituted using the values on the element the
   DECLARATION sits on, and only the result inherits. Declared on :root these
   resolved against :root's own (dark-glass) --accent-primary/--bg-base, so
   every theme inherited the same dark teal surface no matter which
   body.theme-* class was applied. The theme classes are on body, so the
   derivation has to be too.

   The wash is the theme's accent mixed into its canvas -- NOT a black shade,
   which over a near-white canvas produced a desaturated grey matching no
   theme. 12%/4% is the strongest wash that keeps every text token at or above
   4.5:1 on both gradient stops in all four themes. */
body {
    --chrome-tint-strong: color-mix(in srgb, var(--accent-primary) 12%, var(--bg-base));
    --chrome-tint-mid: color-mix(in srgb, var(--accent-primary) 8%, var(--bg-base));
    --chrome-tint-soft: color-mix(in srgb, var(--accent-primary) 4%, var(--bg-base));
    --sidebar-gradient: linear-gradient(165deg, var(--chrome-tint-strong) 0%, var(--chrome-tint-mid) 45%, var(--chrome-tint-soft) 100%);
    --panel-gradient: linear-gradient(135deg, var(--chrome-tint-strong) 0%, var(--chrome-tint-soft) 100%);
}

/* ── Light themes: shared card / input / glass-app overrides ── */
body.theme-daylight .glass-app {
    background: var(--bg-surface);
}

/* A light theme needs a surface HIERARCHY, not one flat white. The dark
   themes separate the modal shell, the panels inside it and the inputs inside
   those by luminance; painting all three the same near-white made Preferences
   and Settings read as one undifferentiated sheet with the group boundaries
   invisible. So: the shell is tinted, and the panels sitting ON it are pure
   white with a real (if quiet) shadow. */
/* Nesting ALTERNATES white / tinted / white rather than stepping down through
   three near-whites. Three shades a few units apart (#FFFFFF / #F7F8FA /
   #F3F5F8) are indistinguishable in practice -- the eye reads them as one
   surface. Alternating means every boundary is a full ~15/255 step, using only
   two colours no matter how deep the nesting goes:
     modal / widget card  -> white
     group inside it      -> tinted (a recess: border, no shadow)
     card inside a group  -> white again (raised: border + shadow)  */
body.theme-daylight .modal-card,
body.theme-daylight .widget-card,
body.theme-daylight .sync-card,
body.theme-daylight .widget-visual-builder,
body.theme-daylight .user-assign-list {
    background: #FFFFFF;
    border: 1px solid var(--border-color);
    box-shadow: var(--card-shadow);
}

body.theme-daylight .settings-group {
    background: #EDF0F4;
    border: 1px solid var(--border-color);
    box-shadow: none;
}

body.theme-daylight .theme-card {
    background: #FFFFFF;
    border: 1px solid var(--border-color);
    box-shadow: var(--card-shadow);
}

body.theme-daylight .theme-card:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.45);
    box-shadow: 0 2px 10px rgba(var(--accent-primary-rgb), 0.16);
}

body.theme-daylight input,
body.theme-daylight select,
body.theme-daylight textarea {
    background: #FFFFFF;
    border: 1px solid rgba(0, 0, 0, 0.12);
    color: #1F2937;
}

/* Light themes: .modal-footer has its own hardcoded dark tint separate from
   .modal-card (already covered above), so it needs its own light override. */
body.theme-daylight .modal-footer {
    background: rgba(0, 0, 0, 0.03);
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

/* Light themes: .parameter-input's hover/focus states hardcode a dark tint
   with higher specificity than the generic input override above, so they
   still flash black on interaction unless overridden here too. */
body.theme-daylight .parameter-input:hover,
body.theme-daylight .parameter-input:focus {
    background: #FFFFFF;
}

/* Light themes: `.login-card input[type="..."]`'s own selector out-specifies
   the generic input override above, so the login fields never picked up the
   white background on their own. */
body.theme-daylight .login-card input[type="text"],
body.theme-daylight .login-card input[type="password"] {
    background: #FFFFFF;
    border-color: rgba(0, 0, 0, 0.12);
    color: #1F2937;
}

/* Light themes: KPI value — soft accent halo (the base 0.45 glow is too
   strong over a white card). */
body.theme-daylight .kpi-value {
    color: var(--accent-primary);
    text-shadow: 0 0 22px rgba(var(--accent-primary-rgb), 0.30);
}

/* Light themes: btn-primary — white text on colored button */
body.theme-daylight .btn-primary {
    color: #ffffff;
}

/* Light themes: btn-secondary — the default is a white-based tint, which is
   invisible on a light background; use a dark-based tint instead. */
body.theme-daylight .btn-secondary {
    background: rgba(0, 0, 0, 0.04);
    border-color: rgba(0, 0, 0, 0.12);
    color: var(--text-main);
}

body.theme-daylight .btn-secondary:hover {
    background: rgba(0, 0, 0, 0.08);
}

/* Light themes: sub-tab default state — the base rule's white-based tint
   (rgba(255, 255, 255, ...)) is invisible on a light background, same issue
   as btn-secondary above; use a dark-based tint so inactive tabs still read
   as clickable pills. */
body.theme-daylight .sub-tab-btn {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.10);
    color: var(--text-secondary);
}

body.theme-daylight .sub-tab-group {
    border-inline-end-color: rgba(0, 0, 0, 0.08);
}

/* Light themes: sub-tab active — bordered pill style */
body.theme-daylight .sub-tab-btn.active {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.10);
    box-shadow: none;
    border-bottom: 2px solid var(--accent-primary);
}

body.theme-daylight .sub-tab-btn:hover {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.06);
    border-color: rgba(var(--accent-primary-rgb), 0.30);
}

/* ── 5. Daylight — the default: warm off-white with a deep corporate blue.
      Replaced Light Ocean and GitHub Light (both aliased to it in themes.js).
      Text/accent pairs are chosen for >=4.5:1 on white, so KPI values, muted
      helper text and chart labels all stay legible in a bright office. ── */
body.theme-daylight {
    --bg-base: #F7F8FA;
    --bg-surface: rgba(255, 255, 255, 0.96);
    --bg-elevated: #FFFFFF;
    --bg-input: #FFFFFF;
    --glass-bg: rgba(255, 255, 255, 0.94);
    --glass-border: rgba(16, 24, 40, 0.08);
    --border-color: #bcc1c9;
    /* 0.085, well above the 0.03-0.045 a light theme usually uses: these
       tables are dense, wide and horizontally scrolled, so the banding has to
       survive being tracked across a full screen width. Rendered side by side
       at 0.045 / 0.06 / 0.07 / 0.09 -- below 0.06 the band is not perceptible
       at all on a typical office monitor. Do not "tidy" this back down to a
       conventional value without looking at a real 12-column table first. */
    /* OPAQUE, unlike the dark themes' translucent overlays: a table cell can
       sit over the card, over the sticky first column's own background, or
       over a data-bar gradient, and a low-alpha wash gets diluted differently
       in each. A solid colour bands identically everywhere. */
    --table-row-stripe: #cbdaee;
    --table-row-hover: #c0d4f7;
    --radial-color-1: rgba(30, 64, 175, 0.055);
    --radial-color-2: rgba(3, 105, 161, 0.035);
    --radial-color-3: rgba(100, 116, 139, 0.03);
    --chart-grid: rgba(16, 24, 40, 0.07);
    --chart-label: #4B5563;
    --chart-tooltip-bg: #FFFFFF;
    --chart-tooltip-title: #101828;
    --chart-tooltip-body: #475467;
    --chart-tooltip-border: #D2D8E1;
    --chart-heatmap-low: rgba(219, 234, 254, 0.95);
    --accent-primary: #1E40AF;
    --accent-primary-rgb: 30, 64, 175;
    --accent-indigo: #4338CA;
    --accent-indigo-rgb: 67, 56, 202;
    --accent-blue: #0369A1;
    --accent-emerald: #065F46;
    --accent-amber: #B45309;
    --accent-amber-rgb: 180, 83, 9;
    --accent-red: #B91C1C;
    --accent-red-rgb: 185, 28, 28;
    --rule-orange: #C2410C;
    --rule-teal: #0F766E;
    --rule-pink: #BE185D;
    --rule-slate: #475569;
    --text-main: #101828;
    --text-primary: #101828;
    --text-secondary: #344054;
    --text-muted: #54606F;
    --text-dark: #1D2939;
    --text-on-accent: #FFFFFF;
    /* Visible two-layer elevation on white -- same presence the dark themes'
       deep shadows give their cards. Kept as var(--card-shadow) so the
       light-surface-hierarchy tests keep passing unchanged. */
    --card-shadow: 0 2px 4px rgba(16, 24, 40, 0.10), 0 10px 28px -6px rgba(16, 24, 40, 0.18);
    --chart-palette: #1E40AF, #84CC16, #EF4444, #0D9488, #F59E0B, #0EA5E9, #EC4899, #7C3AED, #16A34A, #92400E;
}

/* Light themes: chrome accents. Written against --accent-primary-rgb so each
   light theme gets its own accent instead of a hardcoded cobalt. */
body.theme-daylight .glass-app {
    background: var(--bg-surface);
}

body.theme-daylight .nav-btn:hover {
    background: rgba(var(--accent-primary-rgb), 0.07);
    color: var(--text-main);
}

body.theme-daylight .nav-btn.active {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.24);
    box-shadow: 0 0 14px -4px rgba(var(--accent-primary-rgb), 0.22);
    color: var(--accent-primary);
}

body.theme-daylight input:focus,
body.theme-daylight select:focus,
body.theme-daylight textarea:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb), 0.15);
}

/* Keyboard focus on the light daylight theme: the accent outline alone is a
   blue ring on a blue-tinted field, which is a color-not-alone cue. Add a
   solid dark navy outer ring + a white halo so focus stays identifiable for
   low-vision and augmented-contrast users even if they cannot distinguish
   the accent hue. :focus-visible only (mouse clicks keep the quiet field
   border above). */
body.theme-daylight :focus-visible {
    outline: 2px solid #1F2937;
    outline-offset: 2px;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.92);
}

/* ── Dark themes: shared widget / modal / card overrides ── */
body.theme-nord-arctic .widget-card,
body.theme-nord-arctic .sync-card,
body.theme-nord-arctic .modal-card,
body.theme-nord-arctic .settings-group,
body.theme-nord-arctic .widget-visual-builder,
body.theme-nord-arctic .user-assign-list {
    background: rgba(24, 31, 46, 0.80);
    border: 1px solid rgba(136, 192, 208, 0.12);
}

body.theme-nord-arctic input,
body.theme-nord-arctic select,
body.theme-nord-arctic textarea {
    background: rgba(14, 18, 30, 0.70);
    border: 1px solid rgba(136, 192, 208, 0.16);
    color: var(--text-main);
}

body.theme-nord-arctic input:focus,
body.theme-nord-arctic select:focus,
body.theme-nord-arctic textarea:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px rgba(136, 192, 208, 0.18);
}

/* ── 7. Nord Arctic — polar night with frost & aurora ── */
body.theme-nord-arctic {
    --bg-base: #0E1117;
    --bg-surface: rgba(19, 24, 34, 0.92);
    --bg-elevated: rgba(24, 31, 46, 0.90);
    --bg-input: rgba(14, 18, 30, 0.70);
    --border-color: rgba(136, 192, 208, 0.13);
    --radial-color-1: rgba(136, 192, 208, 0.09);
    --radial-color-2: rgba(163, 190, 140, 0.07);
    --radial-color-3: rgba(94, 129, 172, 0.06);
    --chart-grid: rgba(136, 192, 208, 0.07);
    --chart-label: #8BA9B7;
    --chart-tooltip-bg: #2E3440;
    --chart-tooltip-title: #ECEFF4;
    --chart-tooltip-body: #D8DEE9;
    --chart-tooltip-border: #4C566A;
    --chart-heatmap-low: rgba(46, 52, 64, 0.92);
    --accent-primary: #88C0D0;
    --accent-primary-rgb: 136, 192, 208;
    --accent-indigo: #81A1C1;
    --accent-indigo-rgb: 129, 161, 193;
    --accent-blue: #5E81AC;
    --accent-emerald: #A3BE8C;
    --accent-amber: #EBCB8B;
    --accent-amber-rgb: 235, 203, 139;
    /* Nord's aurora red is #BF616A, but at that luminance neither black nor
       white ink clears 4.5:1 on top of it (best case 4.47), so label text
       inside a red pie slice / threshold bar could not be made readable by
       any ink choice. Lightened by 1% L, hue and saturation held, which
       reaches 4.63 and is visually indistinguishable. */
    --accent-red: #C0656D;
    --accent-red-rgb: 192, 101, 109;
    --text-main: #ECEFF4;
    --text-primary: #ECEFF4;
    --text-secondary: #B0C4D0;
    --text-muted: #8FAABC;
    --text-dark: #4C566A;
    --text-on-accent: #2E3440;
    --card-shadow: 0 8px 32px -8px rgba(0, 0, 0, 0.65);
}

body.theme-nord-arctic .nav-btn:hover {
    background: rgba(136, 192, 208, 0.15);
    color: var(--text-main);
}

body.theme-nord-arctic .nav-btn.active {
    background: rgba(136, 192, 208, 0.10);
    border: 1px solid rgba(136, 192, 208, 0.26);
    box-shadow: 0 0 16px -4px rgba(136, 192, 208, 0.32);
    color: var(--text-main);
}

body.theme-nord-arctic .sub-tab-btn.active {
    color: var(--bg-base);
    background: var(--accent-primary);
    box-shadow: 0 4px 14px rgba(136, 192, 208, 0.28);
    font-weight: 600;
}

body.theme-nord-arctic .sub-tab-btn:hover {
    color: var(--accent-primary);
    background: rgba(136, 192, 208, 0.08);
}

/* ═══════════════════════════════════════════════════════════
   NEW THEMES
   ═══════════════════════════════════════════════════════════ */

/* ── Dracula ── */
body.theme-dracula {
    --bg-base: #282A36;
    --bg-surface: rgba(33, 34, 44, 0.96);
    --bg-elevated: rgba(40, 42, 54, 0.96);
    --bg-input: rgba(33, 34, 44, 0.88);
    --glass-bg: rgba(40, 42, 54, 0.82);
    --glass-border: rgba(255, 121, 198, 0.14);
    --border-color: rgba(255, 121, 198, 0.12);
    --radial-color-1: rgba(189, 147, 249, 0.10);
    --radial-color-2: rgba(255, 121, 198, 0.08);
    --radial-color-3: rgba(241, 250, 140, 0.04);
    --chart-grid: rgba(189, 147, 249, 0.10);
    --chart-label: #9A8EC0;
    --chart-tooltip-bg: #282A36;
    --chart-tooltip-title: #F8F8F2;
    --chart-tooltip-body: #D8CFEA;
    --chart-tooltip-border: #6272A4;
    --chart-heatmap-low: rgba(40, 42, 54, 0.92);
    --accent-primary: #FF79C6;
    --accent-primary-rgb: 255, 121, 198;
    --accent-indigo: #BD93F9;
    --accent-indigo-rgb: 189, 147, 249;
    --accent-blue: #8BE9FD;
    --accent-emerald: #50FA7B;
    --accent-amber: #F1FA8C;
    --accent-amber-rgb: 241, 250, 140;
    --accent-red: #FF5555;
    --accent-red-rgb: 255, 85, 85;
    --text-primary: #F8F8F2;
    --text-main: #F8F8F2;
    --text-secondary: #A79DC8;
    --text-muted: #95A1CC;
    /* was #6272A4 = 3.34:1 on this theme's card surface, below WCAG AA */
    --text-dark: #21222C;
    --text-on-accent: #21222C;
    --card-shadow: 0 8px 32px -8px rgba(0, 0, 0, 0.70);
}

/* Dracula's accent is light enough that a 15% tint of it under the same colour
   as text left the avatar initial at 4.45:1 -- under the 4.5:1 AA minimum, and
   16px bold does not qualify as WCAG large text (needs 18.66px bold / 24px).
   Thinning the fill to 10% takes it to 4.87:1 and keeps the pink letter+ring
   identity; every other theme is 6.2-7.4:1 on the shared rule and untouched. */
body.theme-dracula .user-profile-avatar {
    background: rgba(var(--accent-primary-rgb), 0.10);
}

body.theme-dracula .widget-card,
body.theme-dracula .sync-card,
body.theme-dracula .modal-card,
body.theme-dracula .settings-group,
body.theme-dracula .widget-visual-builder,
body.theme-dracula .user-assign-list {
    background: rgba(40, 42, 54, 0.85);
    border: 1px solid rgba(189, 147, 249, 0.14);
}

body.theme-dracula input,
body.theme-dracula select,
body.theme-dracula textarea {
    background: rgba(33, 34, 44, 0.88);
    border: 1px solid rgba(189, 147, 249, 0.18);
    color: var(--text-main);
}

body.theme-dracula input:focus,
body.theme-dracula select:focus,
body.theme-dracula textarea:focus {
    border-color: #BD93F9;
    box-shadow: 0 0 0 2px rgba(189, 147, 249, 0.20);
}

body.theme-dracula .nav-btn:hover {
    background: rgba(189, 147, 249, 0.16);
    color: var(--text-main);
}

body.theme-dracula .nav-btn.active {
    background: rgba(189, 147, 249, 0.12);
    border: 1px solid rgba(189, 147, 249, 0.30);
    box-shadow: 0 0 20px -4px rgba(189, 147, 249, 0.38);
    color: #BD93F9;
}

body.theme-dracula .sub-tab-btn.active {
    color: #282A36;
    background: #BD93F9;
    box-shadow: 0 4px 14px rgba(189, 147, 249, 0.34);
    font-weight: 600;
}

body.theme-dracula .sub-tab-btn:hover {
    color: #BD93F9;
    background: rgba(189, 147, 249, 0.10);
}

/* ═══════════════════════════════════════════════════════════
   OCEAN — light + dark (added 2026-08-24)
   Ported from another app of the same author's. The port is the four
   surface/accent colours of each variant; everything else (text, status,
   chart and chrome tokens) is derived here against THIS app's token layer,
   because copying only the four would leave every other token falling
   through to :root's dark palette.

   Two things about this pair that are not obvious and must survive edits:

   1. Ocean Dark paints DARK ink on its own filled buttons. #3B82F6 is bright
      enough that white on it is 3.68:1 -- under AA. --text-on-accent carries
      that ink (#0A1628, the theme's own panel colour) rather than any rule
      hardcoding white; .btn-primary / .lang-switch-btn.active / the "Brand"
      chip all read it.
   2. --border-color is a 1.3:1 hairline and separates REGIONS only. Anything
      outlined that can be clicked uses --control-border instead (>=3:1 on the
      surfaces it can sit on: 4.66 on a white field, 3.82 on the panel, 3.35
      on a recessed group). Conflating the two is the easy way to end up with
      a palette that looks accessible and is not.

   Every ratio quoted in this section was computed against the same colour
   maths tests/test_chart_label_contrast.py uses, not estimated.
   ═══════════════════════════════════════════════════════════ */

/* ── Ocean (light) — pale sky canvas, cornflower panels, cobalt accent ── */
body.theme-ocean {
    /* Surface ladder. Each boundary is a full ~20/255 step, alternating the
       way the daylight block explains: canvas -> panel -> recess -> raised.
       A nested card lands back on a lighter blue rather than a fourth
       near-identical shade nobody can see. */
    --bg-base: #F0F8FF;
    --bg-surface: #DBEAFE;
    --bg-elevated: #EAF4FE;
    --bg-input: #FFFFFF;
    --bg-recess: #C6DDF9;
    --glass-bg: rgba(234, 244, 254, 0.94);
    --glass-border: rgba(30, 58, 95, 0.12);
    /* Region hairline only -- 1.3:1 against the panel by design. */
    --border-color: #BFDBFE;
    /* Outlined clickables. 4.66:1 on a white field, 3.82:1 on the panel. */
    --control-border: #4E76AC;
    /* Opaque, like daylight's: a table cell can sit over the card, over the
       sticky first column or over a data-bar gradient, and a low-alpha wash
       dilutes differently in each. */
    --table-row-stripe: #C3DCF7;
    --table-row-hover: #AECEF5;
    --radial-color-1: rgba(37, 99, 235, 0.06);
    --radial-color-2: rgba(7, 89, 133, 0.04);
    --radial-color-3: rgba(67, 56, 202, 0.03);
    --chart-grid: rgba(11, 27, 46, 0.08);
    --chart-label: #3F5871;
    --chart-tooltip-bg: #FFFFFF;
    --chart-tooltip-title: #0B1B2E;
    --chart-tooltip-body: #274058;
    --chart-tooltip-border: #BFDBFE;
    --chart-heatmap-low: rgba(219, 234, 254, 0.95);
    --accent-primary: #2563EB;
    --accent-primary-rgb: 37, 99, 235;
    --accent-indigo: #4338CA;
    --accent-indigo-rgb: 67, 56, 202;
    /* Sky-800, not sky-700: at 0.8 alpha over this panel the 700 composites
       to a mid blue where neither white nor near-black ink clears AA (4.36).
       This one reaches 4.99 at the same alpha. */
    --accent-blue: #075985;
    /* Status colours are darkened past the usual 600-weights on purpose. The
       panel is #DBEAFE, not white, and ratios run lower there -- the 600s land
       around 4.1:1 on it, fine for 18px+ but under AA for small body text. */
    --accent-emerald: #046C4E;
    --accent-amber: #9A4C0A;
    --accent-amber-rgb: 154, 76, 10;
    --accent-red: #B4232C;
    --accent-red-rgb: 180, 35, 44;
    --rule-orange: #C2410C;
    --rule-teal: #0F766E;
    --rule-pink: #BE185D;
    --rule-slate: #475569;
    --text-main: #0B1B2E;
    --text-primary: #0B1B2E;
    --text-secondary: #274058;
    --text-muted: #42586F;
    --text-dark: #12283F;
    --text-on-accent: #FFFFFF;
    --card-shadow: 0 2px 4px rgba(12, 26, 46, 0.08), 0 10px 28px -6px rgba(12, 26, 46, 0.16);
    /* The first three UI accents are all blues, so charts drawn from them read
       as monochrome -- same reason daylight ships its own series list. */
    --chart-palette: #2563EB, #0F766E, #B4232C, #7C3AED, #9A4C0A, #0369A1, #BE185D, #166534, #4338CA, #7C2D12;
}

body.theme-ocean .glass-app {
    background: var(--bg-surface);
}

/* --bg-elevated, NOT --bg-surface: .glass-app (the page panel these cards
   sit on) is itself --bg-surface, so a card painted with it was the exact
   same #DBEAFE as the page behind it -- separated only by a 1.3:1 hairline
   border, which reads as one flat wash. Every other theme puts its card one
   rung up the ladder this block declares (daylight: panel -> white).
   Knock-on, and intended: .widget-table th stays --bg-surface, so the table
   header is now a visible band against the lighter card instead of
   disappearing into it. */
body.theme-ocean .modal-card,
body.theme-ocean .widget-card,
body.theme-ocean .sync-card,
body.theme-ocean .widget-visual-builder,
body.theme-ocean .user-assign-list {
    background: var(--bg-elevated);
    border: 1px solid var(--border-color);
    box-shadow: var(--card-shadow);
}

body.theme-ocean .settings-group {
    background: var(--bg-recess, #C6DDF9);
    border: 1px solid var(--border-color);
    box-shadow: none;
}

body.theme-ocean .theme-card {
    background: var(--bg-elevated, #EAF4FE);
    border: 1px solid var(--border-color);
    box-shadow: var(--card-shadow);
}

body.theme-ocean .theme-card:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.45);
    box-shadow: 0 2px 10px rgba(var(--accent-primary-rgb), 0.16);
}

body.theme-ocean input,
body.theme-ocean select,
body.theme-ocean textarea {
    background: var(--bg-input, #FFFFFF);
    border: 1px solid var(--control-border, #4E76AC);
    color: var(--text-main);
}

body.theme-ocean .modal-footer {
    background: rgba(12, 26, 46, 0.04);
    border-top: 1px solid var(--border-color);
}

body.theme-ocean .parameter-input:hover,
body.theme-ocean .parameter-input:focus {
    background: var(--bg-input, #FFFFFF);
}

body.theme-ocean .login-card input[type="text"],
body.theme-ocean .login-card input[type="password"] {
    background: var(--bg-input, #FFFFFF);
    border-color: var(--control-border, #4E76AC);
    color: var(--text-main);
}

body.theme-ocean .kpi-value {
    color: var(--accent-primary);
    text-shadow: 0 0 22px rgba(var(--accent-primary-rgb), 0.30);
}

body.theme-ocean .btn-primary {
    color: var(--text-on-accent, #FFFFFF);
}

body.theme-ocean .btn-secondary {
    background: rgba(12, 26, 46, 0.05);
    border-color: var(--control-border, #4E76AC);
    color: var(--text-main);
}

body.theme-ocean .btn-secondary:hover {
    background: rgba(12, 26, 46, 0.10);
}

body.theme-ocean .sub-tab-btn {
    background: rgba(12, 26, 46, 0.04);
    border-color: var(--control-border, #4E76AC);
    color: var(--text-secondary);
}

body.theme-ocean .sub-tab-group {
    border-inline-end-color: rgba(12, 26, 46, 0.10);
}

body.theme-ocean .sub-tab-btn.active {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.10);
    box-shadow: none;
    border-bottom: 2px solid var(--accent-primary);
}

body.theme-ocean .sub-tab-btn:hover {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.06);
    border-color: rgba(var(--accent-primary-rgb), 0.40);
}

body.theme-ocean .nav-btn:hover {
    background: rgba(var(--accent-primary-rgb), 0.07);
    color: var(--text-main);
}

body.theme-ocean .nav-btn.active {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.24);
    box-shadow: 0 0 14px -4px rgba(var(--accent-primary-rgb), 0.22);
    color: var(--accent-primary);
}

body.theme-ocean input:focus,
body.theme-ocean select:focus,
body.theme-ocean textarea:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb), 0.18);
}

/* Same reasoning as daylight's: an accent ring on a blue-tinted field is a
   colour-not-alone cue, so keyboard focus adds a solid navy ring plus a white
   halo. :focus-visible only -- a mouse click keeps the quiet field border. */
body.theme-ocean :focus-visible {
    outline: 2px solid #0B1B2E;
    outline-offset: 2px;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.92);
}

/* The avatar initial is drawn in the accent on a tint of the same accent. At
   #2563EB that tops out at 4.6:1 against its own fill even with the tint
   thinned to nothing -- under the 4.5 + render-margin floor the parity test
   holds 16px bold glyphs to. A darker blue for the glyph only; the ring stays
   the accent, so the component still reads as the same one every theme has. */
body.theme-ocean .user-profile-avatar {
    background: rgba(var(--accent-primary-rgb), 0.12);
    color: #1E4FC4;
}

body.theme-ocean .widget-table-container::-webkit-scrollbar-thumb {
    background: rgba(12, 26, 46, 0.20);
}

body.theme-ocean .widget-table-container::-webkit-scrollbar-thumb:hover {
    background: rgba(12, 26, 46, 0.32);
}

body.theme-ocean .option-pill {
    background: rgba(12, 26, 46, 0.04);
    border-color: var(--control-border, #4E76AC);
}

body.theme-ocean .option-pill.active {
    background: rgba(var(--accent-primary-rgb), 0.10);
    border-color: rgba(var(--accent-primary-rgb), 0.40);
}

body.theme-ocean .col-mapping-panel {
    background: rgba(12, 26, 46, 0.04);
    border-color: var(--border-color);
}

body.theme-ocean .col-mapping-row input[type="text"],
body.theme-ocean .col-mapping-row select {
    background: var(--bg-input, #FFFFFF);
    border-color: var(--control-border, #4E76AC);
    color: var(--text-main);
}

body.theme-ocean .sync-schedule-panel {
    background: rgba(12, 26, 46, 0.04);
    border-color: var(--border-color);
}

body.theme-ocean .sync-time-input {
    color-scheme: light;
    background: var(--bg-input, #FFFFFF);
    color: var(--text-main);
}

body.theme-ocean .wt-card {
    background: rgba(12, 26, 46, 0.03);
}

body.theme-ocean .wt-card:hover {
    background: rgba(var(--accent-primary-rgb), 0.06);
}

body.theme-ocean .sidebar-db-search-input {
    background: var(--bg-input, #FFFFFF);
    border: 1px solid var(--control-border, #4E76AC);
    color: var(--text-main);
}

body.theme-ocean .sidebar-db-search-input:focus {
    border-color: var(--accent-primary);
}

body.theme-ocean .sidebar-db-option {
    color: var(--text-muted);
}

body.theme-ocean .sidebar-db-option:hover {
    background: rgba(var(--accent-primary-rgb), 0.07);
    color: var(--text-main);
}

body.theme-ocean .sidebar-db-option.active {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border-color: rgba(var(--accent-primary-rgb), 0.25);
    color: var(--text-main);
}

body.theme-ocean .sidebar-db-options-list::-webkit-scrollbar-thumb {
    background: rgba(12, 26, 46, 0.17);
}

body.theme-ocean .sidebar-db-options-list::-webkit-scrollbar-thumb:hover {
    background: rgba(12, 26, 46, 0.27);
}

/* The white-alpha fills that vanish on a light canvas, mirrored to a navy
   tint of the same weight -- the identical list daylight carries. A new
   component with an rgba(255, 255, 255, 0.0x) fill needs its mirror here too. */
body.theme-ocean .style-opt-inner {
    background: rgba(12, 26, 46, 0.04);
    border-color: var(--control-border, #4E76AC);
}

body.theme-ocean .sync-toggle-track {
    background: rgba(12, 26, 46, 0.18);
}

body.theme-ocean code {
    background: rgba(12, 26, 46, 0.07);
}

body.theme-ocean .lang-switch {
    background: rgba(12, 26, 46, 0.05);
}

body.theme-ocean .user-profile-badge:not(.user-profile-badge--clickable) {
    background: rgba(12, 26, 46, 0.04);
}

body.theme-ocean .onboarding-step .onboarding-tip {
    background: rgba(12, 26, 46, 0.04);
}

body.theme-ocean .export-progress-track {
    background: rgba(12, 26, 46, 0.09);
}

body.theme-ocean .connection-status-card {
    background: rgba(12, 26, 46, 0.04);
}

body.theme-ocean .sidebar-toggle-btn:hover {
    background: rgba(12, 26, 46, 0.07);
}

body.theme-ocean .group-pill:hover {
    background: rgba(12, 26, 46, 0.07);
}

body.theme-ocean .widget-action-btn:hover {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .modal-close:hover {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .excel-upload-zone {
    background: rgba(12, 26, 46, 0.03);
}

body.theme-ocean .excel-upload-zone:hover {
    background: rgba(12, 26, 46, 0.05);
}

body.theme-ocean .file-info-preview {
    background: rgba(12, 26, 46, 0.05);
}

body.theme-ocean .cache-badge.cache-miss {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .user-assign-item:hover {
    background: rgba(12, 26, 46, 0.05);
}

body.theme-ocean .user-assign-category-header:hover {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .sidebar-db-category-header:hover {
    background: rgba(12, 26, 46, 0.07);
}

body.theme-ocean .widget-visual-filters {
    background: rgba(12, 26, 46, 0.04);
}

body.theme-ocean .widget-table-column-row {
    background: rgba(12, 26, 46, 0.03);
}

body.theme-ocean .export-option-card {
    background: rgba(12, 26, 46, 0.03);
}

body.theme-ocean .export-option-card:hover {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .saved-view-row:hover {
    background: rgba(12, 26, 46, 0.06);
}

body.theme-ocean .sync-error-detail-lines {
    background: rgba(12, 26, 46, 0.07);
}

/* Light-theme shimmer: the sweep has to go DARKER. The base rule brushes
   rgba(255, 255, 255, 0.14) across the skeleton, which on this panel is a
   3/255 step -- a static grey box with no sign anything is loading. */
body.theme-ocean .widget-skeleton {
    background-image: linear-gradient(100deg,
            transparent 30%, rgba(0, 0, 0, 0.10) 50%, transparent 70%);
}

@media (prefers-reduced-motion: reduce) {

    /* Repeated for the theme-scoped selector on purpose: the rule above is
       (0,2,1) and outranks the (0,1,0) `background-image: none` in the
       reduced-motion block, freezing a diagonal band across every skeleton. */
    body.theme-ocean .widget-skeleton {
        background-image: none;
    }
}

/* ── Ocean Dark — midnight harbour: the panel is DARKER than the canvas ── */
body.theme-ocean-dark {
    --bg-base: #0C1A2E;
    --bg-surface: #0A1628;
    --bg-elevated: #12263E;
    --bg-input: #081321;
    --glass-bg: rgba(10, 22, 40, 0.86);
    --glass-border: rgba(59, 130, 246, 0.16);
    --border-color: #1E3A5F;
    --control-border: #4A719E;
    --table-row-stripe: rgba(255, 255, 255, 0.05);
    --table-row-hover: rgba(59, 130, 246, 0.16);
    --radial-color-1: rgba(59, 130, 246, 0.10);
    --radial-color-2: rgba(34, 211, 238, 0.06);
    --radial-color-3: rgba(139, 157, 249, 0.05);
    --chart-grid: rgba(96, 165, 250, 0.10);
    --chart-label: #8FA9C6;
    --chart-tooltip-bg: #0A1628;
    --chart-tooltip-title: #E8F1FC;
    --chart-tooltip-body: #C7D8EC;
    --chart-tooltip-border: #1E3A5F;
    --chart-heatmap-low: rgba(10, 22, 40, 0.92);
    --accent-primary: #3B82F6;
    --accent-primary-rgb: 59, 130, 246;
    --accent-indigo: #8B9DF9;
    --accent-indigo-rgb: 139, 157, 249;
    --accent-blue: #60A5FA;
    --accent-emerald: #34D399;
    --accent-amber: #FBBF24;
    --accent-amber-rgb: 251, 191, 36;
    --accent-red: #F87171;
    --accent-red-rgb: 248, 113, 113;
    --text-main: #E8F1FC;
    --text-primary: #E8F1FC;
    --text-secondary: #AFC5DE;
    --text-muted: #93AECB;
    --text-dark: #0A1628;
    /* The accent-ink. White on #3B82F6 is 3.68:1; this panel-dark ink is
       4.93:1. Every filled-accent surface reads it -- do not hardcode white
       back over the top of it. */
    --text-on-accent: #0A1628;
    --card-shadow: 0 8px 32px -8px rgba(0, 0, 0, 0.60);
    --chart-palette: #3B82F6, #34D399, #F87171, #C084FC, #FBBF24, #22D3EE, #F472B6, #A3E635, #8B9DF9, #FB923C;
}

body.theme-ocean-dark .widget-card,
body.theme-ocean-dark .sync-card,
body.theme-ocean-dark .modal-card,
body.theme-ocean-dark .settings-group,
body.theme-ocean-dark .widget-visual-builder,
body.theme-ocean-dark .user-assign-list {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
}

/* The panel is the darkest surface in this theme, so a group nested inside a
   card cannot recess any further -- it steps UP instead, the same trick the
   light themes use in reverse. Without this a settings group inside a modal
   has no visible boundary at all. */
body.theme-ocean-dark .settings-group {
    background: var(--bg-elevated, #12263E);
    box-shadow: none;
}

body.theme-ocean-dark input,
body.theme-ocean-dark select,
body.theme-ocean-dark textarea {
    background: var(--bg-input, #081321);
    border: 1px solid var(--control-border, #4A719E);
    color: var(--text-main);
}

body.theme-ocean-dark input:focus,
body.theme-ocean-dark select:focus,
body.theme-ocean-dark textarea:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 2px rgba(var(--accent-primary-rgb), 0.22);
}

body.theme-ocean-dark .btn-primary {
    color: var(--text-on-accent, #0A1628);
}

body.theme-ocean-dark .nav-btn:hover {
    background: rgba(var(--accent-primary-rgb), 0.16);
    color: var(--text-main);
}

body.theme-ocean-dark .nav-btn.active {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.30);
    box-shadow: 0 0 18px -4px rgba(var(--accent-primary-rgb), 0.36);
    color: var(--accent-blue);
}

body.theme-ocean-dark .sub-tab-btn.active {
    color: var(--text-on-accent, #0A1628);
    background: var(--accent-primary);
    box-shadow: 0 4px 14px rgba(var(--accent-primary-rgb), 0.30);
    font-weight: 600;
}

body.theme-ocean-dark .sub-tab-btn:hover {
    color: var(--accent-blue);
    background: rgba(var(--accent-primary-rgb), 0.10);
}

body.theme-ocean-dark .sidebar-db-search-input {
    background: var(--bg-input, #081321);
    border: 1px solid var(--control-border, #4A719E);
    color: var(--text-main);
}

body.theme-ocean-dark .sidebar-db-search-input:focus {
    border-color: var(--accent-primary);
}

body.theme-ocean-dark .sidebar-db-option {
    color: var(--text-secondary);
}

body.theme-ocean-dark .sidebar-db-option:hover {
    background: rgba(var(--accent-primary-rgb), 0.10);
    color: var(--text-main);
}

body.theme-ocean-dark .sidebar-db-option.active {
    background: rgba(var(--accent-primary-rgb), 0.16);
    border-color: rgba(var(--accent-primary-rgb), 0.30);
    color: var(--text-main);
}

/* Same 16px-bold glyph problem as Ocean light, mirrored: the accent on a tint
   of itself is 3.9:1 here, so the initial takes a lighter blue and the ring
   keeps the accent. */
body.theme-ocean-dark .user-profile-avatar {
    color: #7FB2FB;
}

/* --accent-red is far too light for the base rule's white ink (2.8:1); the
   near-black reading is 6.6:1. Same override the other dark themes carry. */
body.theme-ocean-dark .exceptions-badge {
    color: #0A1628;
}

/* ── Preference-picker swatches ── */
.theme-card[data-theme="ocean"] .theme-mini-preview {
    background: #F0F8FF;
}

.theme-card[data-theme="ocean"] .tmp-sidebar {
    background: #DBEAFE;
}

.theme-card[data-theme="ocean"] .tmp-bar:nth-child(1) {
    background: #2563EB;
    width: 64%;
}

.theme-card[data-theme="ocean"] .tmp-bar:nth-child(2) {
    background: #075985;
    width: 42%;
}

.theme-card[data-theme="ocean"] .tmp-kpi:nth-child(1) {
    background: #EAF4FE;
    border-color: rgba(37, 99, 235, 0.30);
}

.theme-card[data-theme="ocean"] .tmp-kpi:nth-child(2) {
    background: #EAF4FE;
    border-color: rgba(7, 89, 133, 0.30);
}

.theme-card[data-theme="ocean"] .tmp-kpi:nth-child(3) {
    background: #EAF4FE;
    border-color: rgba(67, 56, 202, 0.30);
}

.theme-card[data-theme="ocean"] .color-dot:nth-child(1) {
    background: #F0F8FF;
}

.theme-card[data-theme="ocean"] .color-dot:nth-child(2) {
    background: #DBEAFE;
}

.theme-card[data-theme="ocean"] .color-dot:nth-child(3) {
    background: #2563EB;
}

.theme-card[data-theme="ocean"] .color-dot:nth-child(4) {
    background: #075985;
}

.theme-card[data-theme="ocean-dark"] .theme-mini-preview {
    background: #0C1A2E;
}

.theme-card[data-theme="ocean-dark"] .tmp-sidebar {
    background: #0A1628;
}

.theme-card[data-theme="ocean-dark"] .tmp-bar:nth-child(1) {
    background: #3B82F6;
    width: 64%;
}

.theme-card[data-theme="ocean-dark"] .tmp-bar:nth-child(2) {
    background: #34D399;
    width: 42%;
}

.theme-card[data-theme="ocean-dark"] .tmp-kpi:nth-child(1) {
    background: #12263E;
    border-color: rgba(59, 130, 246, 0.35);
}

.theme-card[data-theme="ocean-dark"] .tmp-kpi:nth-child(2) {
    background: #12263E;
    border-color: rgba(96, 165, 250, 0.35);
}

.theme-card[data-theme="ocean-dark"] .tmp-kpi:nth-child(3) {
    background: #12263E;
    border-color: rgba(52, 211, 153, 0.35);
}

.theme-card[data-theme="ocean-dark"] .color-dot:nth-child(1) {
    background: #0C1A2E;
}

.theme-card[data-theme="ocean-dark"] .color-dot:nth-child(2) {
    background: #1E3A5F;
}

.theme-card[data-theme="ocean-dark"] .color-dot:nth-child(3) {
    background: #3B82F6;
}

.theme-card[data-theme="ocean-dark"] .color-dot:nth-child(4) {
    background: #34D399;
}

/* ---------------------------------------------------------
   FONTS SYSTEM
   --------------------------------------------------------- */
body.font-inter {
    font-family: var(--font-primary);
}

body.font-monospace {
    font-family: var(--font-mono);
}

/* Unknown/legacy font classes fall back to the primary font stack. */
body.font-outfit,
body.font-poppins,
body.font-roboto,
body.font-jakarta {
    font-family: var(--font-primary);
}

/* ---------------------------------------------------------
   THEME SELECTOR GRID
   --------------------------------------------------------- */
.theme-selector-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
    gap: 10px;
    margin-top: 12px;
}

.theme-card {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 8px;
    cursor: pointer;
    transition: var(--transition-smooth);
    display: flex;
    flex-direction: column;
    gap: 6px;
    position: relative;
    overflow: hidden;
}

.theme-card:hover {
    border-color: rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.04);
}

.theme-card.active {
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.07);
    box-shadow: 0 0 16px -4px rgba(var(--accent-primary-rgb), 0.40);
}

.theme-preview-palette {
    display: flex;
    justify-content: center;
    gap: 4px;
    margin-top: auto;
}

.color-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.1);
}

.theme-card-title {
    font-family: var(--font-primary);
    font-size: 11.5px;
    font-weight: 600;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 4px;
    text-align: center;
    line-height: 1.3;
}

.theme-mini-preview {
    width: 100%;
    height: 50px;
    border-radius: 6px;
    overflow: hidden;
    display: flex;
    border: 1px solid rgba(255, 255, 255, 0.07);
    margin-bottom: 0;
}

.tmp-sidebar {
    width: 22%;
    height: 100%;
    flex-shrink: 0;
}

.tmp-body {
    flex: 1;
    padding: 7px 8px;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.tmp-bar {
    height: 6px;
    border-radius: 3px;
    opacity: 0.85;
}

.tmp-kpis {
    display: flex;
    gap: 4px;
    margin-top: 2px;
}

.tmp-kpi {
    flex: 1;
    height: 18px;
    border-radius: 4px;
    border: 1px solid transparent;
}

/* ---------------------------------------------------------
   OPTION PILLS — compact radio-button-style toggle group
   --------------------------------------------------------- */
.option-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}

.option-pill {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    border-radius: 8px;
    padding: 7px 14px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: var(--transition-smooth);
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    font-family: inherit;
}

.option-pill:hover {
    background: rgba(var(--accent-primary-rgb), 0.08);
    border-color: rgba(var(--accent-primary-rgb), 0.30);
    color: var(--text-main);
}

.option-pill.active {
    background: rgba(var(--accent-primary-rgb), 0.14);
    border-color: rgba(var(--accent-primary-rgb), 0.50);
    color: var(--accent-primary);
    box-shadow: 0 0 10px -3px rgba(var(--accent-primary-rgb), 0.35);
}

.pill-hint {
    font-size: 10px;
    opacity: 0.65;
    font-weight: 400;
}

/* Light theme overrides for pills */
body.theme-daylight .option-pill {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.10);
}

body.theme-daylight .option-pill.active {
    background: rgba(var(--accent-primary-rgb), 0.10);
    border-color: rgba(var(--accent-primary-rgb), 0.40);
}

/* ---------------------------------------------------------
   WIDGET TYPE PICKER (visual chart-type selector in modal)
   --------------------------------------------------------- */
.widget-type-picker {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 7px;
    margin-top: 8px;
}

.widget-type-section-label {
    grid-column: 1 / -1;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    color: var(--text-muted);
    margin: 6px 0 2px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--border-color);
}

html:lang(en) .widget-type-section-label {
    letter-spacing: 0.8px;
}

.wt-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 9px 4px 7px;
    border-radius: 10px;
    border: 1px solid var(--border-color);
    background: rgba(255, 255, 255, 0.025);
    cursor: pointer;
    transition: var(--transition-smooth);
    color: var(--text-muted);
    user-select: none;
}

.wt-card:hover {
    background: rgba(var(--accent-primary-rgb), 0.07);
    border-color: rgba(var(--accent-primary-rgb), 0.28);
    color: var(--text-main);
}

.wt-card.active {
    background: rgba(var(--accent-primary-rgb), 0.13);
    border-color: rgba(var(--accent-primary-rgb), 0.50);
    color: var(--accent-primary);
    box-shadow: 0 0 10px -3px rgba(var(--accent-primary-rgb), 0.35);
}

/* A type the current source cannot feed (_syncWidgetTypeAvailability in
   widgets.js). Greyed rather than hidden: the card stays findable, and its
   title says what the query would have to return to unlock it. */
.wt-card.wt-card-unfit {
    opacity: 0.45;
    filter: grayscale(1);
    cursor: not-allowed;
    /* Opacity alone is not enough to read: an enabled card's label is
       already --text-muted, so a faded one looks much like it. The dashed
       edge and dropped fill are what actually separate the two at a
       glance, and they survive every theme and grayscale printing. */
    background: transparent;
    border-style: dashed;
}

.wt-card.wt-card-unfit:hover {
    background: rgba(255, 255, 255, 0.025);
    border-color: var(--border-color);
    color: var(--text-muted);
}

/* The selected card is never disabled -- a widget whose saved type no longer
   fits its own query must still be visible and re-selectable -- so it keeps
   full contrast and takes a warning edge instead of the grey. */
.wt-card.wt-card-unfit.active {
    opacity: 1;
    filter: none;
    cursor: default;
    border-style: solid;
    background: rgba(var(--accent-amber-rgb), 0.10);
    border-color: rgba(var(--accent-amber-rgb), 0.55);
    color: var(--accent-amber);
    box-shadow: none;
}

/* A type withheld from the picker (HIDDEN_WIDGET_TYPES in widgets.js). Needs
   !important: the filter-as-you-type in main.js drives `style.display` on every
   card inline, and clearing the filter resets it to "" -- which would otherwise
   bring these back. */
.wt-type-hidden {
    display: none !important;
}

.widget-type-unfit-note {
    margin: 8px 0 0;
    font-size: 11px;
    line-height: 1.45;
    color: var(--accent-amber);
}

.wt-card svg {
    width: 40px;
    height: 28px;
    flex-shrink: 0;
}

.wt-card-label {
    font-size: 10px;
    font-weight: 500;
    line-height: 1.2;
    text-align: center;
    white-space: nowrap;
}

/* ── Chart Style Toggle (2D / 2.5D) ───────────────────────────────────── */
.chart-style-toggle {
    display: flex;
    gap: 10px;
    margin-top: 6px;
}

.style-opt {
    flex: 1;
    cursor: pointer;
}

.style-opt input[type="radio"] {
    display: none;
}

.style-opt-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px 8px 8px;
    border-radius: 10px;
    border: 1.5px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 500;
    transition: border-color 0.18s, background 0.18s, color 0.18s;
    user-select: none;
}

.style-opt-inner svg {
    transition: opacity 0.18s;
    opacity: 0.55;
}

.style-opt input[type="radio"]:checked+.style-opt-inner {
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.10);
    color: var(--accent-primary);
}

.style-opt input[type="radio"]:checked+.style-opt-inner svg {
    opacity: 1;
}

.style-opt-inner:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.4);
    background: rgba(var(--accent-primary-rgb), 0.05);
}

/* ── Column Mapping Panel ───────────────────────────────────────────────── */
.col-mapping-panel {
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: 12px;
    padding: 16px 18px 14px;
    margin-bottom: 18px;
}

.col-mapping-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.col-mapping-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.col-mapping-title i {
    color: var(--accent-primary);
}

.col-mapping-badge {
    font-size: 10px;
    font-weight: 600;
    background: rgba(var(--accent-primary-rgb), 0.12);
    color: var(--accent-primary);
    padding: 2px 7px;
    border-radius: 10px;
    letter-spacing: 0.3px;
}

.col-mapping-actions {
    display: flex;
    gap: 8px;
}

.btn-xs {
    padding: 4px 10px;
    font-size: 11px;
    border-radius: 4px;
    height: auto;
    line-height: 1.4;
}

.btn-ghost {
    /* Was rgba(255,255,255,0.05/0.1) -- a white tint, so on a light theme the
       button and its border sat within 1.02:1 of the surface behind them and
       Reset/Views read as disabled. The fill is now genuinely transparent (any
       accent tint here pushed Dracula's --text-secondary under 4.5:1, and a
       ghost button is meant to be unfilled anyway); the accent-derived border
       is what makes it visible, on light and dark alike. */
    background: transparent;
    border: 1px solid rgba(var(--accent-primary-rgb), 0.45);
    color: var(--text-secondary);
    transition: background 0.15s, color 0.15s, border-color 0.15s;
}

.btn-ghost:hover {
    background: rgba(var(--accent-primary-rgb), 0.1);
    border-color: var(--accent-primary) !important;
    color: var(--accent-primary);
}

.col-mapping-table-head {
    display: grid;
    grid-template-columns: 1.15fr 20px 1fr 100px 150px 28px;
    gap: 6px;
    align-items: center;
    font-size: 10px;
    font-weight: 600;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 0 2px 6px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    margin-bottom: 8px;
}

.col-mapping-row {
    display: grid;
    grid-template-columns: 1.15fr 20px 1fr 100px 150px 28px;
    gap: 6px;
    align-items: center;
    margin-bottom: 6px;
}

.col-mapping-row input[type="text"] {
    height: 34px;
    font-size: 12px;
    padding: 0 10px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: var(--text-primary);
    width: 100%;
    box-sizing: border-box;
    transition: border-color 0.15s;
    font-family: var(--font-mono);
}

.col-mapping-row input[type="text"]:focus {
    outline: none;
    border-color: rgba(var(--accent-primary-rgb), 0.5);
}

.col-mapping-row select {
    height: 34px;
    font-size: 11px;
    padding: 0 6px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: var(--text-primary);
    width: 100%;
    box-sizing: border-box;
}

.col-mapping-row select:focus {
    outline: none;
    border-color: rgba(var(--accent-primary-rgb), 0.5);
}

.col-mapping-row select:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

.col-mapping-row input.local-col {
    font-family: inherit;
}

.col-mapping-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent-primary);
    font-size: 13px;
    opacity: 0.7;
}

.col-mapping-remove {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 4px;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    font-size: 12px;
    padding: 0;
}

.col-mapping-remove:hover {
    background: rgba(var(--accent-red-rgb, 244, 63, 94), 0.12);
    color: var(--accent-red, #F43F5E);
}

.col-mapping-empty {
    font-size: 12px;
    color: var(--text-muted);
    text-align: center;
    padding: 10px 0 4px;
    font-style: italic;
}

code {
    font-family: var(--font-mono);
    font-size: 11px;
    background: rgba(255, 255, 255, 0.07);
    padding: 1px 5px;
    border-radius: 4px;
    color: var(--accent-primary);
}

/* light theme overrides */
body.theme-daylight .col-mapping-panel {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.08);
}

body.theme-daylight .col-mapping-row input[type="text"],
body.theme-daylight .col-mapping-row select {
    background: rgba(255, 255, 255, 0.7);
    border-color: rgba(0, 0, 0, 0.12);
    color: #1E293B;
}

/* ── Sync Schedule Panel ────────────────────────────────────────────────── */
.sync-schedule-panel {
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: 12px;
    padding: 18px 20px 16px;
    margin-bottom: 4px;
}

.sync-schedule-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}

.sync-schedule-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.sync-schedule-title i {
    color: var(--accent-primary);
}

.sync-header-controls {
    display: flex;
    align-items: center;
}

/* Toggle switch */
.sync-toggle-label {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    user-select: none;
}

.sync-enable-checkbox {
    display: none;
}

.sync-toggle-track {
    position: relative;
    width: 36px;
    height: 20px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    transition: background 0.2s;
    flex-shrink: 0;
}

.sync-toggle-thumb {
    position: absolute;
    top: 3px;
    inset-inline-start: 3px;
    width: 14px;
    height: 14px;
    background: var(--text-muted);
    border-radius: 50%;
    transition: inset-inline-start 0.2s, background 0.2s;
}

.sync-enable-checkbox:checked+.sync-toggle-track {
    background: rgba(var(--accent-primary-rgb), 0.35);
    border-color: var(--accent-primary);
}

.sync-enable-checkbox:checked+.sync-toggle-track .sync-toggle-thumb {
    inset-inline-start: 19px;
    background: var(--accent-primary);
}

.sync-toggle-text {
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 500;
}

/* Frequency select: dimmed while auto-sync is off (the old 9-card grid was
   distilled to this select; the time row below keeps its own dimming) */
.sync-schedule-panel.sync-disabled #report-sync-frequency {
    opacity: 0.4;
    pointer-events: none;
}

.sync-schedule-panel.sync-disabled .sync-time-row {
    opacity: 0.4;
    pointer-events: none;
}

/* Time picker row */
.sync-time-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    /* lets .sync-time-hint drop to its own line */
    gap: 4px 10px;
    background: rgba(var(--accent-primary-rgb), 0.06);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.18);
    border-radius: 8px;
    padding: 8px 12px;
    margin-bottom: 10px;
}

.sync-time-row label {
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 500;
    margin: 0;
    white-space: nowrap;
    flex: 0 0 auto;
    /* never let the label be squeezed out of the row */
}

.sync-time-input {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 8px;
    color: var(--text-primary);
    font-size: 13px;
    padding: 4px 8px;
    width: 110px;
    color-scheme: dark;
}

/* The hint gets its own line rather than competing with the label and the
   time input for horizontal space. Inline, it was long enough to wrap to two
   lines inside the bordered row -- which is what made the panel look broken --
   and it squeezed the "Start time" label at narrower modal widths. Arabic and
   the frequency-specific variants are longer still, so this is structural
   rather than a matter of trimming the string. */
.sync-time-hint {
    font-size: 11px;
    color: var(--text-muted);
    flex: 1 0 100%;
    margin-top: 2px;
    line-height: 1.45;
}

/* Next-run preview */

/* Global sync info row */
.sync-global-info {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-secondary);
    background: rgba(var(--accent-indigo-rgb, 139, 92, 246), 0.07);
    border: 1px solid rgba(var(--accent-indigo-rgb, 139, 92, 246), 0.18);
    border-radius: 8px;
    padding: 8px 12px;
    margin-bottom: 10px;
}

.sync-global-info i {
    color: var(--accent-indigo, #8B5CF6);
}

.sync-global-info strong {
    color: var(--text-primary);
}

.sync-global-link {
    margin-inline-start: auto;
    font-size: 11px;
    color: var(--accent-primary);
    text-decoration: none;
    white-space: nowrap;
}

.sync-global-link:hover {
    text-decoration: underline;
}

.sync-next-run {
    font-size: 11px;
    color: var(--text-muted);
    text-align: center;
    min-height: 18px;
    transition: opacity 0.2s;
}

.sync-next-run.has-value {
    color: var(--accent-primary);
}

.sync-next-run i {
    margin-inline-end: 4px;
}

/* Preview states. `.has-value` is the ordinary "here is when it runs next"
   accent; these three override it for the cases that are not good news. */
.sync-next-run.sync-next-run-muted,
.sync-next-run.sync-next-run-muted.has-value {
    color: var(--text-muted);
}

.sync-next-run.sync-next-run-warn,
.sync-next-run.sync-next-run-warn.has-value {
    color: var(--accent-warning, #e0a030);
}

/* Timezone tag, shown only when the browser and the server disagree about
   what time it is -- otherwise it is noise. */
.sync-next-run-tz {
    display: inline-block;
    margin-inline-start: 6px;
    padding: 1px 6px;
    border-radius: 8px;
    font-size: 10px;
    color: var(--text-muted);
    background: rgba(127, 127, 127, 0.14);
    white-space: nowrap;
}

/* "Inherit" pointing at an unset global interval is a dead schedule. */
.sync-global-unset {
    color: var(--accent-warning, #e0a030);
    font-weight: 600;
}

/* light theme overrides */
body.theme-daylight .sync-schedule-panel {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.08);
}


body.theme-daylight .sync-time-input {
    color-scheme: light;
    background: #fff;
    color: #1E293B;
}

.wt-badge-3d {
    font-size: 8px;
    font-weight: 700;
    background: rgba(var(--accent-primary-rgb), 0.18);
    color: var(--accent-primary);
    padding: 1px 5px;
    border-radius: 4px;
    letter-spacing: 0.4px;
    margin-top: 1px;
}

/* light theme picker overrides */
body.theme-daylight .wt-card {
    background: rgba(0, 0, 0, 0.02);
}

body.theme-daylight .wt-card:hover {
    background: rgba(var(--accent-primary-rgb), 0.06);
}


/* ---------------------------------------------------------
   CACHE STATUS BADGE
   --------------------------------------------------------- */
.cache-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border-radius: 9999px;
    font-size: 11px;
    font-weight: 500;
    white-space: nowrap;
    transition: var(--transition-smooth);
    border: 1px solid transparent;
}

.cache-badge.cache-hit {
    background: rgba(var(--accent-primary-rgb), 0.10);
    border-color: rgba(var(--accent-primary-rgb), 0.25);
    color: var(--accent-primary);
}

.cache-badge.cache-miss {
    background: rgba(255, 255, 255, 0.05);
    border-color: var(--border-color);
    color: var(--text-muted);
}

.cache-badge i {
    font-size: 10px;
}

/* ---------------------------------------------------------
   MIGRATION LOG CONSOLE
   --------------------------------------------------------- */
.migration-log-console {
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 15px;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--accent-primary);
    max-height: 250px;
    overflow-y: auto;
    white-space: pre-wrap;
    margin-top: 15px;
    box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.8);
    display: block;
}

.migration-log-console.error {
    color: var(--accent-red);
}

/* ---------------------------------------------------------
   DASHBOARD PARAMETERS BAR STYLING
   --------------------------------------------------------- */
.dashboard-parameters-bar {
    background-color: var(--chrome-tint-soft);
    background-image: var(--panel-gradient);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px 20px;
    display: flex;
    align-items: center;
    gap: 20px;
    flex-wrap: nowrap;
    /* --card-shadow, not a hardcoded black one, which was heavy on light themes. */
    box-shadow: var(--card-shadow);
    transition: var(--transition-smooth);
}

.dashboard-parameters-bar.hidden {
    display: none !important;
}

.parameters-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.parameters-list {
    display: flex;
    flex-wrap: wrap;
    gap: 15px 20px;
    align-items: center;
    flex-grow: 1;
}

.parameter-item {
    display: flex;
    align-items: center;
    gap: 8px;
}

.parameter-item label {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-main);
    white-space: nowrap;
}

.parameter-input {
    padding: 6px 12px;
    font-size: 13px;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background: rgba(0, 0, 0, 0.25);
    color: var(--text-main);
    outline: none;
    transition: var(--transition-smooth);
    min-width: 140px;
    height: 32px;
}

.parameter-input:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.5);
    background: rgba(0, 0, 0, 0.35);
}

.parameter-input:focus {
    border-color: var(--accent-primary);
    background: rgba(0, 0, 0, 0.4);
    box-shadow: 0 0 8px rgba(var(--accent-primary-rgb), 0.25);
}

/* Parameter editor textareas styled via classes instead of inline styles. */
.param-options-textarea,
.param-sql-textarea {
    width: 100%;
    resize: vertical;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 8px;
    padding: 10px 12px;
    color: var(--text-primary);
    font-size: 13px;
    font-family: inherit;
}

.param-sql-textarea {
    font-size: 12px;
    font-family: var(--font-mono);
}

.param-hint {
    color: var(--text-muted);
    font-weight: 400;
}

.param-form-tip {
    font-size: 11px;
    color: var(--text-muted);
    margin-top: 4px;
    display: block;
}

.param-form-tip i {
    margin-inline-end: 4px;
}

.param-form-tip code {
    color: var(--accent-primary);
}

.param-form-actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
    align-items: center;
}

.param-form-actions .btn-primary {
    gap: 7px;
}

.parameters-actions {
    margin-inline-start: auto;
    flex-shrink: 0;
}

/* ---------------------------------------------------------
   USER AUTHENTICATION (LOGIN SCREEN)
   --------------------------------------------------------- */
.login-container {
    position: fixed;
    top: 0;
    inset-inline-start: 0;
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    background: radial-gradient(circle at 50% 50%, var(--radial-color-1) 0%, var(--radial-color-2) 40%, var(--bg-base) 100%);
    z-index: 400;
    backdrop-filter: blur(15px);
    -webkit-backdrop-filter: blur(15px);
}

.login-card {
    width: 100%;
    max-width: 400px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 16px;
    padding: 40px;
    animation: loginFadeIn 0.5s ease-out;
    position: relative;
}

/* Compact language switch — used on the login card (top corner) and in the topbar */
.lang-switch {
    display: inline-flex;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 2px;
    gap: 2px;
    flex-shrink: 0;
}

.login-card .lang-switch {
    position: absolute;
    top: 16px;
    inset-inline-end: 16px;
}

.lang-switch-btn {
    border: none;
    background: transparent;
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.03em;
    padding: 5px 10px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.lang-switch-btn:hover {
    color: var(--text-main);
}

.lang-switch-btn.active {
    background: var(--accent-primary);
    color: var(--text-on-accent, #ffffff);
}

@keyframes loginFadeIn {
    from {
        opacity: 0;
        transform: scale(0.95) translateY(10px);
    }

    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

.login-header {
    text-align: center;
    margin-bottom: 30px;
}

.login-logo-icon {
    font-size: 42px;
    color: var(--accent-primary);
    margin-bottom: 12px;
    filter: drop-shadow(0 0 10px rgba(var(--accent-primary-rgb), 0.3));
}

.login-header h2 {
    font-family: var(--font-primary);
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0;
}

.login-header .sub-brand {
    font-size: 13px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 1.5px;
    margin-top: 4px;
    display: inline-block;
}

.login-error {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 25%, transparent);
    color: var(--accent-red);
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 13px;
    margin-top: 15px;
    margin-bottom: 15px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.login-error.hidden {
    display: none !important;
}

/* Generic inline form feedback (success/error), used by self-service forms
   like Change Password -- same visual language as .login-error but with a
   success variant too. */
.form-feedback {
    border-radius: 8px;
    padding: 10px 14px;
    font-size: 13px;
    margin-top: 10px;
}

.form-feedback.hidden {
    display: none !important;
}

.form-feedback.error {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 25%, transparent);
    color: var(--accent-red);
}

.form-feedback.success {
    background: rgba(16, 185, 129, 0.1);
    border: 1px solid rgba(16, 185, 129, 0.25);
    color: var(--accent-emerald);
}

/* Login form inputs: keep them legible against the glass card without inline styles. */
.login-card .form-group {
    margin-bottom: 15px;
}

.login-card .form-group label {
    display: block;
    margin-bottom: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-main);
}

.login-card input[type="text"],
.login-card input[type="password"] {
    width: 100%;
    padding: 10px 12px;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background: rgba(0, 0, 0, 0.20);
    color: var(--text-primary);
}

/* Login field focus: .login-card input out-specifies the generic
   input:focus rule, so restore the system's accent border + glow on
   focus to keep the login fields consistent with the rest of the app. */
.login-card input[type="text"]:focus,
.login-card input[type="password"]:focus {
    border-color: var(--accent-primary);
    box-shadow: 0 0 10px -3px rgba(var(--accent-primary-rgb), 0.4);
}

.login-card .btn-primary {
    width: 100%;
    justify-content: center;
    margin-top: 10px;
}

/* User profile sidebar badge */
.user-profile-badge {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    margin-top: 15px;
}

.user-profile-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: rgba(var(--accent-primary-rgb), 0.15);
    border: 1.5px solid var(--accent-primary);
    color: var(--accent-primary);
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
    font-weight: 600;
    flex-shrink: 0;
    /* position: relative anchors .has-alerts::after below. Set unconditionally
       so the dot cannot shift the avatar's layout when it appears. */
    position: relative;
}

/* Active-alert indicator. The Exceptions count badge lives inside the account
   menu, which is closed by default -- this surfaces "something is firing" on
   the collapsed badge. Toggled by _refreshExceptionsBadge() in alerts.js. */
.user-profile-avatar.has-alerts::after {
    content: "";
    position: absolute;
    inset-block-start: -2px;
    inset-inline-end: -2px;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--accent-red);
    /* Ring in the top-bar surface so the dot stays legible against the
       avatar's own accent-tinted fill on every theme. */
    box-shadow: 0 0 0 2px var(--bg-base);
}

.user-profile-info {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    flex-grow: 1;
}

.user-profile-name {
    font-size: var(--type-body);
    font-weight: 600;
    color: var(--text-primary);
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}

.user-profile-role {
    font-size: var(--type-meta);
    color: var(--text-muted);
    text-transform: capitalize;
}

/* User management sub-tab forms and elements */
.user-management-layout {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 15px;
}

.user-management-layout>.settings-group {
    flex: 1 1 320px;
    margin-bottom: 0;
}

.user-management-layout>.settings-group:first-child {
    flex: 1.2 1 480px;
}

/* Theme-aware inset panel background (matches .settings-group/.sync-card's
   default tone) -- this used to be a hardcoded black overlay that read as a
   plain gray box on light themes; it's added to the same per-theme override
   selector groups as .settings-group below so it follows every theme. */
.user-assign-list {
    max-height: 280px;
    overflow-y: auto;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 8px 10px;
    background: rgba(12, 18, 34, 0.65);
}

.user-assign-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 6px;
    border-radius: 6px;
    border-bottom: 1px solid var(--border-color);
}

.user-assign-item:hover {
    background: rgba(255, 255, 255, 0.04);
}

.user-assign-item:last-child {
    border-bottom: none;
}

.user-assign-item label {
    cursor: pointer;
    font-size: 13px;
    color: var(--text-main);
    flex-grow: 1;
}

.user-assign-item input[type="checkbox"] {
    width: 16px;
    height: 16px;
    cursor: pointer;
    flex-shrink: 0;
}

/* Category tree (mirrors the sidebar dashboard picker's grouping --
   see .sidebar-db-category-header). Uncategorized dashboards render as
   plain .user-assign-item rows at the top level; categorized ones nest
   under a collapsible header with its own select-all checkbox. */
.user-assign-category {
    margin-block-end: 4px;
}

.user-assign-category-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 6px;
    border-radius: 6px;
    cursor: pointer;
    user-select: none;
    transition: var(--transition-smooth);
}

.user-assign-category-header:hover {
    background: rgba(255, 255, 255, 0.05);
}

.user-assign-category-header:hover .category-label {
    color: var(--text-main);
}

.user-assign-category-header .category-chevron {
    font-size: 9px;
    color: var(--text-muted);
    transition: transform 0.15s ease;
    flex-shrink: 0;
}

.user-assign-category-header .category-chevron.expanded {
    transform: rotate(90deg);
}

.user-assign-category-header .user-assign-category-chk {
    width: 15px;
    height: 15px;
    cursor: pointer;
    flex-shrink: 0;
}

/* Explicit color/weight/size here (not just inherited from the header) so
   this reliably reads as a muted group heading -- matching the sidebar's
   .sidebar-db-category-header treatment -- rather than competing visually
   with the actual dashboard names listed underneath it. */
.user-assign-category-header .category-label {
    flex-grow: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.user-assign-category-header .category-count {
    font-size: 10px;
    font-weight: 500;
    color: var(--text-muted);
    opacity: 0.6;
}

/* Separate from .user-assign-category-chk (bulk-check convenience): checking
   this persists an actual category_assignments row, covering dashboards
   added to the category later too. */
.user-category-assign-label {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    color: var(--text-muted);
    flex-shrink: 0;
}

.user-category-assign-label input[type="checkbox"] {
    width: 15px;
    height: 15px;
    cursor: pointer;
}

.user-category-assign-label:has(input:checked) {
    color: var(--accent-amber);
}

.user-assign-item-nested {
    padding-inline-start: 22px;
    position: relative;
}

.user-assign-item-nested::before {
    content: "";
    position: absolute;
    inset-inline-start: 11px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--border-color);
    opacity: 0.6;
}

/* ==========================================================================
   DASHBOARD SEARCHABLE SELECT & PINNING STYLES
   ========================================================================== */

.sidebar-db-dropdown {
    display: flex;
    position: static;
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 8px;
    flex-direction: column;
    gap: 8px;
    width: 100%;
    flex: 1 1 auto;
    min-height: 0;
    margin-bottom: 10px;
}

.sidebar-db-search-container {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
}

.sidebar-db-search-container .search-icon {
    position: absolute;
    inset-inline-start: 10px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 12px;
    color: var(--text-muted);
    pointer-events: none;
}

.sidebar-db-search-container .sidebar-db-search-input {
    width: 100%;
    padding-block: 8px;
    padding-inline: 28px 30px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    color: var(--text-main);
    font-size: 12px;
    outline: none;
    transition: var(--transition-smooth);
}

.sidebar-db-search-container .sidebar-db-search-input:focus {
    border-color: rgba(var(--accent-primary-rgb), 0.5);
    background: rgba(255, 255, 255, 0.08);
}

.sidebar-db-search-container .search-clear-btn {
    position: absolute;
    inset-inline-end: 10px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px;
    font-size: 11px;
    line-height: 1;
}

.sidebar-db-search-container .search-clear-btn:hover {
    color: var(--text-main);
}

.sidebar-db-options-list {
    flex: 1 1 auto;
    min-height: 80px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-inline-end: 2px;
}

.sidebar-db-options-list::-webkit-scrollbar {
    width: 4px;
}

.sidebar-db-options-list::-webkit-scrollbar-track {
    background: transparent;
}

.sidebar-db-options-list::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 2px;
}

.sidebar-db-options-list::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.20);
}

.sidebar-db-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 8px 10px;
    border-radius: 8px;
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: var(--transition-smooth);
    color: var(--text-muted);
    font-size: 12px;
    text-align: start;
}

.sidebar-db-option:hover {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-main);
}

.sidebar-db-option.active {
    background: rgba(var(--accent-primary-rgb), 0.1);
    border-color: rgba(var(--accent-primary-rgb), 0.25);
    color: var(--text-main);
}

.sidebar-db-option .option-content {
    display: flex;
    align-items: center;
    gap: 8px;
    overflow: hidden;
    flex-grow: 1;
}

.sidebar-db-option .option-label {
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.sidebar-db-option .favorite-star-btn {
    flex-shrink: 0;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 4px;
    color: var(--accent-amber);
    opacity: 0;
    transition: var(--transition-smooth);
}

.sidebar-db-option:hover .favorite-star-btn,
.sidebar-db-option.active .favorite-star-btn,
.sidebar-db-option .favorite-star-btn.is-favorite {
    opacity: 1;
}

.sidebar-db-option .home-dashboard-btn {
    flex-shrink: 0;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 4px;
    color: var(--accent-primary);
    opacity: 0;
    transition: var(--transition-smooth);
}

.sidebar-db-option:hover .home-dashboard-btn,
.sidebar-db-option.active .home-dashboard-btn,
.sidebar-db-option .home-dashboard-btn.is-home {
    opacity: 1;
}

/* Reorder arrows (admin only, injected by _buildDashboardOptionEl). Same
   hover-reveal treatment as the star/home buttons above. */
.sidebar-db-option .db-move-btn {
    flex-shrink: 0;
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 4px 2px;
    font-size: 10px;
    color: var(--text-muted);
    opacity: 0;
    transition: var(--transition-smooth);
}

.sidebar-db-option:hover .db-move-btn,
.sidebar-db-option.active .db-move-btn {
    opacity: 1;
}

.sidebar-db-option .db-move-btn:hover {
    color: var(--accent-primary);
}

.sidebar-db-option .db-move-btn:disabled {
    opacity: 0.2;
    cursor: default;
}

.sidebar-db-favorites-header {
    color: var(--accent-amber);
}

/* Dashboard picker tree: category (folder) headers + nested child rows.
   Uses the same CSS custom properties every theme already defines, so no
   additional per-theme override blocks are needed. */
.sidebar-db-category-header {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 6px 10px;
    border-radius: 8px;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    transition: var(--transition-smooth);
    user-select: none;
}

.sidebar-db-category-header:hover {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-main);
}

.sidebar-db-category-header .category-chevron {
    font-size: 9px;
    transition: transform 0.15s ease;
    flex-shrink: 0;
}

.sidebar-db-category-header .category-chevron.expanded {
    transform: rotate(90deg);
}

.sidebar-db-category-header .category-label {
    flex-grow: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-db-category-header .category-count {
    font-size: 10px;
    font-weight: 500;
    opacity: 0.6;
    text-transform: none;
    letter-spacing: normal;
}

.sidebar-db-option.nested {
    padding-inline-start: 28px;
    position: relative;
}

.sidebar-db-option.nested::before {
    content: "";
    position: absolute;
    inset-inline-start: 15px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--border-color);
    opacity: 0.6;
}

.list-empty-text {
    font-size: 11px;
    color: var(--text-muted);
    font-style: italic;
    padding: 8px 12px;
    text-align: center;
}

/* Collapsed sidebar specifics */
.app-sidebar.collapsed #sidebar-db-dropdown {
    display: none;
}

/* ---------------------------------------------------------
   USER PREFERENCES MODAL
   --------------------------------------------------------- */
.user-profile-badge--clickable {
    cursor: pointer;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 10px;
    padding: 4px 10px 4px 4px;
    transition: background 0.18s, border-color 0.18s;
    text-align: start;
}

.user-profile-badge--clickable:hover {
    background: rgba(var(--accent-primary-rgb), 0.08);
    border-color: rgba(var(--accent-primary-rgb), 0.25);
}

.user-prefs-hint-icon {
    font-size: 12px;
    color: var(--text-muted);
    margin-inline-start: 4px;
    transition: color 0.18s;
}

.user-profile-badge--clickable:hover .user-prefs-hint-icon {
    color: var(--accent-primary);
}

.modal-xlarge {
    max-width: 900px;
    width: 95vw;
}

.user-prefs-modal-body {
    max-height: 72vh;
    display: flex;
    align-items: stretch;
    padding: 0;
    overflow: hidden;
}

.user-prefs-modal-card .modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

/* ==========================================================================
   ACCESSIBILITY & MOTION
   ========================================================================== */

/* Subtle focus indicator for keyboard users that does not fight custom focus states. */
:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* Sync reports: card view for <=900px. The table is hidden; the
   #sync-reports-cards container (populated by sync_settings.js) is shown. */
.sync-reports-cards {
    display: none;
    flex-direction: column;
    gap: 10px;
    margin-top: 16px;
}

@media (max-width: 1024px) {
    .sync-reports-cards {
        display: flex;
    }

    .sync-card.summary-card table {
        display: none;
    }
}

.sync-report-card {
    background: var(--bg-elevated, var(--bg-surface));
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 14px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    animation: syncCardIn 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
}

@keyframes syncCardIn {
    from {
        opacity: 0;
        transform: translateY(4px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

.sync-report-card .sync-report-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.sync-report-card .sync-report-card-name {
    font-weight: 600;
    color: var(--text-main);
    font-size: 14px;
    word-break: break-word;
}

.sync-report-card .sync-report-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
    color: var(--text-secondary);
    font-size: 12px;
}

.sync-report-card .sync-report-card-meta code {
    color: var(--text-secondary);
    font-size: 11px;
}

.sync-report-card .sync-report-card-schedule {
    color: var(--text-muted);
    font-size: 11px;
}

.sync-report-card .sync-report-card-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
}

.sync-report-card .sync-report-card-actions .btn {
    flex: 1;
    justify-content: center;
}

/* First-run onboarding overlay */
.onboarding-card {
    max-width: 520px;
    padding: 28px 28px 20px;
    text-align: center;
}

.onboarding-progress {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 18px;
}

.onboarding-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--border-color);
    transition: background 0.2s cubic-bezier(0.4, 0, 0.2, 1), transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.onboarding-dot.active {
    background: var(--accent-primary);
    transform: scale(1.25);
}

.onboarding-icon {
    font-size: 32px;
    color: var(--accent-primary);
    margin-bottom: 8px;
}

.onboarding-step h2 {
    font-size: 20px;
    font-weight: 600;
    color: var(--text-main);
    margin: 0 0 10px;
}

.onboarding-step p {
    font-size: 13px;
    line-height: 1.55;
    color: var(--text-secondary);
    margin: 0 0 10px;
}

.onboarding-step .onboarding-tip {
    font-size: 12px;
    color: var(--text-muted);
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 10px 12px;
    margin-top: 14px;
    text-align: start;
}

.onboarding-step .onboarding-tip strong {
    color: var(--accent-primary);
    font-weight: 600;
}

.onboarding-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid var(--border-color);
}

.onboarding-nav {
    display: flex;
    gap: 8px;
}

/* "Show me how" affordance on empty states */
.show-me-how {
    display: inline-block;
    margin-inline-start: 10px;
    font-size: 12px;
    color: var(--accent-primary);
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    padding: 8px 12px;
    min-height: 36px;
    cursor: pointer;
    transition: border-color 0.2s cubic-bezier(0.4, 0, 0.2, 1), background 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}

.show-me-how:hover {
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.08);
}

/* "Brand" badge on the FusionFlow theme card */
.theme-badge {
    display: inline-block;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--text-on-accent, #ffffff);
    background: var(--accent-primary);
    padding: 1px 6px;
    border-radius: 4px;
    margin-inline-start: 6px;
    vertical-align: middle;
}

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* Headings should have balanced line breaks for a cleaner display. */
h1,
h2,
h3,
.brand-text h1,
.title-block h2,
.modal-header h3,
.settings-group h3,
.login-header h2,
.theme-card-title {
    text-wrap: balance;
}

/* Long prose should avoid orphans. */
p,
.section-desc,
.form-tip,
.empty-state p {
    text-wrap: pretty;
}

/* ---------------------------------------------------------
   LIGHT SIDEBAR THEMES OVERRIDES (DASHBOARD SELECT DROPDOWN)
   --------------------------------------------------------- */
body.theme-daylight .sidebar-db-search-input {
    background: rgba(0, 0, 0, 0.02);
    border: 1px solid rgba(0, 0, 0, 0.12);
    color: var(--text-main);
}

body.theme-daylight .sidebar-db-search-input:focus {
    background: #FFFFFF;
    border-color: var(--accent-primary);
}

body.theme-daylight .sidebar-db-option {
    color: var(--text-muted);
}

body.theme-daylight .sidebar-db-option:hover {
    background: rgba(var(--accent-primary-rgb), 0.07);
    color: var(--text-main);
}

body.theme-daylight .sidebar-db-option.active {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border-color: rgba(var(--accent-primary-rgb), 0.25);
    color: var(--text-main);
}

body.theme-daylight .sidebar-db-options-list::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.15);
}

body.theme-daylight .sidebar-db-options-list::-webkit-scrollbar-thumb:hover {
    background: rgba(0, 0, 0, 0.25);
}

/* ---------------------------------------------------------
   FUSIONFLOW (BRAND DEFAULT) DROPDOWN OVERRIDES
   --------------------------------------------------------- */
body.theme-fusionflow .sidebar-db-search-input {
    background: rgba(18, 26, 45, 0.66);
    border: 1px solid rgba(140, 170, 220, 0.15);
    color: var(--text-main);
}

body.theme-fusionflow .sidebar-db-search-input:focus {
    border-color: var(--accent-primary);
}

body.theme-fusionflow .sidebar-db-option {
    color: var(--text-secondary);
}

body.theme-fusionflow .sidebar-db-option:hover {
    background: rgba(79, 195, 247, 0.08);
    color: var(--text-main);
}

body.theme-fusionflow .sidebar-db-option.active {
    background: rgba(79, 195, 247, 0.12);
    border-color: rgba(79, 195, 247, 0.25);
    color: var(--text-main);
}

/* ---------------------------------------------------------
   NORD ARCTIC DROPDOWN OVERRIDES
   --------------------------------------------------------- */
body.theme-nord-arctic .sidebar-db-search-input {
    background: rgba(14, 18, 30, 0.70);
    border: 1px solid rgba(136, 192, 208, 0.16);
    color: var(--text-main);
}

body.theme-nord-arctic .sidebar-db-search-input:focus {
    border-color: var(--accent-primary);
}

body.theme-nord-arctic .sidebar-db-option {
    color: var(--text-secondary);
}

body.theme-nord-arctic .sidebar-db-option:hover {
    background: rgba(136, 192, 208, 0.08);
    color: var(--text-main);
}

body.theme-nord-arctic .sidebar-db-option.active {
    background: rgba(136, 192, 208, 0.12);
    border-color: rgba(136, 192, 208, 0.25);
    color: var(--text-main);
}

/* ---------------------------------------------------------
   DRACULA DROPDOWN OVERRIDES
   --------------------------------------------------------- */
body.theme-dracula .sidebar-db-search-input {
    background: rgba(33, 34, 44, 0.88);
    border: 1px solid rgba(189, 147, 249, 0.18);
    color: var(--text-main);
}

body.theme-dracula .sidebar-db-search-input:focus {
    border-color: var(--accent-indigo);
}

body.theme-dracula .sidebar-db-option {
    color: var(--text-secondary);
}

body.theme-dracula .sidebar-db-option:hover {
    background: rgba(189, 147, 249, 0.08);
    color: var(--text-main);
}

body.theme-dracula .sidebar-db-option.active {
    background: rgba(189, 147, 249, 0.12);
    border-color: rgba(189, 147, 249, 0.25);
    color: var(--text-main);
}

/* ---------------------------------------------------------
   USER PREFERENCES MODAL CLEANUP OVERRIDES
   --------------------------------------------------------- */
.modal-header-title-wrapper {
    display: flex;
    align-items: center;
    gap: 10px;
}

.settings-group-spaced {
    margin-top: 20px;
}

.font-select-group {
    max-width: 400px;
    margin-top: 15px;
}

.chart-display-group {
    margin-top: 16px;
}

.color-dot-border {
    border-color: #ccc !important;
}

/* Nord Arctic Preview */
/* FusionFlow preview swatch. This theme is both the brand theme and the
   DEFAULT dark theme, yet it had no [data-theme] preview rules at all -- the
   base .theme-mini-preview, .tmp-* and .color-dot rules set geometry only, no
   colours, so its card in the picker rendered as an empty box with four
   colourless dots. Pre-existing; found while removing the other themes.
   Values mirror the theme's own variables. */
.theme-card[data-theme="fusionflow"] .theme-mini-preview {
    background: #070B14;
}

.theme-card[data-theme="fusionflow"] .tmp-sidebar {
    background: #0B111F;
}

.theme-card[data-theme="fusionflow"] .tmp-bar:nth-child(1) {
    background: #4FC3F7;
    width: 62%;
}

.theme-card[data-theme="fusionflow"] .tmp-bar:nth-child(2) {
    background: #7C6CFF;
    width: 40%;
}

.theme-card[data-theme="fusionflow"] .tmp-kpi:nth-child(1) {
    background: #0E1526;
    border-color: rgba(79, 195, 247, 0.33);
}

.theme-card[data-theme="fusionflow"] .tmp-kpi:nth-child(2) {
    background: #0E1526;
    border-color: rgba(124, 108, 255, 0.33);
}

.theme-card[data-theme="fusionflow"] .tmp-kpi:nth-child(3) {
    background: #0E1526;
    border-color: rgba(96, 165, 250, 0.33);
}

.theme-card[data-theme="fusionflow"] .color-dot:nth-child(1) {
    background: #070B14;
}

.theme-card[data-theme="fusionflow"] .color-dot:nth-child(2) {
    background: #4FC3F7;
}

.theme-card[data-theme="fusionflow"] .color-dot:nth-child(3) {
    background: #7C6CFF;
}

.theme-card[data-theme="fusionflow"] .color-dot:nth-child(4) {
    background: #60A5FA;
}

.theme-card[data-theme="nord-arctic"] .theme-mini-preview {
    background: #0E1117;
}

.theme-card[data-theme="nord-arctic"] .tmp-sidebar {
    background: #09111C;
}

.theme-card[data-theme="nord-arctic"] .tmp-bar:nth-child(1) {
    background: #88C0D0;
    width: 62%;
}

.theme-card[data-theme="nord-arctic"] .tmp-bar:nth-child(2) {
    background: #A3BE8C;
    width: 40%;
}

.theme-card[data-theme="nord-arctic"] .tmp-kpi:nth-child(1) {
    background: #18222E;
    border-color: rgba(136, 192, 208, 0.33);
}

.theme-card[data-theme="nord-arctic"] .tmp-kpi:nth-child(2) {
    background: #18222E;
    border-color: rgba(235, 203, 139, 0.33);
}

.theme-card[data-theme="nord-arctic"] .tmp-kpi:nth-child(3) {
    background: #18222E;
    border-color: rgba(129, 161, 193, 0.33);
}

.theme-card[data-theme="nord-arctic"] .color-dot:nth-child(1) {
    background: #0E1117;
}

.theme-card[data-theme="nord-arctic"] .color-dot:nth-child(2) {
    background: #88C0D0;
}

.theme-card[data-theme="nord-arctic"] .color-dot:nth-child(3) {
    background: #A3BE8C;
}

.theme-card[data-theme="nord-arctic"] .color-dot:nth-child(4) {
    background: #EBCB8B;
}

/* Dracula Preview */
.theme-card[data-theme="dracula"] .theme-mini-preview {
    background: #282A36;
}

.theme-card[data-theme="dracula"] .tmp-sidebar {
    background: #21222C;
}

.theme-card[data-theme="dracula"] .tmp-bar:nth-child(1) {
    background: #FF79C6;
    width: 63%;
}

.theme-card[data-theme="dracula"] .tmp-bar:nth-child(2) {
    background: #BD93F9;
    width: 44%;
}

.theme-card[data-theme="dracula"] .tmp-kpi:nth-child(1) {
    background: #44475A;
    border-color: rgba(255, 121, 198, 0.33);
}

.theme-card[data-theme="dracula"] .tmp-kpi:nth-child(2) {
    background: #44475A;
    border-color: rgba(189, 147, 249, 0.33);
}

.theme-card[data-theme="dracula"] .tmp-kpi:nth-child(3) {
    background: #44475A;
    border-color: rgba(255, 121, 198, 0.33);
}

.theme-card[data-theme="dracula"] .color-dot:nth-child(1) {
    background: #282A36;
}

.theme-card[data-theme="dracula"] .color-dot:nth-child(2) {
    background: #FF79C6;
}

.theme-card[data-theme="dracula"] .color-dot:nth-child(3) {
    background: #BD93F9;
}

.theme-card[data-theme="dracula"] .color-dot:nth-child(4) {
    background: #F1FA8C;
}

/* Daylight Preview */
.theme-card[data-theme="daylight"] .theme-mini-preview {
    background: #F7F8FA;
}

.theme-card[data-theme="daylight"] .tmp-sidebar {
    background: #EEF2F7;
}

.theme-card[data-theme="daylight"] .tmp-bar:nth-child(1) {
    background: #1E40AF;
    width: 62%;
}

.theme-card[data-theme="daylight"] .tmp-bar:nth-child(2) {
    background: #0369A1;
    width: 40%;
}

.theme-card[data-theme="daylight"] .tmp-kpi:nth-child(1) {
    background: #fff;
    border-color: rgba(30, 64, 175, 0.25);
}

.theme-card[data-theme="daylight"] .tmp-kpi:nth-child(2) {
    background: #fff;
    border-color: rgba(3, 105, 161, 0.25);
}

.theme-card[data-theme="daylight"] .tmp-kpi:nth-child(3) {
    background: #fff;
    border-color: rgba(67, 56, 202, 0.25);
}

.theme-card[data-theme="daylight"] .color-dot:nth-child(1) {
    background: #F7F8FA;
}

.theme-card[data-theme="daylight"] .color-dot:nth-child(2) {
    background: #1E40AF;
}

.theme-card[data-theme="daylight"] .color-dot:nth-child(3) {
    background: #0369A1;
}

.theme-card[data-theme="daylight"] .color-dot:nth-child(4) {
    background: #4338CA;
}

/* ---------------------------------------------------------
   Dataset Designer (Plan 002 Step 6)
   Logical properties throughout; physical identifiers/SQL/code stay dir="ltr".
--------------------------------------------------------- */
.sr-only {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.dd-ltr-identifier {
    font-family: var(--font-mono);
    font-size: 12px;
    unicode-bidi: isolate;
}

.dd-list-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    flex-wrap: wrap;
    margin-block-end: 16px;
}

.dd-search-row {
    margin-block-end: 12px;
}

.dd-search-input {
    inline-size: 100%;
    max-inline-size: 360px;
    padding: 8px 12px;
    border-radius: 10px;
    border: 1px solid var(--border-color);
    background: var(--bg-base);
    color: var(--text-main);
}

.dd-status-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 8px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.dd-status-draft {
    background: rgba(var(--accent-indigo-rgb), 0.16);
    color: var(--accent-indigo);
}

.dd-status-active {
    background: rgba(var(--accent-primary-rgb), 0.16);
    color: var(--accent-primary);
}

.dd-dirty-indicator {
    font-size: 11px;
    color: var(--text-muted);
    font-style: italic;
}

.dd-empty-state,
.dd-error-state {
    text-align: center;
    padding: 48px 16px;
    color: var(--text-muted);
}

.dd-empty-state i,
.dd-error-state i {
    font-size: 28px;
    margin-block-end: 12px;
    display: block;
}

.dd-empty-state i {
    color: var(--accent-primary);
}

.dd-error-state i {
    color: var(--accent-red);
}

.dd-skeleton-row {
    block-size: 40px;
    border-radius: 10px;
    margin-block-end: 8px;
    background: linear-gradient(90deg, var(--bg-surface) 25%, var(--border-color) 50%, var(--bg-surface) 75%);
    background-size: 200% 100%;
    animation: dd-skeleton-shimmer 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .dd-skeleton-row {
        animation: none;
    }
}

@keyframes dd-skeleton-shimmer {
    0% {
        background-position: 200% 0;
    }

    100% {
        background-position: -200% 0;
    }
}

.dd-toolbar {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    padding-block-end: 12px;
    border-block-end: 1px solid var(--border-color);
    margin-block-end: 16px;
}

.dd-toolbar-title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 auto;
    min-inline-size: 200px;
}

.dd-name-input {
    font-size: 15px;
    font-weight: 600;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 4px 8px;
    color: var(--text-main);
}

.dd-name-input:focus {
    border-color: var(--accent-primary);
    outline: none;
}

.dd-toolbar-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.dd-validation-summary,
.dd-publish-success,
.dd-dependency-error {
    border-radius: 10px;
    padding: 12px 16px;
    margin-block-end: 16px;
}

.dd-validation-summary.dd-validation-ok {
    background: rgba(var(--accent-primary-rgb), 0.1);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.3);
}

.dd-validation-summary.dd-validation-fail {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 35%, transparent);
}

.dd-validation-summary-header {
    font-weight: 600;
    margin-block-end: 6px;
}

.dd-validation-summary ul {
    margin: 0;
    padding-inline-start: 20px;
}

.dd-validation-error {
    color: var(--accent-red);
}

.dd-validation-warning {
    color: var(--text-secondary);
}

.dd-publish-success {
    background: rgba(var(--accent-primary-rgb), 0.12);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.3);
}

.dd-dependency-error {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 35%, transparent);
    color: var(--accent-red);
}

.dd-editor-grid {
    display: grid;
    grid-template-columns: 240px 1fr 280px;
    gap: 16px;
    align-items: start;
}

@media (max-width: 1024px) {
    .dd-editor-grid {
        grid-template-columns: 1fr 1fr;
    }

    .dd-properties-pane {
        grid-column: 1 / -1;
    }
}

@media (max-width: 768px) {
    .dd-editor-grid {
        grid-template-columns: 1fr;
    }

    .dd-source-pane,
    .dd-model-pane,
    .dd-properties-pane {
        grid-column: 1 / -1;
    }
}

.dd-pane {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px;
}

.dd-source-list {
    margin-block-start: 8px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-block-size: 480px;
    overflow-y: auto;
}

.dd-source-table {
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 8px;
}

.dd-source-table-used {
    opacity: 0.55;
}

.dd-source-table-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
}

.dd-source-table-header code {
    flex: 1 1 auto;
    min-inline-size: 0;
    overflow-wrap: anywhere;
    word-break: break-all;
}

.dd-source-table-header .widget-action-btn {
    flex-shrink: 0;
}

.dd-source-columns {
    margin-block-start: 6px;
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.dd-source-columns code {
    background: var(--bg-base);
    padding: 1px 6px;
    border-radius: 6px;
}

.dd-lanes {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
}

@media (max-width: 768px) {
    .dd-lanes {
        grid-template-columns: 1fr;
    }
}

.dd-lane h5 {
    margin-block-end: 8px;
    color: var(--text-secondary);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.dd-lane-items {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-block-size: 60px;
}

.dd-lane-hint {
    color: var(--text-muted);
    font-size: 12px;
}

.dd-table-card {
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 8px 10px;
    background: var(--bg-base);
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.dd-table-card:hover,
.dd-table-card:focus-visible {
    border-color: var(--accent-primary);
    outline: none;
}

.dd-table-card-primary {
    border-color: var(--accent-primary);
}

.dd-primary-tag {
    font-size: 10px;
    color: var(--accent-primary);
    text-transform: uppercase;
}

.dd-relationships {
    margin-block-start: 16px;
}

.dd-relationships-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-block-end: 8px;
}

.dd-relationship-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.dd-relationship-item {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 6px 10px;
}

.dd-rel-meta {
    color: var(--text-muted);
    font-size: 11px;
    margin-inline-start: auto;
}

.dd-properties-empty {
    text-align: center;
    color: var(--text-muted);
    padding: 24px 8px;
}

.dd-properties-empty i {
    font-size: 20px;
    margin-block-end: 8px;
    display: block;
}

.dd-fields-heading {
    margin-block-start: 16px;
}

.dd-field-list {
    list-style: none;
    margin: 8px 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.dd-field-list li {
    display: flex;
    align-items: center;
    gap: 8px;
}

.dd-field-kind {
    font-size: 10px;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-inline-start: auto;
}

.dd-danger {
    color: var(--accent-red);
}

.dd-preview-panel {
    margin-block-start: 16px;
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 12px;
}

.dd-preview-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.dd-preview-body {
    margin-block-start: 8px;
    overflow-x: auto;
}

.dd-preview-error {
    color: var(--accent-red);
}

/* Widget Visual Builder (Plan 002 Steps 7-8) */
.widget-source-section {
    margin-block-start: var(--space-6);
}

.widget-source-tabs {
    display: flex;
    gap: var(--space-2);
    border-block-end: 1px solid var(--border-color);
    margin-block-end: var(--space-5);
}

.widget-source-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 40px;
    padding: var(--space-3) var(--space-5);
    color: var(--text-secondary);
    background: transparent;
    border: 0;
    border-block-end: 2px solid transparent;
    cursor: pointer;
    transition: color 180ms cubic-bezier(0.4, 0, 0.2, 1), background-color 180ms cubic-bezier(0.4, 0, 0.2, 1);
}

.widget-source-tab:hover {
    color: var(--text-main);
    background: rgba(var(--accent-primary-rgb), 0.06);
}

.widget-source-tab:active {
    background: rgba(var(--accent-primary-rgb), 0.12);
}

.widget-source-tab[aria-selected="true"] {
    color: var(--accent-primary);
    border-block-end-color: var(--accent-primary);
}

.widget-source-tab:disabled {
    opacity: 0.55;
    cursor: not-allowed;
}

/* ── SQL table display configuration: builder UI ─────────────────────── */
.tcb-tabs {
    margin-block-end: var(--space-3);
}

.tcb-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

.tcb-status {
    font-size: 12px;
    color: var(--text-secondary);
    flex: 1 1 160px;
    min-width: 0;
}

.tcb-status.text-danger {
    color: var(--accent-red);
}

.tcb-row-header,
.tcb-row {
    display: grid;
    grid-template-columns: 1fr 1.1fr 1.1fr 110px 56px 48px 52px 46px 32px;
    gap: 8px;
    align-items: center;
    padding: 6px 4px;
}

.tcb-row-header {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    border-bottom: 1px solid var(--border-color);
    padding-bottom: 8px;
}

.tcb-row {
    border-bottom: 1px solid var(--border-color);
}

.tcb-row-stale {
    background: rgba(var(--accent-amber-rgb, 245, 158, 11), 0.08);
}

.tcb-col-name {
    font-family: monospace;
    font-size: 11px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    direction: ltr;
}

.tcb-field-input {
    font-size: 12px;
}

.tcb-move {
    display: flex;
    gap: 2px;
}

.tcb-move-btn {
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 9px;
    line-height: 1;
    padding: 3px 4px;
}

.tcb-move-btn:hover:not(:disabled) {
    color: var(--accent-primary);
    border-color: var(--accent-primary);
}

.tcb-move-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

.tcb-row-computed {
    background: rgba(var(--accent-primary-rgb), 0.06);
}

.tcb-calc-name {
    font-family: monospace;
    font-size: 11px;
}

.widget-table-column-formula {
    font-family: monospace;
}

/* Calculated and spacer rows carry fewer controls than a normal column row,
   so they opt out of the shared 12-column grid rather than padding it with
   empty cells. */
.widget-table-column-row-calc {
    display: flex;
    gap: var(--space-3);
    align-items: center;
}

.widget-table-column-row-calc>input {
    flex: 1 1 120px;
}

.widget-table-column-spacer-label,
.tcb-spacer-label {
    color: var(--text-muted);
    font-size: 12px;
    font-style: italic;
    flex: 1 1 auto;
}

.tcb-row-spacer {
    opacity: 0.85;
}

.tcb-expand-btn {
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    color: var(--text-secondary);
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.tcb-expand-btn:hover {
    color: var(--text-main);
    border-color: var(--accent-primary);
}

.tcb-stale-note {
    grid-column: 1 / -1;
    font-size: 11px;
    color: var(--accent-amber);
    padding: 0 4px 8px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.tcb-remove-btn {
    background: transparent;
    border: 1px solid var(--accent-red);
    color: var(--accent-red);
    border-radius: 6px;
    font-size: 11px;
    padding: 2px 8px;
    cursor: pointer;
}

.tcb-advanced {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 8px;
    background: var(--bg-surface);
    border-radius: 8px;
    padding: 10px;
    margin: 0 4px 10px;
}

.tcb-advanced-full {
    grid-column: 1 / -1;
}

.tcb-advanced-field label {
    display: block;
    font-size: 10px;
    color: var(--text-muted);
    margin-bottom: 3px;
}

.tcb-rules-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 8px;
}

.tcb-rule-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr auto;
    gap: 6px;
    align-items: center;
}

.tcb-rules-empty {
    font-size: 11px;
    color: var(--text-muted);
    margin: 0 0 6px;
}

.tcb-rule-add-btn {
    margin-bottom: 6px;
}

@media (max-width: 768px) {

    .tcb-row-header,
    .tcb-row {
        grid-template-columns: 1fr;
    }

    .tcb-row-header {
        display: none;
    }

    .tcb-advanced {
        grid-template-columns: 1fr;
    }

    .tcb-rule-row {
        grid-template-columns: 1fr;
    }
}

.widget-source-banner {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-5);
    color: var(--text-secondary);
    background: rgba(245, 158, 11, 0.08);
    border: 1px solid rgba(245, 158, 11, 0.3);
    border-radius: 8px;
}

.widget-visual-builder {
    margin-block-start: var(--space-2);
    padding: var(--space-5);
    background: rgba(12, 18, 34, 0.65);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 12px;
}

.widget-visual-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
}

.widget-visual-span-2 {
    grid-column: 1 / -1;
}

.widget-visual-grid .form-group {
    margin-block-end: 0;
}

.widget-visual-section-title {
    grid-column: 1 / -1;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--accent-primary);
    margin-block-end: -4px;
}

.widget-visual-section-title:not(:first-child) {
    margin-block-start: var(--space-2);
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--border-color);
}

.widget-visual-filters {
    min-inline-size: 0;
    margin: 0;
    padding: var(--space-5);
    background: rgba(255, 255, 255, 0.025);
    border: 1px solid var(--border-color);
    border-radius: 8px;
}

.widget-visual-filters legend {
    padding-inline: var(--space-2);
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 600;
}

.widget-filters-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.widget-filters-list:not(:empty) {
    margin-block-end: var(--space-3);
}

.widget-filter-row {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) minmax(130px, 0.9fr) minmax(160px, 1.3fr) 28px;
    gap: var(--space-3);
    align-items: center;
}

.widget-filter-value-wrap {
    min-inline-size: 0;
}

.widget-filter-value-wrap input {
    inline-size: 100%;
}

.widget-filter-value-pair {
    display: flex;
    gap: var(--space-2);
}

.widget-filter-value-pair input {
    min-inline-size: 0;
}

.widget-preview-action {
    display: flex;
    align-items: flex-end;
}

.widget-preview-results {
    margin-block-start: var(--space-5);
    min-block-size: 52px;
    padding: var(--space-5);
    color: var(--text-secondary);
    background: var(--bg-input, var(--bg-surface));
    border: 1px solid var(--border-color);
    border-radius: 8px;
}

.widget-preview-results.is-error {
    color: var(--accent-red);
    border-color: rgba(244, 63, 94, 0.45);
}

.widget-preview-results.is-success {
    border-color: rgba(16, 185, 129, 0.45);
}

.widget-preview-results.is-empty {
    text-align: center;
}

.widget-preview-table-wrap {
    max-inline-size: 100%;
    overflow-x: auto;
}

.widget-preview-table-wrap table {
    inline-size: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

.widget-preview-table-wrap th,
.widget-preview-table-wrap td {
    padding: var(--space-3);
    text-align: start;
    border-block-end: 1px solid var(--border-color);
    white-space: nowrap;
}

.widget-preview-table-wrap th {
    color: var(--text-main);
    font-family: var(--font-mono);
}

.widget-table-columns-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.widget-table-columns-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* Total-row formula editor: the text input takes the space, the column
   picker beside it inserts a real SQL column name at the caret so the
   name never has to be typed (and can't be confused with the column's
   display label -- the failure mode for non-Latin column names). */
.tcb-formula-input-row {
    display: flex;
    gap: var(--space-3);
    align-items: center;
}

.tcb-formula-input-row input {
    flex: 1 1 auto;
    min-width: 0;
}

.tcb-formula-input-row select {
    flex: 0 0 auto;
    max-width: 180px;
}

.widget-table-column-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    padding: var(--space-3);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.02);
}

/* Text/select controls share the line and shrink together; min-width: 0 is
   what actually lets a flex item shrink below its intrinsic width. The
   fixed-size clusters (checkboxes, reorder arrows, remove) never stretch. */
.widget-table-column-row>input,
.widget-table-column-row>select {
    flex: 1 1 150px;
    min-width: 0;
}

.widget-table-column-row>.widget-table-column-flags,
.widget-table-column-row>.widget-table-column-move,
.widget-table-column-row>.col-mapping-remove {
    flex: 0 0 auto;
}

/* The highlight rule is three controls in one span -- give it its own line
   rather than letting it squeeze the rest of the row. */
.widget-table-column-row>.widget-table-column-rule {
    flex: 1 1 260px;
    min-width: 0;
}

.widget-table-column-row>.widget-table-column-rule>input,
.widget-table-column-row>.widget-table-column-rule>select {
    flex: 1 1 80px;
    min-width: 0;
}

/* Reorder arrows sit last on the line, pushed to the far edge so they line
   up down the list however the controls above them wrap. */
.widget-table-column-row>.widget-table-column-move {
    margin-inline-start: auto;
}

/* Plan 004 Step 4: repeatable composite column-pair list in the Dataset
   Designer's relationship editor -- same list/row/remove-button pattern as
   .widget-table-columns-list / .widget-table-column-row above. */
.dd-rel-pairs-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.dd-rel-pair-row {
    display: grid;
    grid-template-columns: 1fr 1fr 28px;
    gap: var(--space-3);
    align-items: center;
}

.dd-rel-pairs-empty {
    color: var(--text-secondary);
    font-size: 13px;
}

@media (max-width: 768px) {
    .dd-rel-pair-row {
        grid-template-columns: 1fr;
    }
}

.widget-filter-value-wrap input,
#widget-visual-limit,
#widget-custom-sql {
    font-family: var(--font-mono);
}

#widget-visual-preview.is-loading {
    opacity: 0.72;
    cursor: wait;
}

@media (max-width: 768px) {
    .widget-source-tabs {
        inline-size: 100%;
    }

    .widget-source-tab {
        flex: 1;
        justify-content: center;
    }

    .widget-visual-grid {
        grid-template-columns: 1fr;
    }

    .widget-visual-span-2 {
        grid-column: auto;
    }

    .widget-filter-row {
        grid-template-columns: 1fr;
    }

    .widget-filter-value-pair {
        flex-direction: column;
    }

    .widget-preview-action .btn {
        inline-size: 100%;
        justify-content: center;
    }
}

/* ---------------------------------------------------------
   DASHBOARD EXPORT & DELIVERY MODALS STYLING
   --------------------------------------------------------- */
.export-options-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    margin-top: var(--space-4);
    margin-bottom: var(--space-4);
}

.export-option-card {
    display: block;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: var(--space-4);
    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.export-option-card:hover {
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.25);
    transform: translateY(-1px);
}

.export-option-card input[type="radio"] {
    display: none;
}

.export-option-card input[type="radio"]:checked+.export-option-content {
    color: var(--text-primary);
}

.export-option-card input[type="radio"]:checked {
    border-color: var(--color-primary, #0284c7);
}

.export-option-card:has(input[type="radio"]:checked) {
    border-color: var(--color-primary, #0284c7);
    background: rgba(2, 132, 199, 0.05);
    box-shadow: 0 0 0 1px var(--color-primary, #0284c7);
}

.export-option-content {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.export-option-content i {
    font-size: 28px;
    transition: transform 0.2s ease;
}

.export-option-card:hover .export-option-content i {
    transform: scale(1.08);
}

.icon-pdf {
    color: var(--accent-red);
}

.icon-excel {
    color: var(--accent-emerald);
}

.icon-csv {
    color: var(--accent-amber);
}

.export-option-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.export-option-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}

.export-option-desc {
    font-size: 11px;
    color: var(--text-secondary);
}

.export-progress-bar {
    margin-top: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: 8px;
    align-items: center;
}

.export-progress-track {
    width: 100%;
    height: 4px;
    background: rgba(255, 255, 255, 0.05);
    border-radius: 2px;
    overflow: hidden;
}

.export-progress-thumb {
    height: 100%;
    width: 35%;
    background: var(--color-primary, #0284c7);
    border-radius: 2px;
    animation: indeterminateProgress 1.5s infinite linear;
}

@keyframes indeterminateProgress {
    0% {
        transform: translateX(-100%);
    }

    100% {
        transform: translateX(300%);
    }
}

.export-progress-text {
    font-size: 11px;
    color: var(--text-secondary);
}

.export-error {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 20%, transparent);
    color: var(--accent-red);
    padding: var(--space-3);
    border-radius: 8px;
    font-size: 12px;
    margin-top: var(--space-3);
    text-align: center;
}

/* KPI Alerts count badge, shown on the account menu's Exceptions entry. */
.exceptions-badge {
    background: var(--accent-red);
    color: #ffffff;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    padding: 3px 6px;
    border-radius: 9999px;
    margin-inline-start: 4px;
}

/* Dark themes: --accent-red is too light for the white base-rule ink
   (3.1-3.9:1 on these three reds vs 6.5:1 on daylight's). Near-black ink
   passes 4.5:1 on all three; daylight keeps the white base rule. */
body.theme-fusionflow .exceptions-badge {
    color: #070B14;
}

body.theme-nord-arctic .exceptions-badge {
    color: #0E1117;
}

body.theme-dracula .exceptions-badge {
    color: #1E1E2E;
}

/* Exceptions inbox list */
.exceptions-filter-bar {
    display: flex;
    gap: 8px;
    margin-block: var(--space-3);
}

.exceptions-filter-btn.active {
    background: rgba(var(--accent-primary-rgb), 0.15);
    border-color: rgba(var(--accent-primary-rgb), 0.4);
    color: var(--accent-primary);
}

.exceptions-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.exception-row {
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.exception-status-active {
    border-inline-start: 3px solid var(--accent-red);
}

.exception-status-acknowledged {
    border-inline-start: 3px solid var(--accent-amber);
}

.exception-status-resolved {
    border-inline-start: 3px solid var(--accent-emerald);
    opacity: 0.75;
}

.exception-row-main {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.exception-status-badge {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

.exception-row-name {
    font-weight: 600;
    color: var(--text-main);
}

.exception-row-widget {
    font-size: 12px;
    color: var(--text-muted);
}

.exception-row-detail {
    font-size: 12px;
    color: var(--text-secondary);
}

.exception-row-time {
    margin-inline-start: 8px;
    color: var(--text-muted);
}

/* Per-widget KPI alert list (modal-kpi-alerts) */
.kpi-alerts-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-block-end: var(--space-4);
}

.kpi-alert-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 8px 10px;
}

.kpi-alert-inactive {
    opacity: 0.5;
}

.kpi-alert-row-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.kpi-alert-row-name {
    font-weight: 600;
    color: var(--text-main);
    font-size: 13px;
}

.kpi-alert-row-condition {
    font-size: 11px;
    color: var(--text-muted);
}

.kpi-alert-row-actions {
    display: flex;
    gap: 4px;
}

.kpi-alert-condition-row {
    display: flex;
    gap: 12px;
}

.kpi-alert-condition-row>div {
    flex: 1;
}

/* Dashboard-level data freshness banner */
.freshness-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(245, 158, 11, 0.1);
    border: 1px solid rgba(245, 158, 11, 0.25);
    color: var(--accent-amber, #F59E0B);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 13px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.freshness-banner i {
    flex-shrink: 0;
}

.freshness-banner .btn {
    margin-inline-start: auto;
}

#dashboard-freshness-list {
    font-weight: 600;
}

.dashboard-parameters-help,
.dashboard-secondary-actions-help {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--type-meta);
}

.dashboard-parameters-help {
    flex-basis: 100%;
}

.dashboard-secondary-actions-help {
    padding: var(--space-2) var(--space-3) 0;
}

#sync-logs-search,
#sync-trace-log {
    font-size: var(--type-compact);
}

/* Drill-detail modal table (underlying rows for a clicked chart point) */
.drill-detail-table-wrap {
    overflow-x: auto;
    max-height: 60vh;
    overflow-y: auto;
}

/* Saved Views control (inside .dashboard-parameters-bar) */
.saved-views-control {
    position: relative;
    flex-shrink: 0;
}

.saved-views-dropdown {
    position: absolute;
    inset-inline-end: 0;
    top: calc(100% + 8px);
    z-index: 50;
    width: 280px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    box-shadow: var(--card-shadow);
    padding: 12px;
}

.saved-views-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-height: 240px;
    overflow-y: auto;
    margin-bottom: 10px;
}

.saved-view-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    border-radius: 6px;
    padding: 2px;
}

.saved-view-row:hover {
    background: rgba(255, 255, 255, 0.05);
}

.saved-view-apply-btn {
    flex-grow: 1;
    text-align: start;
    background: transparent;
    border: none;
    color: var(--text-main);
    font-size: 13px;
    padding: 6px 8px;
    cursor: pointer;
    border-radius: 6px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.saved-view-apply-btn:hover {
    color: var(--accent-primary);
}

.saved-view-delete-btn {
    flex-shrink: 0;
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 4px 6px;
}

.saved-view-delete-btn:hover {
    color: var(--accent-red);
}

.saved-views-save-row {
    display: flex;
    gap: 6px;
    border-top: 1px solid var(--border-color);
    padding-top: 10px;
}

.saved-views-save-row input {
    flex-grow: 1;
    min-width: 0;
    padding: 6px 8px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background: var(--bg-input, transparent);
    color: var(--text-main);
    font-size: 12px;
}

/* Dataset Designer: formula field editor -- click-to-insert field tokens */
.dd-formula-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
}

.dd-formula-chip {
    padding: 2px 10px;
    border-radius: 999px;
    border: 1px solid var(--border-color);
    background: var(--bg-surface);
    color: var(--text-secondary);
    font-family: monospace;
    font-size: 0.8em;
    cursor: pointer;
}

.dd-formula-chip:hover {
    border-color: var(--accent-primary);
    color: var(--text-main);
}

/* Toast notifications (ui_fix.md #15) */
#toast-container {
    position: fixed;
    inset-block-start: 16px;
    inset-inline-end: 16px;
    z-index: 10000;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 360px;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    border-radius: 8px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    box-shadow: var(--card-shadow);
    color: var(--text-main);
    animation: toast-slide-in 0.2s ease-out;
}

.toast-error {
    border-inline-start: 4px solid var(--accent-red);
}

.toast-error i {
    color: var(--accent-red);
}

.toast-success {
    border-inline-start: 4px solid var(--accent-emerald);
}

.toast-success i {
    color: var(--accent-emerald);
}

.toast-message {
    flex: 1;
    font-size: 13px;
    line-height: 1.4;
}

.toast-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    padding: 0 2px;
}

.toast-close:hover {
    color: var(--text-main);
}

.toast-fade-out {
    opacity: 0;
    transition: opacity 0.2s ease-out;
}

@keyframes toast-slide-in {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Confirm modal (ui_fix.md #15) */
.confirm-modal-body {
    margin: 0;
    line-height: 1.5;
    white-space: pre-line;
}

/* ==========================================================================
   PRESENTATION MODE  (body.presenting)
   --------------------------------------------------------------------------
   The class is added/removed ONLY in response to the Fullscreen API's own
   fullscreenchange event -- see togglePresentationMode()/_syncPresentationMode()
   in static/js/dashboards.js. Nothing else touches it, so Esc, F11 and the
   browser's own exit control all restore the UI with no toggle state of our
   own that could drift out of sync with what the user is actually seeing.

   Deliberately KEPT while presenting: the dashboard title, the page selector
   and the filter bar -- presenters switch pages and change filters mid-meeting.
   Hidden: top bar, sidebar, freshness banner and every toolbar action except
   the Present button itself, which becomes the exit affordance.
   ========================================================================== */
body.presenting #system-topbar,
body.presenting #app-sidebar,
body.presenting #sidebar-resize-handle,
body.presenting #dashboard-freshness-banner {
    display: none !important;
}

body.presenting #dashboard-toolbar-actions>*:not(#btn-presentation-mode) {
    display: none;
}

/* Faint until hovered or focused so it stays out of the way on a projector,
   but never fully invisible -- Esc exits too, this is just the discoverable
   path. inset-inline-end rather than right so it lands on the correct side
   under the Arabic RTL locale without a second rule. */
body.presenting #btn-presentation-mode {
    position: fixed;
    top: 14px;
    inset-inline-end: 14px;
    z-index: 60;
    opacity: 0.18;
    transition: opacity 0.2s ease;
}

body.presenting #btn-presentation-mode:hover,
body.presenting #btn-presentation-mode:focus-visible {
    opacity: 1;
}

/* Page selector is presentation-only. min-width is generous on purpose:
   dashboard names here are long and must stay readable rather than being
   ellipsised down to nothing. */
.presentation-page-select {
    display: none;
}

body.presenting .presentation-page-select {
    display: block;
    margin-top: 10px;
    min-width: 320px;
    max-width: 560px;
    width: auto;
}

/* Multi-select filter parameter. The <select multiple> stays in the DOM as the
   value holder but is hidden; this is the checkbox dropdown drawn over it.
   Positioning uses logical properties so the panel opens on the correct side in
   the Arabic RTL locale without a second rule. */
.ms {
    position: relative;
    display: inline-block;
}

.ms-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 180px;
    max-width: 260px;
    padding: 7px 10px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    background: var(--bg-input, var(--bg-surface));
    color: var(--text-main);
    font-size: 12px;
    cursor: pointer;
}

.ms-toggle .ms-summary {
    flex: 1;
    text-align: start;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ms-toggle.has-selection {
    border-color: var(--accent-primary);
}

.ms-toggle:hover {
    border-color: var(--accent-primary);
}

.ms-panel {
    position: absolute;
    z-index: 40;
    inset-inline-start: 0;
    top: calc(100% + 4px);
    min-width: 240px;
    max-width: 340px;
    padding: 8px;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    background: var(--bg-surface);
    box-shadow: var(--card-shadow);
    backdrop-filter: blur(12px);
}

.ms-search {
    width: 100%;
    margin-bottom: 6px;
    padding: 6px 8px;
    font-size: 12px;
}

.ms-actions {
    display: flex;
    gap: 6px;
    margin-bottom: 6px;
}

.ms-action {
    flex: 1;
    padding: 4px 6px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    font-size: 11px;
    cursor: pointer;
}

.ms-action:hover {
    color: var(--text-main);
    border-color: var(--accent-primary);
}

.ms-options {
    max-height: 240px;
    overflow-y: auto;
}

.ms-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 6px;
    border-radius: 6px;
    font-size: 12px;
    color: var(--text-main);
    cursor: pointer;
}

.ms-option:hover {
    background: rgba(var(--accent-primary-rgb), 0.12);
}

.ms-option input {
    flex-shrink: 0;
    margin: 0;
}

.ms-option span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.parameter-item .parameter-hint {
    display: block;
    margin-top: 4px;
    font-size: 10px;
    color: var(--text-muted);
    max-width: 260px;
}

/* ==========================================================================
   A-1 (audit.md) - RESTORE KEYBOARD FOCUS RINGS
   --------------------------------------------------------------------------
   The global `:focus-visible` rule above (specificity 0,1,0) is defeated by
   six earlier/higher-specificity `outline: none` declarations. Each selector
   below is deliberately written to win the cascade against its specific
   offender - either by higher specificity, or by equal specificity plus later
   source order. Do not reorder or move this block above those rules.
   ========================================================================== */

/* vs. the base form-control rule: `input[type="text"]` is (0,1,1) and so beat
   the bare `:focus-visible` (0,1,0) regardless of source order. (0,2,1) wins. */
.custom-select:focus-visible,
input[type="text"]:focus-visible,
input[type="password"]:focus-visible,
input[type="number"]:focus-visible,
input[type="url"]:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* vs. `.sidebar-db-search-container .sidebar-db-search-input` (0,2,0). */
.sidebar-db-search-container .sidebar-db-search-input:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* vs. `.col-mapping-row input[type="text"]:focus` (0,2,1) - equal specificity,
   later source. */
.col-mapping-row input[type="text"]:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* vs. `.col-mapping-row select:focus` (0,2,0) - equal specificity, later source. */
.col-mapping-row select:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* vs. `.dd-name-input:focus` (0,2,0) - equal specificity, later source. */
.dd-name-input:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* vs. `.dd-table-card:hover, .dd-table-card:focus-visible { outline: none }`,
   which suppressed the ring on the very state it was named for. The card is a
   keyboard-selectable control, so it needs a visible ring, not just the hover
   border-colour change. */
.dd-table-card:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

/* ============================================================
   BOLDER LOGIN — split brand moment
   The login is the brand's only home (no logo asset exists), so this
   amplifies it in the system's own vocabulary: the layered surface
   (--bg-surface), the single accent glow (--accent-primary-rgb), and
   the Inter wordmark at full strength. No new colors, fonts, radii, or
   shadows are introduced -- every value is a token the surface already
   owns. The committed DESIGN.md world is "layered solid surfaces" where
   "decoration is subservient to the data", so the brand panel stays a
   composed solid surface with an accent glow rather than a decorative
   grid. The form panel quiets down so the brand carries the first
   impression. Under 769px the brand panel recedes and the card stands
   alone with its compact header, exactly as before.
   ============================================================ */

.login-container {
    flex-direction: row;
    align-items: stretch;
    justify-content: flex-start;
}

/* LEFT — the commanding brand panel. */
.login-brand-panel {
    flex: 1 1 0;
    min-width: 0;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 28px;
    padding: clamp(48px, 6vw, 88px) clamp(40px, 5vw, 80px);
    overflow: hidden;
    background-color: var(--bg-surface);
    background-image:
        radial-gradient(ellipse 70% 60% at 18% 32%, rgba(var(--accent-primary-rgb), 0.12) 0%, transparent 62%);
    border-inline-end: 1px solid var(--border-color);
    animation: brandRise 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.login-brand-mark {
    display: flex;
    align-items: center;
    gap: 18px;
    position: relative;
    z-index: 1;
}

.login-brand-panel .brand-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* The brand glyph at full presence -- the brand's only identity asset,
   given real weight rather than a 42px decoration. */
.login-brand-panel .login-logo-icon {
    font-size: 56px;
    line-height: 1;
    color: var(--accent-primary);
    filter: drop-shadow(0 0 18px rgba(var(--accent-primary-rgb), 0.45));
}

.login-brand-wordmark {
    font-family: var(--font-primary);
    font-size: clamp(34px, 3.6vw, 44px);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--text-main);
    margin: 0;
    text-wrap: balance;
}

.login-brand-panel .sub-brand {
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.5px;
}

/* A short accent rule -- the system's single-accent discipline as a
   structural divider, not decoration. */
.login-brand-rule {
    width: 56px;
    height: 2px;
    border: 0;
    background: var(--accent-primary);
    box-shadow: 0 0 12px -1px rgba(var(--accent-primary-rgb), 0.6);
    border-radius: 2px;
    position: relative;
    z-index: 1;
}

@keyframes brandRise {
    from {
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* RIGHT -- the form panel quiets down on desktop: it is a flex child
   rail (not a centered 400px card) and the brand panel owns the
   identity, so the card's compact header is hidden. Same glass surface,
   radius, padding, and loginFadeIn the card already had. */
@media (min-width: 769px) {
    .login-card {
        flex: 0 0 clamp(380px, 34vw, 460px);
        max-width: none;
        width: auto;
        display: flex;
        flex-direction: column;
        justify-content: center;
    }

    .login-card .login-header {
        display: none;
    }
}

/* Under 769px the brand panel recedes; the card is the whole surface
   and shows its compact header, exactly as before. */
@media (max-width: 768px) {
    .login-container {
        flex-direction: column;
        justify-content: center;
        align-items: center;
        padding: 20px;
    }

    .login-brand-panel {
        display: none;
    }

    .login-card {
        max-width: 400px;
        width: 100%;
    }
}

/* Honor reduced-motion: the brand panel's rise is the one authored
   moment; the card's existing fade is already covered by the global
   reduced-motion reset. */
@media (prefers-reduced-motion: reduce) {
    .login-brand-panel {
        animation: none;
    }
}

/* Toolbar dividers -- structural grouping for the dashboard action row.
   Gives the flat button row rhythm and separates state, management,
   filtering, output, and presentation actions with the system's own
   separator token. Presence through structure, not loudness. */
.toolbar-divider {
    width: 1px;
    height: 24px;
    background: var(--border-color);
    flex-shrink: 0;
    align-self: center;
}

/* Shell rhythm: keep global controls, workspace actions, and filters readable
   as their available inline space changes. */
.system-topbar {
    flex-wrap: wrap;
    row-gap: var(--space-3);
}

.system-topbar-user {
    flex-wrap: wrap;
}

.tab-header {
    align-items: flex-start;
    gap: var(--space-6);
}

.title-block {
    min-width: 0;
}

.title-block h2,
.title-block p {
    overflow-wrap: anywhere;
}

#dashboard-toolbar-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    justify-content: flex-end;
}

#dashboard-toolbar-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

#auto-refresh-select {
    width: auto;
}

.dashboard-secondary-actions {
    position: relative;
}

.dashboard-secondary-actions>summary {
    list-style: none;
}

.dashboard-secondary-actions>summary::-webkit-details-marker {
    display: none;
}

.dashboard-secondary-actions-menu {
    display: none;
    position: absolute;
    z-index: 50;
    inset-block-start: calc(100% + var(--space-2));
    inset-inline-end: 0;
    min-inline-size: 12rem;
    padding: var(--space-2);
    gap: var(--space-2);
    background: var(--bg-elevated, var(--bg-surface));
    border: 1px solid var(--border-color);
    border-radius: 10px;
}

.dashboard-secondary-actions[open] .dashboard-secondary-actions-menu {
    display: grid;
}

.dashboard-secondary-actions-menu .btn {
    justify-content: flex-start;
    width: 100%;
}

.mobile-dashboard-browser-toggle {
    display: none;
}

.dashboard-parameters-bar {
    flex-wrap: wrap;
    gap: var(--space-5);
    margin-block-end: var(--space-5);
}

@media (max-width: 1024px) {
    .system-topbar-workspace {
        flex-basis: 100%;
        order: 3;
    }

    .tab-header {
        flex-direction: column;
    }

    #dashboard-toolbar-actions {
        width: 100%;
        justify-content: flex-start;
    }

    .toolbar-divider {
        display: none;
    }
}

@media (max-width: 768px) {
    body {
        height: auto;
        min-height: 100vh;
        padding: 0;
    }

    .app-shell {
        gap: 0;
    }

    .system-topbar {
        padding: var(--space-4);
    }

    .sidebar-toggle-btn {
        display: none;
    }

    .mobile-dashboard-browser-toggle {
        display: inline-flex;
    }

    .system-topbar-workspace,
    .system-topbar-user {
        flex-basis: 100%;
    }

    .system-topbar-workspace {
        flex-direction: column;
        align-items: stretch;
    }

    .system-topbar-user {
        margin-inline-start: 0;
    }

    .glass-app {
        flex-direction: column;
        border-radius: 0;
    }

    /* Keep the dashboard browser available without letting it displace the
       current workspace until the user asks to browse. */
    .app-sidebar,
    .app-sidebar.collapsed {
        width: 100%;
        max-height: 0;
        padding-block: 0;
        border: 0;
        visibility: hidden;
        pointer-events: none;
        transition: max-height 0.25s cubic-bezier(0.4, 0, 0.2, 1), padding 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    }

    .app-sidebar.mobile-open {
        max-height: min(60svh, 480px);
        padding: var(--space-4);
        border-inline-end: 0;
        border-block-end: 1px solid var(--border-color);
        visibility: visible;
        pointer-events: auto;
    }

    .app-sidebar.collapsed .sidebar-section-header {
        display: block;
    }

    .app-sidebar.collapsed #sidebar-db-dropdown {
        display: flex;
    }

    .app-sidebar.collapsed #btn-new-dashboard {
        padding: 8px 12px;
    }

    .app-content {
        min-height: 0;
        padding: var(--space-5);
    }

    .dashboard-parameters-bar {
        align-items: stretch;
        flex-direction: column;
    }

    .parameters-list {
        width: 100%;
    }

    .parameter-item {
        flex: 1 1 220px;
        flex-wrap: wrap;
    }

    .parameter-input {
        flex: 1 1 140px;
    }

    .modal-card {
        max-height: calc(100svh - var(--space-5) - var(--space-5));
        margin-inline: var(--space-5);
    }
}

/* ---------------------------------------------------------------------------
   DAYLIGHT: white-alpha fills that vanish on a light canvas.
   Each rule below has a base `rgba(255, 255, 255, 0.0x)` background, which is
   invisible over daylight's near-white surfaces -- the control reads as empty
   space. Mirrored to a dark-based tint of the same weight: the same fix
   already applied to .btn-secondary / .sub-tab-btn further up this file.
   ------------------------------------------------------------------------- */
body.theme-daylight .style-opt-inner {
    background: rgba(0, 0, 0, 0.03);
    border-color: rgba(0, 0, 0, 0.12);
}

body.theme-daylight .sync-toggle-track {
    background: rgba(0, 0, 0, 0.16);
}

body.theme-daylight code {
    background: rgba(0, 0, 0, 0.06);
}

body.theme-daylight .lang-switch {
    background: rgba(0, 0, 0, 0.04);
}

/* :not(--clickable) because the top-bar account badge opts OUT of a chip:
   .user-profile-badge--clickable sets `background: transparent`, but that rule
   is (0,1,0) and this one is (0,2,1), so before the :not() the daylight tint
   won -- giving the light theme a gray pill behind the avatar that no dark
   theme has (measured 8/255 darker than the top bar; every dark theme 0-1/255).
   The tint stays available for a future non-clickable badge. */
body.theme-daylight .user-profile-badge:not(.user-profile-badge--clickable) {
    background: rgba(0, 0, 0, 0.03);
}

body.theme-daylight .onboarding-step .onboarding-tip {
    background: rgba(0, 0, 0, 0.03);
}

body.theme-daylight .export-progress-track {
    background: rgba(0, 0, 0, 0.08);
}

/* Dark-slate slab hardcoded outside the theme system -- a grey box on white. */
body.theme-daylight .connection-status-card {
    background: rgba(0, 0, 0, 0.03);
}

/* ---------------------------------------------------------
   APP FOOTER (brand strip below the app frame)
   --------------------------------------------------------- */
.app-footer {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 6px 0 0;
    color: var(--text-muted);
    font-size: 11px;
    line-height: 1;
    letter-spacing: 0.02em;
    text-align: center;
    user-select: none;
}

.app-footer-sep {
    opacity: 0.55;
}

/* ---------------------------------------------------------------------------
   DAYLIGHT MIRRORS (batch, 2026-08-10): the remaining white-alpha fills that
   had no dark-based counterpart. The mirrored rule sets below complete the
   list -- before this, hovers like .widget-action-btn:hover / .modal-close:hover
   were invisible on daylight's white card surfaces and the 0.02 fills on
   .widget-table-column-row / .export-option-card read as plain white boxes.
   Same weight-for-weight dark tint convention as the rules above. When a new
   component adds a white-alpha fill, add its mirror here.
   ------------------------------------------------------------------------- */
body.theme-daylight .sidebar-toggle-btn:hover {
    background: rgba(0, 0, 0, 0.06);
}

body.theme-daylight .group-pill:hover {
    background: rgba(0, 0, 0, 0.06);
}

body.theme-daylight .widget-action-btn:hover {
    background: rgba(0, 0, 0, 0.05);
}

body.theme-daylight .modal-close:hover {
    background: rgba(0, 0, 0, 0.05);
}

body.theme-daylight .excel-upload-zone {
    background: rgba(0, 0, 0, 0.02);
}

body.theme-daylight .excel-upload-zone:hover {
    background: rgba(0, 0, 0, 0.04);
}

body.theme-daylight .file-info-preview {
    background: rgba(0, 0, 0, 0.04);
}

body.theme-daylight .cache-badge.cache-miss {
    background: rgba(0, 0, 0, 0.05);
}

body.theme-daylight .user-assign-item:hover {
    background: rgba(0, 0, 0, 0.04);
}

body.theme-daylight .user-assign-category-header:hover {
    background: rgba(0, 0, 0, 0.05);
}

body.theme-daylight .sidebar-db-category-header:hover {
    background: rgba(0, 0, 0, 0.06);
}

body.theme-daylight .widget-visual-filters {
    background: rgba(0, 0, 0, 0.03);
}

body.theme-daylight .widget-table-column-row {
    background: rgba(0, 0, 0, 0.02);
}

body.theme-daylight .export-option-card {
    background: rgba(0, 0, 0, 0.02);
}

body.theme-daylight .export-option-card:hover {
    background: rgba(0, 0, 0, 0.05);
}

body.theme-daylight .saved-view-row:hover {
    background: rgba(0, 0, 0, 0.05);
}


/* ---------------------------------------------------------
   Impeccable polish pass (2026-08-12)
   - Widget loading skeleton (replaces empty canvas / "--" while data loads)
   - Widget type picker "More chart types" collapse
   - Schedule modal cron preview
   Kept below the A-1 focus block comment line; new selectors only, no
   reordering of existing rules.
--------------------------------------------------------- */

/* Widget loading skeleton: neutral gray base reads on both light and dark
   surfaces; the shimmer highlight is subtle so it never outshines data. */
.widget-skeleton {
    width: 100%;
    height: 100%;
    min-height: 48px;
    border-radius: 8px;
    background-color: rgba(128, 128, 128, 0.10);
    background-image: linear-gradient(100deg,
            transparent 30%, rgba(255, 255, 255, 0.14) 50%, transparent 70%);
    background-size: 200% 100%;
    animation: widget-skeleton-shimmer 1.5s ease-in-out infinite;
}

.widget-body .widget-skeleton {
    align-self: center;
}

.widget-body-table .widget-skeleton {
    align-self: flex-start;
    min-height: 120px;
}

.widget-skeleton-kpi {
    width: 55%;
    height: 44px;
}

.widget-skeleton-title {
    width: 40%;
    height: 12px;
    border-radius: 4px;
    margin-bottom: 8px;
}

@keyframes widget-skeleton-shimmer {
    0% {
        background-position: 200% 0;
    }

    100% {
        background-position: -200% 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .widget-skeleton {
        animation: none;
        background-image: none;
    }
}

/* Light-theme shimmer: the highlight has to go DARKER, not lighter.
   The base rule sweeps rgba(255, 255, 255, 0.14) across the skeleton, which
   reads as a moving stripe on a dark card (base 21 -> peak 54, a 32/255 step)
   but is invisible on theme-daylight: that card is #FFFFFF, the 10% gray base
   already composites to 241, and 14% white over 241 lands on 244 -- a 3/255
   step, so the default theme showed a static gray box with no sign that
   anything was loading. Measured across the full 1.5s cycle in all four
   themes; 10% black over the same base gives 217, a 24/255 step, matching the
   dark themes perceptually without turning the sweep into a solid bar.
   .dd-skeleton-row (the Dataset Designer skeleton) never had this problem
   because it builds its gradient from --bg-surface/--border-color, which
   already differ per theme. */
body.theme-daylight .widget-skeleton {
    background-image: linear-gradient(100deg,
            transparent 30%, rgba(0, 0, 0, 0.10) 50%, transparent 70%);
}

@media (prefers-reduced-motion: reduce) {

    /* Repeated for the theme-scoped selector on purpose: the rule above is
       (0,2,1) and would otherwise outrank the (0,1,0) `background-image: none`
       in the reduced-motion block above, leaving a static diagonal band frozen
       across every skeleton for reduced-motion users on the default theme. */
    body.theme-daylight .widget-skeleton {
        background-image: none;
    }
}

/* ---------------------------------------------------------
   Size/type-adaptive skeleton pieces (impeccable polish pass 5)
   The root `.widget-skeleton` holds `.ws` shimmer pieces whose sizes scale
   with the owning widget's grid span (`.widget-card.w-N.h-N`). A tall wide
   widget loads a big chart-area block; a small one loads a compact set of
   lines -- the skeleton matches the widget footprint instead of one gray box.
   --------------------------------------------------------- */
.widget-skeleton .ws {
    border-radius: 6px;
    background-color: inherit;
    background-image: inherit;
    background-size: inherit;
    animation: inherit;
}

@media (prefers-reduced-motion: reduce) {
    .widget-skeleton .ws {
        animation: none;
        background-image: none;
    }
}

/* Chart-style title bar (top of a chart/table skeleton). */
.ws-title {
    height: 12px;
    width: 40%;
    margin-bottom: 14px;
}

/* Chart content area: grows with the widget's height class. */
.ws-area {
    border-radius: 8px;
    margin-bottom: 14px;
}

.ws-area-h1 {
    height: 60px;
}

.ws-area-h2 {
    height: 150px;
}

.ws-area-h3 {
    height: 260px;
}

/* Bottom legend hint for charts. */
.ws-legend {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    display: inline-block;
    margin-inline-end: 6px;
    vertical-align: middle;
}

/* Generic shimmer lines of varying width (used for KPI sub-labels). */
.ws-line {
    height: 12px;
    margin-bottom: 8px;
}

.ws-line-50 {
    width: 50%;
}

.ws-line-70 {
    width: 70%;
}

/* KPI: a placeholder number block scaled to the footprint. */
.ws-kpi-number {
    height: 44px;
    width: 46%;
    min-width: 120px;
    border-radius: 8px;
    margin-bottom: 10px;
}

/* KPI sub-label line sits below the number.
   .kpi-skeleton has to BE a flex column: the `align-self: center` below was
   inert while it was a plain block, so the placeholder number rendered
   left-aligned at 46% width while the real .kpi-value it stands in for is
   centred by .kpi-value-wrapper -- the skeleton pointed at the wrong place and
   then the number jumped sideways when data arrived. justify-content: center
   also makes the pieces sit at the vertical middle of whatever height the card
   has, instead of clinging to the top of a tall one. */
.kpi-skeleton {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.kpi-skeleton .ws-kpi-number {
    align-self: center;
}

.kpi-skeleton .ws-line {
    margin-inline-end: auto;
    margin-inline-start: auto;
    width: 34%;
}

.kpi-spotlight-skeleton {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.kpi-spotlight-skeleton .ws-kpi-number {
    width: 70%;
}

/* Table skeleton: a header bar then body rows; row count and cell widths are
   produced by _buildSkeleton from the widget's span. Each row is a flex line
   of shimmer cells so gaps read like real table columns. */
.table-skeleton {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.table-skeleton .ws-title {
    width: 30%;
}

.ws-table-row {
    display: flex;
    gap: 12px;
}

.ws-table-row--header {
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border-color, rgba(255, 255, 255, 0.07));
}

.ws-cell {
    height: 12px;
    flex: none;
}

.ws-cell-15 {
    width: 15%;
}

.ws-cell-20 {
    width: 20%;
}

.ws-cell-25 {
    width: 25%;
}

.ws-cell-30 {
    width: 30%;
}

.ws-cell-35 {
    width: 35%;
}

.ws-cell-45 {
    width: 45%;
}

.ws-cell-55 {
    width: 55%;
}

/* Scope the pieces by layout container so they stack correctly. */
.widget-body .widget-skeleton.table-skeleton {
    align-self: flex-start;
    width: 100%;
}

/* The table skeleton deliberately builds MORE rows than the shortest card can
   show (see _buildSkeleton) and lets the container clip them, so it fills the
   card's height at every h-class instead of stopping a third of the way down a
   h-3. Clipping rather than scrolling: .widget-table-container is overflow:auto
   for the real table, which would otherwise put a scrollbar on a placeholder.
   Scoped with :has so the real table keeps its scrollbar. */
.widget-table-container:has(> .table-skeleton) {
    overflow: hidden;
}

.widget-body .widget-skeleton.chart-skeleton {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
}

.widget-body .widget-skeleton.chart-skeleton .ws-area {
    flex: 1 1 auto;
}

.widget-body .widget-skeleton.chart-skeleton .ws-title,
.widget-body .widget-skeleton.chart-skeleton .ws-line,
.widget-body .widget-skeleton.chart-skeleton .ws-legend {
    flex-shrink: 0;
}

/* ---- Widget type picker: filter-as-you-type (re-review P2) ---- */
.widget-type-filter-row {
    grid-column: 1 / -1;
    display: flex;
    gap: 6px;
    margin-bottom: 6px;
}

.widget-type-filter {
    flex: 1;
    min-width: 0;
}

.widget-type-filter-clear {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
}

.widget-type-filter-clear:hover {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.05);
}

.widget-type-filter-count {
    grid-column: 1 / -1;
    font-size: 11px;
    color: var(--text-muted);
    margin: -2px 0 6px;
}

.widget-type-picker.filtering .wt-card-more {
    display: flex;
}

.widget-type-picker.filtering .wt-more-section {
    display: block;
}

/* Widget type picker: primary 6 types shown, the rest behind "More types". */
.wt-more-toggle {
    grid-column: 1 / -1;
    margin-top: 8px;
    justify-content: center;
    font-weight: 500;
}

.widget-type-picker .wt-card-more,
.widget-type-picker .wt-more-section {
    display: none;
}

.widget-type-picker.show-all .wt-card-more {
    display: flex;
}

.widget-type-picker.show-all .wt-more-section {
    display: block;
}

/* Cron expression preview under the schedule form's cron field. */
.cron-preview {
    margin-top: 6px;
    font-size: 11px;
    color: var(--text-secondary);
    line-height: 1.55;
}

.cron-preview ul {
    margin: 4px 0 0;
    padding-inline-start: 16px;
}

.cron-preview li {
    font-family: var(--font-mono);
    font-size: 11px;
}

.cron-preview-error {
    color: var(--accent-red);
}

/* Sync failure detail row (impeccable pass 4): expandable cause under a
   FAILED / PARTIAL report row, instead of a cause buried in a tooltip. */
.sync-error-detail {
    background: color-mix(in srgb, var(--accent-red) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-red) 25%, transparent);
    border-radius: 8px;
    padding: 10px 12px;
    margin: 6px 0;
}

.sync-error-detail strong {
    font-size: 12px;
    color: var(--text-main);
    display: block;
    margin-bottom: 6px;
}

.sync-error-detail-lines {
    margin: 0 0 8px;
    padding: 8px 10px;
    max-height: 180px;
    overflow-y: auto;
    background: rgba(0, 0, 0, 0.25);
    border-radius: 6px;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.5;
    white-space: pre-wrap;
}

body.theme-daylight .sync-error-detail-lines {
    background: rgba(0, 0, 0, 0.06);
}

/* Sync failure-row toggle affordance (impeccable re-review P3): the status
   cell is a keyboard-focusable button; the chevron rotates when open. */
.sync-status-toggle:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

.sync-error-chevron {
    margin-inline-start: 5px;
    font-size: 10px;
    color: var(--text-muted);
    transition: transform 0.2s ease;
}

.sync-status-toggle[aria-expanded="true"] .sync-error-chevron {
    transform: rotate(180deg);
}

/* Mobile sync-card failure detail (impeccable re-review P2): mirrors the
   desktop table's expandable cause; the "Why did it fail?" button sits in
   the card action row. */
.sync-card-error-detail {
    margin-top: 8px;
}

.sync-card-error-detail .sync-error-detail {
    margin: 0;
}

.sync-report-card-actions .btn[aria-expanded="true"] {
    border-color: color-mix(in srgb, var(--accent-red) 45%, transparent);
    color: var(--accent-red);
}

/* Cron preview busy state: amber, not red -- being throttled is not a
   broken expression (impeccable re-review P1). */
.cron-preview-busy {
    color: var(--accent-amber);
}

/* Toast action button (undo-style "Restore" after a delete): sits between
   the message and the close button, primary-colored so it reads as the
   escape hatch it is (impeccable re-review P1). */
.toast-action {
    flex-shrink: 0;
    background: rgba(var(--accent-primary-rgb), 0.14);
    border: 1px solid rgba(var(--accent-primary-rgb), 0.45);
    border-radius: 6px;
    color: var(--accent-primary);
    font-size: 12px;
    font-weight: 600;
    padding: 5px 12px;
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.toast-action:hover {
    background: rgba(var(--accent-primary-rgb), 0.24);
}

/* ============================================================
   OPS BOARD -- admin shell world (impeccable shape, 2026-08-13)
   The data pipeline as a flight operations display. Scoped to admin
   surfaces (#settings-tab, #exceptions-tab, admin modals) so the
   dashboard reading surface is untouched. Identity via composition,
   typography and status language only -- every color comes from the
   4-theme token system.
   ============================================================ */

/* ---- Board channel strip: sub-tab nav as an ops header ---- */
#settings-tab .sub-tabs-nav {
    border-bottom: 1px solid var(--border-color);
    gap: 0;
    padding-bottom: 0;
    margin-bottom: 22px;
}

#settings-tab .sub-tab-group {
    padding-inline-end: 0;
    margin-inline-end: 0;
    border-inline-end: none;
    gap: 2px;
}

#settings-tab .sub-tab-group-label {
    font-size: 10px;
    letter-spacing: 0.01em;
    color: var(--text-muted);
    padding-inline: 14px 10px;
    text-transform: uppercase;
    display: flex;
    align-items: center;
    margin-inline-end: 0;
}

html:lang(en) #settings-tab .sub-tab-group-label {
    letter-spacing: 0.14em;
}

/* Channel buttons: hairline, quiet; active = accent underline + tint,
   never a filled pill (distill: accent is reserved for actions). */
#settings-tab .sub-tab-btn {
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    color: var(--text-secondary);
    padding: 10px 14px;
    font-size: 12px;
    letter-spacing: 0.02em;
    transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}

#settings-tab .sub-tab-btn:hover {
    background: transparent;
    color: var(--text-main);
    border-bottom-color: var(--border-color);
}

#settings-tab .sub-tab-btn.active {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.06);
    border-bottom-color: var(--accent-primary);
    font-weight: 600;
    box-shadow: none;
}

#settings-tab .sub-tab-group:has(.sub-tab-btn.active) .sub-tab-group-label {
    color: var(--accent-primary);
}

/* ---- Group pills: same quiet-active treatment ---- */
.group-pill.active {
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.10);
    border-color: rgba(var(--accent-primary-rgb), 0.45);
    box-shadow: none;
}

/* ---- Board panels: stencil band headers ---- */
#settings-tab .settings-group>h2,
#settings-tab .settings-group>h3,
#settings-tab .sync-card h2,
#settings-tab .sync-card h3,
#exceptions-tab .sync-card h2,
#schedule-modal .settings-group h4 {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-bottom: 6px;
}

html:lang(en) #settings-tab .settings-group>h2,
html:lang(en) #settings-tab .settings-group>h3,
html:lang(en) #settings-tab .sync-card h2,
html:lang(en) #settings-tab .sync-card h3,
html:lang(en) #exceptions-tab .sync-card h2,
html:lang(en) #schedule-modal .settings-group h4 {
    letter-spacing: 0.12em;
}

#settings-tab .settings-group>h2 i,
#settings-tab .settings-group>h3 i,
#settings-tab .sync-card h2 i,
#settings-tab .sync-card h3 i,
#exceptions-tab .sync-card h2 i,
#schedule-modal .settings-group h4 i {
    color: var(--accent-primary);
    font-size: 12px;
}

#settings-tab .sync-header-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
    gap: 12px;
    flex-wrap: wrap;
}

/* P3: the primary panel title must outrank its own description and buttons --
   13px/700 for the Sync and Exceptions titles, 11px for all nested bands. */
#settings-tab .sync-card>.sync-header-row h2,
#exceptions-tab .sync-card>h2 {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.02em;
}

html:lang(en) #settings-tab .sync-card>.sync-header-row h2,
html:lang(en) #exceptions-tab .sync-card>h2 {
    letter-spacing: 0.08em;
}

#settings-tab .sync-header-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

#settings-tab .logs-search-wrapper {
    margin: 10px 0;
    position: relative;
}

#settings-tab .logs-search-wrapper input {
    width: 100%;
    padding-block: 8px;
    padding-inline: 12px 35px;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background: var(--bg-input, var(--bg-surface));
    color: var(--text-main);
}

/* ---- Status stamps (gate states on the departure board) ---- */
.stamp {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    font-family: var(--font-mono);
    padding: 3px 8px;
    border-radius: 4px;
    border: 1px solid transparent;
    white-space: nowrap;
}

html:lang(en) .stamp {
    letter-spacing: 0.09em;
}

.stamp-ok {
    color: var(--accent-emerald);
    background: color-mix(in srgb, var(--accent-emerald) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-emerald) 35%, transparent);
}

.stamp-run {
    color: var(--accent-amber);
    background: color-mix(in srgb, var(--accent-amber) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-amber) 35%, transparent);
    animation: stampPulse 2s ease-in-out infinite;
}

@keyframes stampPulse {

    0%,
    100% {
        box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent-amber) 35%, transparent);
    }

    50% {
        box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-amber) 0%, transparent);
    }
}

@media (prefers-reduced-motion: reduce) {
    .stamp-run {
        animation: none;
    }
}

.stamp-warn {
    color: var(--accent-amber);
    background: color-mix(in srgb, var(--accent-amber) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-amber) 35%, transparent);
}

.stamp-err {
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 12%, transparent);
    border-color: color-mix(in srgb, var(--accent-red) 35%, transparent);
}

.stamp-mute {
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 10%, transparent);
    border-color: var(--border-color);
}

/* ---- Alarm banner (SCADA DNA): services needing attention ---- */
.ops-alarm-banner {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border: 1px solid color-mix(in srgb, var(--accent-red) 40%, transparent);
    border-inline-start: 3px solid var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 7%, transparent);
    border-radius: 10px;
    padding: 12px 16px;
    margin-bottom: 18px;
}

.ops-alarm-banner-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--accent-red);
}

html:lang(en) .ops-alarm-banner-header {
    letter-spacing: 0.1em;
}

.ops-alarm-banner-header i {
    font-size: 13px;
}

.ops-alarm-banner-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ops-alarm-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 12px;
    flex-wrap: wrap;
}

.ops-alarm-row-name {
    font-weight: 600;
    color: var(--text-main);
}

.ops-alarm-row-meta {
    color: var(--text-muted);
    font-size: 11px;
    font-family: var(--font-mono);
}

.ops-alarm-row-actions {
    display: flex;
    gap: 6px;
    align-items: center;
}

/* ---- Flight recorder: trace log + migration console ---- */
.flight-recorder-screen {
    direction: ltr;
    unicode-bidi: isolate;
    background: #0B1120;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    color: #D7E3F4;
    font-family: var(--font-mono);
    font-size: 11px;
    line-height: 1.6;
    overflow: hidden;
    padding: 12px;
    position: relative;
}

.flight-recorder-screen::before {
    content: "REC \25CF";
    position: absolute;
    inset-block-start: 8px;
    inset-inline-end: 12px;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--accent-red);
    opacity: 0.75;
}

.flight-recorder-screen>div,
.flight-recorder-screen>pre {
    max-height: 400px;
    overflow-y: auto;
    white-space: pre-wrap;
    line-height: 1.6;
    margin: 0;
    color: inherit;
    font-family: inherit;
    font-size: inherit;
}

#sub-tab-migration .migration-log-console {
    flex-grow: 1;
    min-height: 200px;
    background: #0B1120;
    border-color: var(--border-color);
    color: #D7E3F4;
}

/* ---- Danger buttons (harden) ---- */
.btn-danger {
    background: var(--accent-red);
    border: 1px solid var(--accent-red);
    color: var(--bg-base);
    font-weight: 600;
}

.btn-danger:hover {
    filter: brightness(1.08);
    color: var(--bg-base);
}

.btn-danger-ghost {
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--accent-red) 40%, transparent);
    color: var(--accent-red);
    font-weight: 600;
}

.btn-danger-ghost:hover {
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    color: var(--accent-red);
    border-color: var(--accent-red);
}

/* ---- Danger confirm dialog ---- */
.modal-overlay.modal-danger .modal-card {
    border-color: color-mix(in srgb, var(--accent-red) 45%, transparent);
}

.modal-overlay.modal-danger .modal-header h3 i {
    color: var(--accent-red);
}

/* ---- Topbar alert bell (delight) ---- */
.topbar-alert-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    border: 1px solid var(--border-color);
    background: rgba(255, 255, 255, 0.03);
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition-smooth);
    font-size: 14px;
}

.topbar-alert-bell:hover {
    color: var(--text-main);
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.08);
}

.topbar-alert-bell .exceptions-badge {
    position: absolute;
    inset-block-start: -6px;
    inset-inline-end: -6px;
    font-size: 9px;
    padding: 1px 5px;
}

/* ---- KPI delta chip (bolder) ---- */
.kpi-delta-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 999px;
    font-variant-numeric: tabular-nums;
    margin-top: 2px;
}

.kpi-delta-chip.up {
    color: var(--accent-emerald);
    background: color-mix(in srgb, var(--accent-emerald) 12%, transparent);
}

.kpi-delta-chip.down {
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 12%, transparent);
}

.kpi-delta-chip.flat {
    color: var(--text-muted);
    background: color-mix(in srgb, var(--text-muted) 10%, transparent);
}

/* ---- KPI emphasis: accent carries the value when no delta is configured;
   when a delta chip is present, the chip owns the accent and the value
   recedes to neutral. ---- */
.kpi-value,
.kpi-spotlight-value {
    color: var(--accent-primary);
    text-shadow: none;
}

.kpi-value-wrapper.has-delta .kpi-value,
.kpi-spotlight.has-delta .kpi-spotlight-value {
    color: var(--text-main);
}

body.theme-daylight .kpi-value {
    color: var(--accent-primary);
    text-shadow: none;
}

/* ---- Schedule modal radius / logical cleanup (polish) ---- */
#schedule-modal input.custom-select,
#schedule-modal select.custom-select,
#schedule-modal textarea.custom-select {
    border-radius: 8px;
}

#schedule-modal .schedule-form-panel {
    border-inline-start: 1px solid var(--border-color);
    padding-inline-start: 20px;
}

/* ---- Report editor modal: same stencil band headers (ops board) ---- */
#modal-report-editor .col-mapping-title,
#modal-report-editor .sync-schedule-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
}

html:lang(en) #modal-report-editor .col-mapping-title,
html:lang(en) #modal-report-editor .sync-schedule-title {
    letter-spacing: 0.12em;
}

#modal-report-editor .col-mapping-title i,
#modal-report-editor .sync-schedule-title i {
    color: var(--accent-primary);
    font-size: 12px;
}

#modal-report-editor .sync-schedule-panel {
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 16px;
    background: rgba(255, 255, 255, 0.015);
}

#modal-report-editor .col-mapping-panel {
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 16px;
}

#modal-report-editor #report-job-status-panel {
    border: 1px solid color-mix(in srgb, var(--accent-amber) 40%, transparent);
    border-radius: 10px;
    background: color-mix(in srgb, var(--accent-amber) 6%, transparent);
}

/* Alert bell: when exceptions are open, the bell wears the accent ring */
.topbar-alert-bell.has-alerts {
    border-color: var(--accent-red);
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 8%, transparent);
}

.topbar-alert-bell.has-alerts:hover {
    border-color: var(--accent-red);
    color: var(--accent-red);
    background: color-mix(in srgb, var(--accent-red) 14%, transparent);
}

/* ============================================================
   FEEL PASS (impeccable polish, 2026-08-13)
   The interaction layer the world was missing: one authored
   moment (widget grid entrance), tactile press states, menu
   motion, native-form accents, and a themed selection color.
   Everything is token-driven and respects the global
   prefers-reduced-motion reset (all animations have !important
   duration 0.01ms there).
   ============================================================ */

/* ---- The authored moment: widget cards rise into place on load.
   Transform-only (no opacity) so content is never gated; stagger
   comes from JS setting animation-delay per card. ---- */
.widget-card {
    /* backwards fill: the from-state applies only during the delay, then the
       element returns to its normal declarations -- a `both`/`forwards` fill
       would hold translateY(0) forever and defeat the :hover lift. */
    animation: widgetCardIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) backwards;
}

@keyframes widgetCardIn {
    from {
        transform: translateY(8px);
    }

    to {
        transform: translateY(0);
    }
}

/* ---- Tactile press feedback: press snaps down (0.1s), release
   eases back on the base transition. --transition-smooth's 0.35s
   all-properties timing made presses feel mushy. ---- */
.btn {
    transition:
        background-color 0.2s ease,
        border-color 0.2s ease,
        color 0.2s ease,
        box-shadow 0.25s ease,
        filter 0.2s ease,
        transform 0.1s ease-out;
}

.btn:active {
    transform: translateY(1px) scale(0.96);
}

.btn-primary:active {
    filter: brightness(0.92);
    box-shadow: 0 0 12px -4px rgba(var(--accent-primary-rgb), 0.4);
}

.btn-secondary:active {
    background: rgba(255, 255, 255, 0.11);
}

/* ---- Menus: quick corner-anchored entrance (account menu,
   dashboard secondary actions) instead of a cold pop. ---- */
.system-topbar-account[open] .system-topbar-account-menu,
.dashboard-secondary-actions[open] .dashboard-secondary-actions-menu {
    animation: menuIn 0.16s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes menuIn {
    from {
        opacity: 0;
        transform: translateY(-4px) scale(0.98);
    }

    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

/* ---- Native form accents: checkboxes/radios/ranges instantly
   feel intentional in every theme. ---- */
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
    accent-color: var(--accent-primary);
}

/* ---- Themed selection color (was the browser default). ---- */
::selection {
    background: rgba(var(--accent-primary-rgb), 0.28);
    color: var(--text-main);
}

/* ---- Delta chip appears with a tiny settle (already-visible
   default: the chip only exists once data has rendered). ---- */
.kpi-delta-chip {
    animation: chipIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes chipIn {
    from {
        opacity: 0;
        transform: translateY(3px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ---- Long bilingual widget titles wrap instead of truncating
   (uppercase + letter-spacing + nowrap clipped them). ---- */
.widget-card .widget-header h3 {
    text-wrap: balance;
    overflow-wrap: break-word;
    word-break: break-word;
    line-height: 1.35;
}

/* ---- Main scroll areas get a slim, token-driven scrollbar to
   match the styled table/sidebar scrollbars. ---- */
.app-main-wrapper,
.app-content,
#sync-trace-log,
.migration-log-console {
    scrollbar-width: thin;
    scrollbar-color: rgba(148, 163, 184, 0.4) transparent;
}

.app-main-wrapper::-webkit-scrollbar,
.app-content::-webkit-scrollbar,
#sync-trace-log::-webkit-scrollbar,
.migration-log-console::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

.app-main-wrapper::-webkit-scrollbar-thumb,
.app-content::-webkit-scrollbar-thumb,
#sync-trace-log::-webkit-scrollbar-thumb,
.migration-log-console::-webkit-scrollbar-thumb {
    background: rgba(148, 163, 184, 0.35);
    border-radius: 8px;
}

.app-main-wrapper::-webkit-scrollbar-thumb:hover,
.app-content::-webkit-scrollbar-thumb:hover,
#sync-trace-log::-webkit-scrollbar-thumb:hover,
.migration-log-console::-webkit-scrollbar-thumb:hover {
    background: rgba(148, 163, 184, 0.55);
}

.app-main-wrapper::-webkit-scrollbar-track,
.app-content::-webkit-scrollbar-track,
#sync-trace-log::-webkit-scrollbar-track,
.migration-log-console::-webkit-scrollbar-track {
    background: transparent;
}


/* ============================================================
   READING SURFACE (impeccable bolder, 2026-08-15)
   The dashboard reading surface gets its own authored anchor:
   a confident hero title with an accent rule, and tabular
   numerals on KPI figures so they align and read as data.
   ============================================================ */

/* Hero dashboard title: the page's single identity anchor. */
.title-block h2 {
    font-size: var(--type-hero);
    font-weight: 700;
    letter-spacing: 0;
    padding-bottom: 10px;
    position: relative;
}

html:lang(en) .title-block h2 {
    letter-spacing: -0.01em;
}

.title-block h2::after {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    bottom: 0;
    width: 32px;
    height: 3px;
    border-radius: 2px;
    background: var(--accent-primary);
}

/* KPI figures read as data: tabular numerals align digits across
   cards and match the delta chip's own tabular-nums. */
.kpi-value,
.kpi-spotlight-value {
    font-variant-numeric: tabular-nums;
}


/* ---- Row actions (reports/queries): 32px targets, 6px gaps (re-review P1) ---- */
.row-action-btn {
    width: 36px;
    height: 36px;
    font-size: 12px;
    position: relative;
}

.row-action-btn::after {
    content: "";
    position: absolute;
    inset: -4px;
}

.row-action-btn+.row-action-btn {
    margin-inline-start: 8px;
}

/* ---- Widget card: overflow menu (move/duplicate) ---- */
.widget-more-menu {
    position: relative;
    display: inline-flex;
}

.widget-more-menu summary {
    list-style: none;
}

.widget-more-menu summary::-webkit-details-marker {
    display: none;
}

.widget-more-menu[open] .widget-action-btn {
    color: var(--text-main);
    background: rgba(255, 255, 255, 0.06);
}

.widget-more-menu-panel {
    position: absolute;
    inset-inline-end: 0;
    top: calc(100% + 4px);
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 6px;
    min-width: 150px;
    border-radius: 10px;
    border: 1px solid var(--border-color);
    background: var(--bg-elevated, #0C1222);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
}

.widget-more-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    background: transparent;
    border: none;
    color: var(--text-main);
    font-size: 12px;
    font-weight: 500;
    padding: 6px 10px;
    border-radius: 6px;
    cursor: pointer;
    text-align: start;
    white-space: nowrap;
}

.widget-more-menu-item:hover {
    background: rgba(255, 255, 255, 0.06);
}

/* ---- Exceptions inbox: stale-report dismiss ---- */
.exceptions-stale-dismiss {
    background: transparent;
    border: none;
    color: var(--text-muted);
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
    flex-shrink: 0;
}

.exceptions-stale-dismiss:hover {
    color: var(--accent-emerald);
    background: rgba(255, 255, 255, 0.05);
}

/* 44px-class touch target for the compact card actions (impeccable P2):
   an invisible hit-area extension keeps the layout unchanged. */
.widget-actions .widget-action-btn {
    position: relative;
}

.widget-actions .widget-action-btn::after {
    content: "";
    position: absolute;
    inset: -10px 0;
    border-radius: 8px;
}

/* Extend only the cluster's outer edges horizontally so adjacent targets
   never overlap (impeccable re-review P2). */
.widget-actions .widget-action-btn:first-of-type::after {
    inset: -10px 0 -10px -10px;
}

.widget-actions .widget-action-btn:last-of-type::after {
    inset: -10px -10px -10px 0;
}

/* ---- Migration section titles: de-inlined, Arabic-safe (re-review P3) ---- */
.migration-section-title {
    font-size: 11px;
    font-weight: 600;
    margin-bottom: 10px;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0;
}

html:lang(en) .migration-section-title {
    letter-spacing: 0.5px;
}

/* ---- Custom-tables VIEW badge: de-inlined (re-review P3) ---- */
.views-badge {
    font-family: inherit;
    font-size: 10px;
    letter-spacing: 0;
    padding: 1px 5px;
    border-radius: 4px;
    background: rgba(var(--accent-indigo-rgb), 0.15);
    color: var(--accent-indigo);
}

html:lang(en) .views-badge {
    letter-spacing: 0.5px;
}

/* ---- Small-control hit areas (audit P1): param chips + show-me-how ---- */
.param-chip,
.show-me-how {
    position: relative;
}

.param-chip::after {
    content: "";
    position: absolute;
    inset: -6px;
    border-radius: 6px;
}

.show-me-how::after {
    content: "";
    position: absolute;
    inset: -6px;
    border-radius: 8px;
}

/* ---- Shared layout utilities (audit polish) ---- */
.ff-flex-center {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* ---- Widget editor: click-to-insert :param chips ---- */
.param-chip {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 11px;
    padding: 3px 8px;
    border-radius: 6px;
    border: 1px solid var(--border-color);
    background: rgba(255, 255, 255, 0.03);
    color: var(--accent-primary);
    cursor: pointer;
    margin-inline-end: 6px;
    margin-bottom: 4px;
}

.param-chip:hover {
    background: rgba(var(--accent-primary-rgb), 0.10);
    border-color: var(--accent-primary);
}

/* ---- Exceptions inbox: stale-data report box (viewer flags) ---- */
.exceptions-stale-box {
    border: 1px solid color-mix(in srgb, var(--accent-amber) 35%, transparent);
    border-radius: 10px;
    padding: 10px 12px;
    margin-bottom: 12px;
    background: color-mix(in srgb, var(--accent-amber) 5%, transparent);
}

.exceptions-stale-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    color: var(--text-secondary);
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 6px;
}

html:lang(en) .exceptions-stale-title {
    letter-spacing: 0.08em;
}

.exceptions-stale-title i {
    color: var(--accent-amber);
}

.exceptions-stale-row {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    font-size: 12px;
    padding: 2px 0;
}

.exceptions-stale-dash {
    color: var(--text-main);
    font-weight: 600;
}

.exceptions-stale-meta {
    color: var(--text-muted);
}

/* ---- Widget filter hint: which dashboard parameters drive this card ---- */
.widget-filter-hint {
    font-size: 11px;
    font-weight: 500;
    color: var(--text-muted);
    letter-spacing: 0.01em;
    margin: -6px 0 12px;
    padding-inline-start: 2px;
}

html:lang(en) .widget-filter-hint {
    letter-spacing: 0.02em;
}

/* ============================================================
   De-inlined inline styles (2026-08-15)
   Mechanical refactor: inline style="..." attributes converted to
   .ff-* classes. Classes used in a single modal are scoped under
   that modal id; shared classes are unscoped. This block stays at
   the END of the file so it wins equal-specificity conflicts with
   earlier rules (it replaces what were inline styles).
   ============================================================ */
.ff-accent {
    color: var(--accent-primary);
}

.ff-badge-blue {
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--accent-blue) 15%, transparent);
    margin-inline-start: 5px;
    color: var(--accent-blue);
    font-weight: 600;
}

.ff-badge-green {
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--accent-emerald) 15%, transparent);
    margin-inline-start: 5px;
    color: var(--accent-emerald);
    font-weight: 600;
}

.ff-badge-muted {
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.05);
    margin-inline-start: 5px;
    color: var(--text-muted);
    font-weight: 600;
}

.ff-badge-self {
    margin-inline-start: 5px;
    font-size: 10px;
    padding: 2px 6px;
    background: rgba(0, 217, 160, 0.15);
    border: 1px solid var(--accent-primary);
    color: var(--accent-primary);
    border-radius: 4px;
}

.ff-blue-600 {
    color: var(--accent-blue);
    font-weight: 600;
}

.ff-box-panel {
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 12px;
    margin-top: 15px;
    background: rgba(255, 255, 255, 0.02);
}

.ff-break-all {
    word-break: break-all;
}

.ff-btn-danger {
    padding: 4px 8px;
    font-size: 11px;
    background: color-mix(in srgb, var(--accent-red) 10%, transparent);
    border-color: color-mix(in srgb, var(--accent-red) 20%, transparent);
    color: var(--accent-red);
}

.ff-btn-danger-ghost {
    color: var(--accent-red);
    border-color: color-mix(in srgb, var(--accent-red) 10%, transparent);
}

.ff-btn-danger-soft {
    padding: 5px 10px;
    font-size: 11px;
    background: color-mix(in srgb, var(--accent-red) 8%, transparent);
    border-color: color-mix(in srgb, var(--accent-red) 20%, transparent);
    color: var(--accent-red);
}

.ff-btn-excel {
    width: 100%;
    margin-top: 10px;
    justify-content: center;
}

.ff-btn-sm-pad {
    padding: 6px 12px;
    font-size: 13px;
}

.ff-btn-view {
    width: 100%;
    justify-content: center;
}

.ff-center {
    text-align: center;
}

.ff-center-muted {
    text-align: center;
    color: var(--text-muted);
}

.ff-center-red {
    text-align: center;
    color: var(--accent-red);
}

.ff-chart-empty {
    color: var(--text-muted);
    padding: 20px;
    text-align: center;
    font-size: 13px;
}

.ff-chart-empty-24 {
    text-align: center;
    color: var(--text-muted);
    padding: 24px 12px;
}

.ff-chart-error {
    color: var(--accent-red);
    padding: 20px;
    text-align: center;
    font-size: 13px;
}

.ff-chip-5-10 {
    padding: 5px 10px;
    font-size: 11px;
    gap: 5px;
}

.ff-chip-5-9 {
    padding: 5px 9px;
    font-size: 11px;
}

.ff-chip-icon {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: rgba(var(--accent-primary-rgb), 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.ff-chip-md {
    padding: 6px 10px;
    font-size: 12px;
}

.ff-chip-md-me6 {
    padding: 6px 10px;
    font-size: 12px;
    margin-inline-end: 6px;
}

.ff-chip-sm {
    padding: 4px 10px;
    font-size: 11px;
}

.ff-chip-sm-me6 {
    padding: 4px 10px;
    font-size: 11px;
    margin-inline-end: 6px;
}

.ff-chip-xs {
    padding: 2px 6px;
    font-size: 10px;
    margin-inline-start: 8px;
}

.ff-color-swatch {
    width: 44px;
    height: 38px;
    padding: 2px;
    cursor: pointer;
}

#modal-new-dashboard,
#modal-edit-dashboard .ff-dc-groups {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 16px;
}

.ff-emerald-11 {
    color: var(--accent-emerald);
    font-size: 11px;
}

.ff-empty-cell {
    text-align: center;
    padding: 20px;
}

.ff-empty-dashed {
    text-align: center;
    color: var(--text-muted);
    padding: 18px;
    border: 1px dashed var(--border-color);
    border-radius: 10px;
}

.ff-empty-dashed-12 {
    text-align: center;
    padding: 22px 10px;
    border: 1px dashed var(--border-color);
    border-radius: 10px;
    color: var(--text-muted);
    font-size: 12px;
}

.ff-empty-desc {
    color: var(--text-muted);
    font-size: 12px;
    margin-bottom: 12px;
}

.ff-empty-filter-ico {
    font-size: 22px;
    margin-bottom: 8px;
    display: block;
    opacity: 0.35;
}

.ff-empty-ico {
    font-size: 26px;
    color: var(--accent-emerald);
    opacity: 0.55;
    margin-bottom: 8px;
}

.ff-empty-muted {
    text-align: center;
    color: var(--text-muted);
    padding: 20px;
}

.ff-empty-muted-15 {
    text-align: center;
    color: var(--text-muted);
    padding: 15px;
}

.ff-empty-muted-18 {
    text-align: center;
    color: var(--text-muted);
    padding: 18px;
    font-size: 12px;
}

.ff-empty-note {
    font-size: 12px;
    color: var(--text-muted);
    margin: 0;
}

.ff-empty-red {
    text-align: center;
    color: var(--accent-red);
    padding: 15px;
}

.ff-empty-title {
    color: var(--text-main);
    font-weight: 600;
    margin-bottom: 4px;
}

.ff-error-box {
    color: var(--accent-red);
    font-size: 12px;
    padding: 14px;
}

.ff-flex-1 {
    flex: 1;
}

.ff-fs-11 {
    font-size: 11px;
}

.ff-group-form {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 15px;
    max-width: 620px;
    align-items: center;
}

.ff-grow-160 {
    flex-grow: 1;
    min-width: 160px;
}

.ff-hidden {
    display: none;
}

.ff-ico-muted {
    color: var(--text-muted);
    margin-inline-end: 4px;
}

.ff-indigo-500 {
    color: var(--accent-indigo);
    font-weight: 500;
}

.ff-input-200 {
    width: 100%;
    max-width: 200px;
}

.ff-input-iconpad-45 {
    padding-inline-start: 45px;
    width: 100%;
}

.ff-input-iconpad-62 {
    padding-inline-start: 62px;
    width: 100%;
}

#schedule-modal .ff-mb-10 {
    margin-bottom: 10px;
}

#schedule-modal .ff-mb-12 {
    margin-bottom: 12px;
}

#export-modal .ff-modal-desc {
    margin-bottom: 15px;
    color: var(--text-muted);
    font-size: 13px;
}

.ff-modal-title {
    margin: 0;
    font-size: 15px;
}

.ff-mono-strong {
    font-weight: 600;
    color: var(--text-main);
    font-family: var(--font-mono);
}

.ff-mt-20 {
    margin-top: 20px;
}

.ff-mt-6 {
    margin-top: 6px;
}

.ff-muted {
    color: var(--text-muted);
}

.ff-muted-11 {
    color: var(--text-muted);
    font-size: 11px;
}

.ff-muted-italic {
    color: var(--text-muted);
    font-style: italic;
}

.ff-muted-w500 {
    color: var(--text-muted);
    font-weight: 500;
}

.ff-new-db-btn {
    margin-top: auto;
    width: 100%;
    justify-content: center;
    flex-shrink: 0;
}

.ff-nowrap-end {
    text-align: end;
    white-space: nowrap;
}

.ff-param-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

#modal-parameters-manager .ff-pm-divider {
    margin: 20px 0;
    border-top: 1px solid var(--border-color);
    position: relative;
}

#modal-parameters-manager .ff-pm-head {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--text-muted);
}

#modal-parameters-manager .ff-pm-sub {
    font-size: 11px;
    color: var(--text-muted);
}

#modal-parameters-manager .ff-pm-tag {
    background: rgba(var(--accent-primary-rgb), 0.12);
    color: var(--accent-primary);
    padding: 1px 6px;
    border-radius: 4px;
    font-size: 10px;
}

#modal-parameters-manager .ff-pm-title {
    position: absolute;
    top: -10px;
    inset-inline-start: 20px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    background: var(--bg-surface, #0C1222);
    padding: 0 10px;
    color: var(--accent-primary);
}

.ff-prefix-ico {
    position: absolute;
    inset-inline-start: 12px;
    color: var(--text-muted);
    font-size: 13px;
    font-weight: 600;
}

.ff-prefix-ico-end {
    position: absolute;
    inset-inline-end: 12px;
    top: 11px;
    color: var(--text-muted);
    pointer-events: none;
}

.ff-prefix-wrap {
    position: relative;
    display: flex;
    align-items: center;
}

#modal-report-editor .ff-rep-clock {
    color: var(--accent-primary);
    font-size: 13px;
}

#modal-report-editor .ff-rep-flex-none {
    flex: 0 0 auto;
    width: auto;
}

#modal-report-editor .ff-rep-hr {
    border-color: var(--border-color);
    margin: 20px 0;
}

#modal-report-editor .ff-rep-preview {
    display: none;
    color: var(--text-muted);
    margin-top: 4px;
    display: block;
}

#modal-report-editor .ff-rep-status-text {
    font-size: 12px;
    color: var(--text-secondary);
}

.ff-scroll-mt15 {
    overflow-x: auto;
    margin-top: 15px;
}

.ff-secondary {
    color: var(--text-secondary);
}

.ff-secondary-11 {
    font-size: 11px;
    color: var(--text-secondary);
}

.ff-secondary-12 {
    color: var(--text-secondary);
    font-size: 12px;
}

#schedule-modal .ff-sm-card-w {
    max-width: 850px;
    width: 90vw;
}

#schedule-modal .ff-sm-dest-box {
    background: rgba(255, 255, 255, 0.02);
    border: 1px dashed rgba(255, 255, 255, 0.1);
    border-radius: 6px;
    padding: 12px;
    margin-bottom: 12px;
}

#schedule-modal .ff-sm-form-panel {
    border-inline-start: 1px solid var(--border-color);
    padding-inline-start: 20px;
}

#schedule-modal .ff-sm-hint {
    font-size: 12px;
    color: var(--text-muted);
    margin-top: 4px;
    display: block;
}

#schedule-modal .ff-sm-history-box {
    max-height: 180px;
    overflow-y: auto;
    background: rgba(0, 0, 0, 0.1);
    border-radius: 8px;
    border: 1px solid var(--border-color);
    padding: 6px;
}

#schedule-modal .ff-sm-history-sec {
    margin-top: 20px;
    border-top: 1px solid var(--border-color);
    padding-top: 15px;
}

#schedule-modal .ff-sm-input {
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 8px;
    border-radius: 8px;
}

#schedule-modal .ff-sm-input-mt6 {
    margin-top: 6px;
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 8px;
    border-radius: 8px;
}

#schedule-modal .ff-sm-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-muted);
    margin-bottom: 4px;
    text-transform: uppercase;
}

#schedule-modal .ff-sm-mono {
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 8px;
    border-radius: 8px;
    font-family: var(--font-mono);
}

#schedule-modal .ff-sm-multiselect {
    height: 80px;
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 6px;
    border-radius: 8px;
}

#schedule-modal .ff-sm-scroll-box {
    overflow-x: auto;
    background: rgba(0, 0, 0, 0.1);
    border-radius: 8px;
    border: 1px solid var(--border-color);
    padding: 6px;
}

#schedule-modal .ff-sm-section-title {
    margin-bottom: 12px;
    display: flex;
    align-items: center;
    gap: 6px;
}

#schedule-modal .ff-sm-select {
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 8px;
    border-radius: 8px;
    height: 35px;
}

#schedule-modal .ff-sm-table {
    width: 100%;
    font-size: 12px;
    margin: 0;
}

#schedule-modal .ff-sm-textarea {
    width: 100%;
    box-sizing: border-box;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 8px;
    border-radius: 8px;
    resize: vertical;
}

.ff-sql-area {
    width: 100%;
    font-family: monospace;
    font-size: 12px;
}

.ff-strong {
    font-weight: 600;
    color: var(--text-main);
}

.ff-strong-500 {
    font-weight: 500;
    color: var(--text-primary);
}

.ff-swatch-44 {
    width: 44px;
    height: 32px;
    padding: 2px;
    cursor: pointer;
}

.ff-table-max {
    width: 100%;
    max-width: 680px;
}

.ff-table-w {
    width: 100%;
    font-size: 12px;
}

.ff-tag-accent {
    font-size: 10px;
    color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.1);
    padding: 1px 6px;
    border-radius: 4px;
}

.ff-tag-muted {
    font-size: 10px;
    color: var(--text-muted);
}

.ff-tag-muted-cap {
    font-size: 10px;
    color: var(--text-muted);
    text-transform: capitalize;
}

.ff-td-empty {
    text-align: center;
    color: var(--text-muted);
    padding: 20px;
}

.ff-th-130 {
    width: 130px;
    text-align: end;
}

.ff-th-150 {
    width: 150px;
    text-align: end;
}

.ff-th-60 {
    width: 60px;
    text-align: end;
}

.ff-th-80 {
    width: 80px;
    text-align: end;
}

.ff-th-90 {
    width: 90px;
    text-align: end;
}

#modal-report-editor .ff-tip-neg {
    margin-top: -10px;
    margin-bottom: 15px;
}

#modal-report-editor .ff-tip-zero {
    margin: 0 0 10px;
}

.ff-view-form {
    margin-top: 18px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border-color);
}

.ff-w-100 {
    width: 100%;
}

.ff-w500 {
    font-weight: 500;
}

/* ---- Touch targets on coarse pointers (impeccable audit P2) ---- */
@media (pointer: coarse) {
    .sidebar-db-option button {
        min-height: 44px;
        min-width: 44px;
    }

    .lang-switch-btn {
        min-height: 44px;
        min-width: 44px;
    }

    .ff-chip-5-9 {
        min-height: 44px;
    }
}

/* Parameters manager row card: de-inlined (impeccable audit P2). */
.param-row-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-radius: 10px;
    border: 1px solid var(--border-color);
    background: var(--bg-surface);
    transition: border-color 0.2s;
}

/* Views form headings: de-inlined (impeccable audit P3). */
.ff-view-form h4 {
    margin: 0 0 4px;
}

.ff-view-form .form-tip {
    margin-block-start: 0;
}

/* Sync frequency select + one-line preview (impeccable distill): the
   9-card picker collapsed to a labelled select; the preview sits under it. */
.sync-next-run {
    margin: 8px 0 0;
    font-size: 11px;
    color: var(--text-secondary);
}

/* ---- Modal exit: softer than the entrance (make-interfaces-feel-better
       review). 0.15s fade + 8px settle; the global reduced-motion
       kill-switch collapses the animation and main.js skips the delay. ---- */
.modal-overlay.modal-closing {
    animation: modalFadeOut 0.15s ease-out forwards;
}

.modal-overlay.modal-closing .modal-card {
    animation: modalSlideOut 0.15s ease-out forwards;
}

@keyframes modalFadeOut {
    to {
        opacity: 0;
    }
}

@keyframes modalSlideOut {
    to {
        transform: translateY(8px);
        opacity: 0;
    }
}

/* ---- Dense-desktop hit areas (make-interfaces-feel-better review): the
       coarse-pointer pass already set 44px min-heights on touch; on fine
       pointers these compact controls get an invisible vertical extension
       (the same ::after pattern .widget-actions already uses). Horizontal is
       deliberately NOT extended -- adjacent controls (EN/AR pair, bell vs
       account menu) must never overlap hit areas. ---- */
@media (pointer: fine) {

    .sidebar-db-option .db-move-btn,
    .lang-switch-btn {
        position: relative;
    }

    .sidebar-db-option .db-move-btn::after,
    .topbar-alert-bell::after,
    .lang-switch-btn::after {
        content: "";
        position: absolute;
        inset: -8px 0;
        border-radius: 8px;
    }

    .ff-chip-5-9 {
        min-height: 40px;
    }
}

/* ---- Sync record counts update on every poll: tabular digits keep the
       columns from shifting (make-interfaces-feel-better review). ---- */
#sync-reports-list .sync-count-col,
.sync-report-card-meta {
    font-variant-numeric: tabular-nums;
}

/* Widget editor -> Axis Labels: two checkboxes stacked, matching the spacing
   of the other checkbox groups in this modal. */
.axis-labels-toggles {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 4px;
}

/* ---------------------------------------------------------------------------
   GRID REFRESH STATE

   Between a filter change and the new rows arriving, the grid showed the
   PREVIOUS parameters' figures at full opacity -- indistinguishable from a
   fresh answer. The veil says "this is not the answer to what you just asked"
   without throwing the old numbers away, so a reader keeps their bearings and
   nothing reflows.

   Note this is NOT the same idea as .dashboard-freshness-banner, which means
   "the synced source data is old". Different problem, different wording.
--------------------------------------------------------------------------- */
.dashboard-refresh-bar {
    height: 2px;
    margin: 0 0 6px;
    border-radius: 2px;
    background: rgba(var(--accent-primary-rgb), 0.15);
    overflow: hidden;
}

.dashboard-refresh-bar>span {
    display: block;
    height: 100%;
    width: 35%;
    border-radius: 2px;
    background: var(--accent-primary);
    animation: refreshBarSlide 1.1s ease-in-out infinite;
}

@keyframes refreshBarSlide {
    0% {
        transform: translateX(-100%);
    }

    100% {
        transform: translateX(340%);
    }
}

/* Cards keep their layout and stay selectable -- only their confidence drops.
   Interaction is blocked at the handler (see _drillBlockedByPendingRefresh in
   charts.js) rather than with pointer-events, so a figure can still be copied
   during a long query. */
.widgets-grid.grid-updating .widget-card {
    opacity: 0.45;
    transition: opacity 0.15s ease-out;
}

.dashboard-refresh-notice {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 10px;
    padding: 8px 12px;
    border-radius: 8px;
    font-size: 12px;
    color: var(--text-primary);
    background: rgba(var(--accent-amber-rgb, 245, 158, 11), 0.10);
    border: 1px solid rgba(var(--accent-amber-rgb, 245, 158, 11), 0.35);
}

.dashboard-refresh-notice i {
    color: var(--accent-amber, #F59E0B);
}

.dashboard-refresh-notice .refresh-notice-text {
    flex: 1;
    min-width: 0;
}

/* The bar's job is to say "something is happening"; with motion reduced the
   animation is the wrong way to say it, so the bar becomes a static rule and
   the veil carries the meaning on its own, without a transition. */
@media (prefers-reduced-motion: reduce) {
    .dashboard-refresh-bar>span {
        animation: none;
        width: 100%;
    }

    .widgets-grid.grid-updating .widget-card {
        transition: none;
    }
}

/* ==========================================================================
   USER PREFERENCES -- TWO-PANE LAYOUT                          (2026-08-25)
   Section rail on the inline-start edge, one pane per settings group on the
   inline-end side. Everything here uses logical properties (border-inline-*,
   padding-inline-*, text-align: start) so the rail moves to the right-hand
   edge under dir="rtl" with no locale-specific override.
   ========================================================================== */

/* Wider than the shared .modal-xlarge 900px: the rail eats ~212px of it and
   the theme grid needs the remainder to fit six tiles on one row. Scoped to
   this modal only, the same way #modal-widget-editor takes its own width. */
.user-prefs-modal-card {
    max-width: 1040px;
    width: 95vw;
}

/* ── the rail ── */
.prefs-rail {
    flex: 0 0 212px;
    min-width: 0;
    padding: 18px 12px;
    overflow-y: auto;
    background: rgba(var(--accent-primary-rgb), 0.05);
    border-inline-end: 1px solid var(--border-color);
}

.prefs-rail-group {
    margin-bottom: 18px;
}

.prefs-rail-group:last-child {
    margin-bottom: 0;
}

.prefs-rail-label {
    margin: 0 0 8px;
    padding-inline: 10px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.prefs-rail-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    margin-bottom: 2px;
    padding: 9px 11px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary);
    font-family: inherit;
    font-size: 13.5px;
    text-align: start;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.prefs-rail-item i {
    width: 16px;
    flex-shrink: 0;
    text-align: center;
    font-size: 12.5px;
    opacity: 0.85;
}

.prefs-rail-item:hover {
    background: rgba(var(--accent-primary-rgb), 0.10);
    color: var(--text-main);
}

.prefs-rail-item.active {
    background: var(--accent-primary);
    color: var(--text-on-accent, #ffffff);
    font-weight: 600;
}

.prefs-rail-item.active i {
    opacity: 1;
}

/* ── the pane side ── */
.prefs-pane-scroll {
    flex: 1;
    min-width: 0;
    padding: 22px 26px;
    overflow-y: auto;
}

.prefs-pane {
    display: none;
}

.prefs-pane.active {
    display: block;
}

.prefs-pane-title {
    margin: 0 0 4px;
    font-family: var(--font-primary);
    font-size: 18px;
    font-weight: 650;
    color: var(--text-main);
}

.prefs-pane-desc {
    margin: 0 0 18px;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--text-muted);
}

.prefs-block-label {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px;
    margin: 24px 0 2px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

.prefs-block-hint {
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--text-muted);
}

.prefs-action-row {
    margin-top: 12px;
}

/* ── live preview strip ── */
.prefs-live-preview {
    margin-bottom: 4px;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    overflow: hidden;
    background: rgba(var(--accent-primary-rgb), 0.04);
}

.plp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 8px 14px;
    border-bottom: 1px solid var(--border-color);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
}

.plp-tag {
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.plp-body {
    display: flex;
    align-items: stretch;
    gap: 14px;
    padding: 13px 14px;
}

.plp-kpi {
    display: flex;
    flex: 1;
    min-width: 0;
    flex-direction: column;
    gap: 1px;
    padding: 9px 11px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
}

.plp-k {
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.plp-v {
    font-family: var(--font-primary);
    font-size: 20px;
    font-weight: 700;
    line-height: 1.15;
    color: var(--accent-primary);
}

.plp-d {
    font-size: 10.5px;
    font-weight: 600;
}

.plp-up {
    color: var(--accent-emerald);
}

.plp-down {
    color: var(--accent-red);
}

.plp-bars {
    display: flex;
    flex: 1.1;
    min-width: 0;
    align-items: flex-end;
    gap: 6px;
    height: 62px;
}

.plp-bars i {
    flex: 1;
    border-radius: 3px 3px 0 0;
    background: var(--accent-primary);
    opacity: 0.85;
}

.plp-bars i:nth-child(1) { height: 40%; }
.plp-bars i:nth-child(2) { height: 64%; }
.plp-bars i:nth-child(3) { height: 48%; }
.plp-bars i:nth-child(4) { height: 86%; }
.plp-bars i:nth-child(5) { height: 70%; }
.plp-bars i:nth-child(6) { height: 96%; }
.plp-bars i:nth-child(7) { height: 58%; }

.plp-foot {
    margin: 0;
    padding: 0 14px 10px;
    font-size: 10.5px;
    color: var(--text-muted);
}

/* ── typeface pills: the specimen shows the face before it is applied ── */
.prefs-font-pills .option-pill {
    padding-inline-start: 10px;
}

.font-specimen {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    padding: 2px 5px;
    border: 1px solid var(--border-color);
    border-radius: 5px;
    font-size: 12px;
    line-height: 1.2;
}

.font-specimen-inter {
    font-family: "Inter", "Segoe UI", system-ui, sans-serif;
}

.font-specimen-mono {
    font-family: "JetBrains Mono", "Courier New", monospace;
}

/* Narrow viewports: the rail lies down across the top and scrolls sideways
   rather than squeezing the pane to nothing. */
@media (max-width: 760px) {
    .user-prefs-modal-body {
        flex-direction: column;
    }

    .prefs-rail {
        flex: 0 0 auto;
        display: flex;
        gap: 6px;
        padding: 10px 12px;
        overflow-x: auto;
        overflow-y: hidden;
        border-inline-end: none;
        border-bottom: 1px solid var(--border-color);
    }

    .prefs-rail-group {
        display: flex;
        gap: 6px;
        margin-bottom: 0;
    }

    .prefs-rail-label {
        display: none;
    }

    .prefs-rail-item {
        width: auto;
        margin-bottom: 0;
        white-space: nowrap;
    }

    .prefs-pane-scroll {
        padding: 18px;
    }

    .plp-body {
        flex-wrap: wrap;
    }
}

/* Selected-tile tick. On Ocean and Daylight the tile background and the modal
   card behind it are the same colour, so .active's border tint alone is close
   to invisible there -- this is what actually says "this is the one". */
.theme-card.active::after {
    content: "✓";
    position: absolute;
    inset-block-start: 5px;
    inset-inline-end: 5px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--accent-primary);
    color: var(--text-on-accent, #ffffff);
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
}

/* A single-line field does not get more usable by being 770px wide. Pill
   groups are deliberately excluded -- those wrap and want the whole row. */
.prefs-pane .form-group input,
.prefs-pane .form-group select {
    max-width: 460px;
}

/* ==========================================================================
   SETTINGS -- RAIL + PANEL LAYOUT                              (2026-08-25)
   Everything below is inside a min-width query on purpose: under 900px the
   rules never apply and the sub-tab nav keeps the horizontal strip it already
   had, which is the right shape for a narrow viewport. So there is nothing to
   undo -- the strip IS the small-screen fallback.

   switchSubTab() needed no change: it only toggles .active/aria-* on the same
   buttons and panels, which a rail flips exactly as a strip did.
   ========================================================================== */
@media (min-width: 900px) {
    #settings-tab .settings-shell {
        display: flex;
        align-items: flex-start;
        gap: 26px;
    }

    #settings-tab .sub-tabs-nav {
        flex: 0 0 226px;
        flex-direction: column;
        flex-wrap: nowrap;
        gap: 18px;
        margin-bottom: 0;
        padding-bottom: 0;
        padding-inline-end: 16px;
        border-bottom: 0;
        border-inline-end: 1px solid var(--border-color);
        /* .app-main-wrapper is the scrollport, so the rail stays put while a
           long panel scrolls past it. */
        position: sticky;
        top: 0;
        max-height: calc(100vh - 150px);
        overflow-y: auto;
    }

    #settings-tab .sub-tab-group {
        flex-direction: column;
        align-items: stretch;
        gap: 2px;
    }

    #settings-tab .sub-tab-group-label {
        display: block;
        margin-bottom: 6px;
        padding-inline: 11px;
        font-size: 10.5px;
    }

    /* The underline the strip used becomes a bar on the inline-start edge.
       Still a tint plus a rule, never a filled pill -- accent stays reserved
       for actions, same decision the channel-strip block above records. */
    #settings-tab .sub-tab-btn {
        width: 100%;
        justify-content: flex-start;
        text-align: start;
        padding: 9px 11px;
        border-bottom: 0;
        border-radius: 8px;
        border-inline-start: 2px solid transparent;
    }

    /* Labels line up as a column even though the glyphs differ in width. */
    #settings-tab .sub-tab-btn i {
        width: 16px;
        flex-shrink: 0;
        text-align: center;
    }

    #settings-tab .sub-tab-btn:hover {
        border-bottom: 0;
        background: rgba(var(--accent-primary-rgb), 0.07);
    }

    #settings-tab .sub-tab-btn.active {
        border-bottom: 0;
        border-inline-start-color: var(--accent-primary);
        background: rgba(var(--accent-primary-rgb), 0.10);
    }

    #settings-tab .sub-tab-panels {
        flex: 1;
        min-width: 0;
    }
}
