/* XTFusionDirect Portal — shared styles. Theme via [data-theme] on <html>, default dark. */

/* Tells the browser which palette its own UA-drawn chrome (select dropdown popups, number-input
   spinner arrows, checkboxes, date pickers, scrollbars) should use. Without this, those native
   widgets default to light-mode rendering regardless of the custom [data-theme="dark"] palette
   below — which is what made some text (e.g. the number spinner on Row Limit Default, and select
   dropdown option lists across Pipeline Detail) unreadable in dark mode even though every custom
   element's color already came from a theme variable. */
:root { color-scheme: light; }
html[data-theme="dark"] { color-scheme: dark; }

:root {
    --bg-primary: #f4f6fb;
    --bg-panel: #ffffff;
    --bg-elevated: #ffffff;
    --border-color: #d8dee9;
    --text-primary: #1a2233;
    --text-muted: #6b7488;
    --accent: #2f6fed;
    --accent-hover: #1f56c9;
    --success: #1c8a4b;
    --success-on: #ffffff;
    --warning: #b5760a;
    --warning-on: #ffffff;
    --danger: #c73838;
    --sidebar-bg: #101a30;
    --sidebar-text: #c9d2e6;
    --navbar-bg: #101a30;
}

html[data-theme="dark"] {
    --bg-primary: #0d1220;
    --bg-panel: #151d33;
    --bg-elevated: #1b2540;
    --border-color: #2a3654;
    --text-primary: #e7ecf7;
    --text-muted: #aab6d1;
    --accent: #4d84ff;
    --accent-hover: #6c99ff;
    --success: #35c47a;
    --success-on: #0d1220;
    --warning: #e0a634;
    --warning-on: #0d1220;
    --danger: #ef5757;
    --sidebar-bg: #0a1122;
    --sidebar-text: #c9d2e6;
    --navbar-bg: #0a1122;
}

* { box-sizing: border-box; }
body { margin: 0; font-family: 'Segoe UI', Arial, sans-serif; background: var(--bg-primary); color: var(--text-primary); }
a { color: var(--accent); text-decoration: none; }

/* Navbar */
.portal-navbar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 24px; background: var(--navbar-bg); border-bottom: 1px solid var(--border-color);
}
.brand-title { font-size: 20px; font-weight: 700; color: #fff; }
.brand-subtitle { font-size: 12px; color: var(--sidebar-text); }
.navbar-right { display: flex; align-items: center; gap: 16px; }
.welcome-text { color: #fff; font-size: 13px; }
.welcome-date { color: var(--sidebar-text); font-size: 11px; }
.theme-toggle { background: none; border: none; font-size: 18px; cursor: pointer; }
.admin-link { color: #fff !important; font-size: 13px; cursor: pointer; }
.tenant-switcher { min-width: 220px; }

/* Sidebar */
.xtfd-sidebar { width: 220px; background: var(--sidebar-bg); padding: 16px 0; flex-shrink: 0; }
.sidebar-nav-link {
    display: block; padding: 10px 20px; color: var(--sidebar-text) !important; font-size: 14px;
    border-left: 3px solid transparent;
}
.sidebar-nav-link:hover, .sidebar-nav-link.active { background: rgba(255,255,255,0.06); border-left-color: var(--accent); color: #fff !important; }
.sidebar-divider { height: 1px; background: rgba(255,255,255,0.08); margin: 12px 20px; }
.sidebar-section-label { padding: 0 20px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); margin-bottom: 4px; }

/* Login */
.bg-xtfd-login { background: var(--sidebar-bg); min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.login-container { display: flex; flex-direction: column; align-items: center; gap: 20px; padding: 20px; }
.login-card { background: var(--bg-panel); border-radius: 10px; padding: 32px; width: 380px; box-shadow: 0 10px 40px rgba(0,0,0,0.35); }
.login-header { text-align: center; margin-bottom: 20px; }
.login-title { font-size: 22px; font-weight: 700; color: var(--text-primary); }
.login-subtitle { font-size: 13px; color: var(--text-muted); }
.login-form-group { margin-bottom: 14px; }
.login-error { color: var(--danger); display: block; margin-top: 8px; font-size: 13px; }
.login-instructions { margin-top: 20px; font-size: 12px; color: var(--text-muted); }
.login-instructions-title { font-weight: 600; margin-bottom: 6px; }

/* Forms / buttons */
.form-input {
    width: 100%; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: 6px;
    background: var(--bg-elevated); color: var(--text-primary); font-size: 13px;
}
.form-label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.form-inline-row { display: flex; align-items: center; gap: 10px; margin: 10px 0; flex-wrap: wrap; }
/* .form-input defaults to width:100%, which is right for a lone field filling its own row, but
   inside .form-inline-row two or more 100%-wide fields can't fit side by side and each wraps onto
   its own line — override to share the row's space instead, wrapping only when genuinely out of room. */
.form-inline-row .form-input { width: auto; flex: 1 1 140px; min-width: 0; }
.btn-portal { padding: 8px 16px; border-radius: 6px; border: none; cursor: pointer; font-size: 13px; font-weight: 600; }
.btn-primary-portal { background: var(--accent); color: #fff; }
.btn-primary-portal:hover { background: var(--accent-hover); }
.btn-secondary-portal { background: var(--bg-elevated); color: var(--text-primary); border: 1px solid var(--border-color); }
.btn-danger-portal { background: var(--danger); color: #fff; }
/* The Enable/Disable pair in the Schedules grid: green for the action that turns a
   schedule on, amber for the one that turns it off. Deliberately not --danger — disabling
   is reversible and Delete, sitting in the same cell, isn't. The --*-on foregrounds are
   per-theme because the dark palette's green/amber are bright enough that white text on
   them fails contrast, while the light palette's are dark enough that it's required. */
.btn-success-portal { background: var(--success); color: var(--success-on); }
.btn-warning-portal { background: var(--warning); color: var(--warning-on); }
.btn-success-portal:hover, .btn-warning-portal:hover { filter: brightness(1.08); }
.btn-small { padding: 4px 10px; font-size: 12px; }

/* Button loading state — spinner replaces the label while a Gateway request is in flight */
.btn-loading { position: relative; color: transparent !important; pointer-events: none; }
.btn-loading::after {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    width: 14px; height: 14px;
    margin: -7px 0 0 -7px;
    border: 2px solid rgba(255,255,255,0.35);
    border-top-color: #fff;
    border-radius: 50%;
    animation: btn-spin 0.7s linear infinite;
}
.btn-secondary-portal.btn-loading::after { border-color: var(--border-color); border-top-color: var(--accent); }
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* Fixed-schema table input (schema is always dbo — admin only types the bare table name) */
.pd-schema-prefix-group { display: inline-flex; align-items: stretch; width: 220px; vertical-align: middle; }
.pd-schema-prefix {
    display: flex; align-items: center;
    padding: 0 8px;
    background: var(--bg-elevated);
    border: 1px solid var(--border-color);
    border-right: none;
    border-radius: 6px 0 0 6px;
    color: var(--text-muted);
    font-size: 13px;
}
.pd-schema-prefix-input.form-input { border-radius: 0 6px 6px 0; width: auto; flex: 1; min-width: 0; }

/* Gateway/query request status lines — full-contrast text-primary rather than the dimmer
   text-muted, since these convey active/actionable state (waiting/failed/succeeded), not
   decorative caption text. */
.pd-status-text { color: var(--text-primary); font-size: 13px; font-weight: 600; margin: 10px 0; }

/* Layout helpers */
.page-title { font-size: 24px; margin: 0 0 4px 0; }
.page-header-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.text-muted { color: var(--text-muted); }

/* Deactivated accounts in PlatformAdmin/Users.aspx — dimmed so an active roster still reads at a
   glance when "Show deactivated" is on, without hiding the row's own actions. */
.data-grid tr.row-inactive td { opacity: 0.55; }
.data-grid tr.row-inactive td .btn-portal { opacity: 1; }
.panel-section { background: var(--bg-panel); border: 1px solid var(--border-color); border-radius: 8px; padding: 18px; margin: 18px 0; }
.panel-section-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.section-title { font-size: 15px; margin: 0; }
.pd-grid { display: grid; grid-template-columns: 1fr 2fr; gap: 8px 16px; align-items: center; margin: 16px 0; }
.pd-action-bar { display: flex; gap: 8px; }
/* Full-contrast text-primary, not the dimmer text-muted — same reasoning as .pd-status-text
   above: this is substantive explanatory copy the user needs to actually read (e.g. which load
   strategy to pick, what Post-Load Procedures does), not decorative caption text, so it shouldn't
   be styled at reduced emphasis. */
.pd-panel-note { color: var(--text-primary); margin: 8px 0 0; font-size: 13px; line-height: 1.5; }
.pd-load-strategy-config { margin-top: 12px; }
.pd-post-load-procs { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.pd-post-load-proc-row { display: flex; align-items: center; gap: 8px; }
.pd-post-load-proc-row .form-input { flex: 1; }
.pd-match-key-cols { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.pd-match-key-row { display: flex; align-items: center; gap: 8px; }
.pd-match-key-row .form-input { flex: 1; }
.pd-inline-checkbox { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-primary); cursor: pointer; }
.pd-inline-checkbox input[type="checkbox"] { width: auto; margin: 0; }

/* Alerts / badges / toast */
.alert-info-portal { background: rgba(77,132,255,0.12); border: 1px solid var(--accent); padding: 12px 16px; border-radius: 6px; color: var(--text-primary); }
.alert-success-portal { background: rgba(53,196,122,0.15); border: 1px solid var(--success); padding: 10px 14px; border-radius: 6px; color: var(--text-primary); }
.alert-danger-portal { background: rgba(239,87,87,0.12); border: 1px solid var(--danger); padding: 10px 14px; border-radius: 6px; color: var(--text-primary); }
/* The base class carries a neutral fill on purpose: a status with no variant rule below used to
   render with no background and no color at all — bare text sitting where a pill was expected.
   Now an unrecognised status still reads as a pill, just an uncolored one.

   No text-transform: statuses are written in the case they should read in, and uppercasing them
   turned the multi-word ones into a wall of letters ("SUCCESS · WARNINGS", "NOT REGISTERED").
   The fill, radius and weight are what make it a pill; the shouting was never doing that work. */
.status-badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 700; margin-left: 8px; background: rgba(170,182,209,0.15); color: var(--text-muted); }
.status-badge.Draft { background: rgba(224,166,52,0.2); color: var(--warning); }
.status-badge.Published { background: rgba(53,196,122,0.2); color: var(--success); }
.status-badge.Running { background: rgba(77,132,255,0.2); color: var(--accent); }
.status-badge.Success { background: rgba(53,196,122,0.2); color: var(--success); }
.status-badge.SuccessWithWarnings { background: rgba(224,166,52,0.2); color: var(--warning); }
.status-badge.Failed { background: rgba(239,87,87,0.2); color: var(--danger); }
.status-badge.Online { background: rgba(53,196,122,0.2); color: var(--success); }
.status-badge.Offline, .status-badge.Revoked, .status-badge.NeverConnected { background: rgba(239,87,87,0.15); color: var(--danger); }
.status-badge.NotRegistered { background: rgba(170,182,209,0.15); color: var(--text-muted); }
/* "never ran" is a real state worth showing in the same shape as the others, not plain grey text. */
.status-badge.Never { background: rgba(170,182,209,0.15); color: var(--text-muted); font-style: italic; }
/* Schedule pill in the Pipelines grid. Same rounded shape as .status-badge, but not uppercase and
   with no left margin — it fills a cell of its own and carries a real recurrence ("Daily 03:30"),
   which uppercasing would make harder to read, not easier. */
.schedule-pill { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 600; white-space: nowrap; }
.schedule-pill.on { background: rgba(53,196,122,0.2); color: var(--success); }
.schedule-pill.off { background: rgba(170,182,209,0.15); color: var(--text-muted); }
.schedule-pill.broken { background: rgba(224,166,52,0.2); color: var(--warning); }
/* The recurrence preview line doubles as the validation message for the day picker — it is
   already where the user looks to see what they just built. */
.sch-preview-error { color: var(--danger); font-weight: 600; }

/* Small rounded type tag — same pill language as .status-badge, used for the Field Mapping
   table's Target Type column instead of plain (near-invisible in dark mode) text. Colored by
   type category (see TYPE_PILL_CATEGORIES in xtfd-app.js) so numeric/string/date/boolean columns
   are visually distinguishable at a glance rather than one uniform color for every type. */
.pd-type-pill { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 11px; font-weight: 700; text-transform: uppercase; }
.pd-type-pill.numeric { background: rgba(77,132,255,0.15); color: var(--accent); }
.pd-type-pill.string { background: rgba(53,196,122,0.15); color: var(--success); }
.pd-type-pill.date { background: rgba(224,166,52,0.2); color: var(--warning); }
.pd-type-pill.boolean { background: rgba(239,87,87,0.15); color: var(--danger); }
.pd-type-pill.other { background: rgba(170,182,209,0.15); color: var(--text-muted); }
/* Field Mapping's Target Type (data-type) column has nothing to show for a target with no schema
   to discover (Excel Export) — PipelineDetail toggles this class on #pdMappingTable rather than
   hiding each cell individually, since rows are torn down and rebuilt on every renderMapping()
   call (Map Fields, query changes, unpivot changes) and a per-cell inline style would need to be
   reapplied on every one of those instead of just once. */
.pd-hide-datatype-col .pd-map-datatype-col { display: none; }
.toast-portal { position: fixed; bottom: 20px; right: 20px; z-index: 9999; display: flex; flex-direction: column; gap: 8px; }
.toast-item { background: var(--bg-elevated); border: 1px solid var(--border-color); border-left: 4px solid var(--accent); padding: 10px 16px; border-radius: 6px; box-shadow: 0 6px 20px rgba(0,0,0,0.3); font-size: 13px; }
.toast-item.error { border-left-color: var(--danger); }
.toast-item.success { border-left-color: var(--success); }

/* Modal (popup dialogs — replaces native alert/confirm/prompt) */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.55); display: flex; align-items: center; justify-content: center; z-index: 10000; }
.modal-box { background: var(--bg-elevated); border: 1px solid var(--border-color); border-radius: 8px; padding: 24px; width: 420px; max-width: 90vw; box-shadow: 0 10px 40px rgba(0,0,0,0.4); }
.modal-title { font-size: 16px; font-weight: 600; margin: 0 0 16px; color: var(--text-primary); }
.modal-message { font-size: 14px; margin: 0; color: var(--text-primary); line-height: 1.5; }
.modal-field { margin-bottom: 12px; }
.modal-field:last-child { margin-bottom: 0; }
.modal-field label { display: block; font-size: 12px; color: var(--text-muted); margin-bottom: 4px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 22px; }

/* Data grid */
.table-scroll-x { overflow-x: auto; }
.data-grid { width: 100%; border-collapse: collapse; background: var(--bg-panel); border: 1px solid var(--border-color); border-radius: 8px; overflow: hidden; }
.data-grid th, .data-grid td { padding: 10px 12px; border-bottom: 1px solid var(--border-color); text-align: left; font-size: 13px; }
.data-grid th { background: var(--bg-elevated); font-size: 11px; text-transform: uppercase; color: var(--text-muted); }
.data-grid tr:last-child td { border-bottom: none; }
.data-grid a.row-link { cursor: pointer; }
.data-grid tr.row-clickable { cursor: pointer; }
.data-grid tr.row-clickable:hover td { background: var(--bg-elevated); }

/* Field Mapping's per-rule parameter pairs (Find/Replace, Min/Max) — shown only when the
   Transform Rules dropdown is set to the matching rule (toggled in addMappingRow's change listener). */
.pd-rule-params-row { display: flex; gap: 6px; margin-top: 6px; }
.pd-rule-params-row .form-input { width: auto; flex: 1; min-width: 0; }

/* Query result preview — capped to ~5 visible rows, scrolls both ways since Oracle queries can
   return many columns. (No sticky header here: position:sticky on a <th> inside a
   border-collapse:collapse table is unreliable across browsers, so the header scrolls with the
   body rather than silently failing to stick in some of them.) */
.pd-query-result-scroll { max-height: 232px; overflow: auto; }
.pd-query-result-scroll table { width: max-content; min-width: 100%; }

/* SQL editor */
.sql-editor { width: 100%; font-family: 'Consolas', 'Courier New', monospace; font-size: 13px; padding: 12px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-elevated); color: var(--text-primary); resize: vertical; }

/* CodeMirror-based SQL editor (Query preview, PipelineDetail.aspx#pdSqlEditor) — same rounded/
   bordered panel look as .sql-editor above; token colors reuse the app's own theme variables so
   the highlighting matches both light and dark mode without a separate CodeMirror theme file. */
/* resize:vertical + overflow:auto gives the wrapper a native drag handle (bottom-right corner)
   for making the query box taller — xtfd-app.js's _initSqlEditor wires a ResizeObserver to keep
   CodeMirror's own internal layout in sync with whatever height the user drags it to. */
.xtfd-sql-cm.CodeMirror { height: 320px; min-height: 150px; resize: vertical; overflow: hidden; font-family: 'Consolas', 'Courier New', monospace; font-size: 13px; border: 1px solid var(--border-color); border-radius: 6px; background: var(--bg-elevated); color: var(--text-primary); }
.xtfd-sql-cm .CodeMirror-gutters { background: var(--bg-elevated); border-right: 1px solid var(--border-color); }
.xtfd-sql-cm .CodeMirror-linenumber { color: var(--text-muted); }
.xtfd-sql-cm .CodeMirror-cursor { border-left: 1px solid var(--text-primary); }
.xtfd-sql-cm .CodeMirror-selected { background: rgba(77, 132, 255, 0.25); }
.xtfd-sql-cm .cm-keyword { color: var(--accent); font-weight: 600; }
.xtfd-sql-cm .cm-string { color: var(--success); }
.xtfd-sql-cm .cm-number { color: var(--warning); }
.xtfd-sql-cm .cm-comment { color: var(--text-muted); font-style: italic; }
.xtfd-sql-cm .cm-operator { color: var(--text-primary); }
.xtfd-sql-cm .cm-variable-2 { color: var(--danger); }
.xtfd-sql-cm .cm-builtin { color: var(--accent); }
.xtfd-sql-cm .CodeMirror-placeholder { color: var(--text-muted); }
.xtfd-sql-cm-readonly { opacity: 0.6; }

/* Stat cards */
.stat-cards-row { display: flex; gap: 16px; margin: 16px 0; flex-wrap: wrap; }
.stat-card { background: var(--bg-panel); border: 1px solid var(--border-color); border-radius: 8px; padding: 16px 24px; min-width: 140px; }
.stat-card-value { font-size: 28px; font-weight: 700; }
.stat-card-value.ok { color: var(--success); }
.stat-card-value.bad { color: var(--danger); }
.stat-card-label { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
/* Secondary line on a stat card (e.g. "3 with warnings" under Succeeded) — the qualifier belongs
   with the number, not in a tile of its own. */
.stat-card-note { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
/* The Dashboard's stat cards already sit on --bg-panel; the Run Outcomes tiles sit INSIDE a
   .panel-section, so on the same background they'd be invisible boxes — lift them one step. */
.panel-section .stat-cards-row { margin-top: 0; }
.panel-section .stat-card { background: var(--bg-elevated); padding: 12px 20px; }
.panel-section .stat-card-value { font-size: 24px; }

/* Run Outcomes chart (Dashboard) — succeeded vs failed per day/month. Deliberately CSS-only:
   two series of bars sized in percent, colored from the theme variables so the light/dark toggle
   needs no redraw. Bar heights are set inline by XTFD.dashboard._renderChart. */
.run-chart-zone { font-size: 12px; color: var(--text-muted); }
.run-chart { display: flex; align-items: flex-start; gap: 8px; margin-top: 4px; }
.run-chart-yaxis {
    width: 32px; height: 160px; display: flex; flex-direction: column; justify-content: space-between;
    align-items: flex-end; font-size: 11px; color: var(--text-muted);
}
/* Columns share the width evenly and shrink as the range grows; below min-width the plot scrolls
   sideways rather than collapsing 90 days into slivers. */
.run-chart-plot { flex: 1; display: flex; align-items: flex-start; gap: 3px; overflow-x: auto; }
.run-chart-col { flex: 1 1 0; min-width: 12px; display: flex; flex-direction: column; align-items: center; }
/* Fixed height (matching .run-chart-yaxis) is what makes the bars' percentage heights resolve. */
.run-chart-bars {
    height: 160px; width: 100%; display: flex; align-items: flex-end; justify-content: center; gap: 2px;
    border-bottom: 1px solid var(--border-color); border-radius: 2px 2px 0 0;
}
.run-chart-col:hover .run-chart-bars { background: rgba(127, 143, 178, 0.12); }
.run-chart-bar { width: 42%; max-width: 14px; border-radius: 2px 2px 0 0; }
.run-chart-bar.ok { background: var(--success); }
.run-chart-bar.bad { background: var(--danger); }
/* A count of zero still draws a flat tick, so every column shows both series and an empty day is
   visibly empty rather than just missing. */
.run-chart-bar.zero { height: 2px; background: var(--border-color); }
.run-chart-xlabel { font-size: 10px; color: var(--text-muted); margin-top: 4px; height: 14px; white-space: nowrap; }
.run-chart-legend { display: flex; align-items: center; gap: 16px; margin-top: 10px; font-size: 12px; color: var(--text-muted); }
.run-chart-key { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; }
.run-chart-key.ok { background: var(--success); }
.run-chart-key.bad { background: var(--danger); }
.run-chart-hint { margin-left: auto; }
