@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Poppins:wght@400;500;600&display=swap');

:root {
  --ig-navy:        #162a3c;
  --ig-navy-deep:   #0b1a26;
  --ig-navy-darker: #131e2f;
  --ig-off-white:   #edeff2;
  --ig-white:       #ffffff;
  --ig-slate:       #53606b;
  --ig-slate-2:     #54595f;
  --ig-text:        #7a7a7a;
  --ig-border:      #dfe1e5;
  --ig-blue:        #6ec1e4;
  --ig-teal:        #00d2a9;
  --ig-green:       #61ce70;
  --ig-red:         #c0392b;
}

html, body {
  margin: 0; background: var(--ig-off-white); color: #1f2933;
  font-family: 'Poppins', system-ui, sans-serif; font-size: 14px; line-height: 1.45;
}
* { box-sizing: border-box; }

/* ---- Layout modes ---- */
/* body.fixed-viewport is height:100vh + overflow:hidden, which stops the BODY
   scrolling but says nothing about the root element. Lock that too, so a
   fixed-viewport page cannot produce a window scrollbar no matter what a
   descendant does. :has() is Chromium 105+/Edge - the CSS below is unaffected
   where it is unsupported, this is belt and braces, not the mechanism. */
html:has(> body.fixed-viewport) { height: 100%; overflow: hidden; }


body.fixed-viewport, body.scroll-page { margin: 0; height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
body.fixed-viewport .app-header, body.scroll-page .app-header { flex: 0 0 auto; }
body.fixed-viewport .app-footer, body.scroll-page .app-footer { position: static; flex: 0 0 auto; }
body.fixed-viewport .app-body, body.scroll-page .app-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: row; overflow: hidden; }
body.scroll-page   .app-main { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 12px 18px; }
body.fixed-viewport .app-main { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; padding: 12px 18px; gap: 10px; overflow: hidden; }

/* ---- Header / footer ---- */
.app-header {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 24px;
  background: var(--ig-navy); color: var(--ig-white);
  padding: 12px 24px;
  border-bottom: 3px solid var(--ig-teal);
}
.brand { display: flex; align-items: center; gap: 12px; color: var(--ig-white); text-decoration: none; font-weight: 600; }
.brand-logo { height: 32px; width: auto; display: block; }
.brand-divider { width: 1px; height: 22px; background: rgba(255,255,255,0.3); }
.brand-name { font-weight: 500; font-size: 20px; letter-spacing: 0.01em; color: var(--ig-off-white); }

.user-strip { margin-left: auto; display: flex; align-items: center; gap: 14px; font-size: 13px; }
.logout { color: var(--ig-blue); text-decoration: none; }
.user-email { color: var(--ig-off-white); opacity: 0.85; }

/* ---- Left sidebar nav ----
   Hierarchy the eye can parse in a narrow rail: bold folders, muted leaves, a
   hairline guide down each nesting level (instead of ever-deeper padding), and a
   teal accent bar on the active page. */
/* The <nav> wrapper inside .app-sidebar. .app-sidebar is a flex column, but its
   gap applies to THIS element (its only child), not to the links inside it - so
   without a flex context here the links stack with no spacing at all. */
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.app-sidebar {
  flex: 0 0 300px; background: var(--ig-navy); border-right: 1px solid var(--ig-navy-deep);
  padding: 10px 8px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
  font-size: 13px; line-height: 1.3;
}

/* Leaf links: the lightest weight in the tree, so folders read as folders.
   Muted until hover/active; the active item gets a teal bar on its guide line. */
.sidebar-link {
  display: flex; align-items: center; gap: 8px; position: relative; min-width: 0;
  color: var(--ig-off-white); text-decoration: none;
  padding: 4px 8px; border-radius: 5px;
  font-weight: 400; font-size: 12.5px; opacity: 0.66;
}
.sidebar-link svg { width: 13px; height: 13px; flex: 0 0 auto; opacity: 0.8; }
.sidebar-link span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-link:hover { background: rgba(255,255,255,0.06); opacity: 1; }
.sidebar-link.active { background: rgba(0,210,169,0.12); color: var(--ig-teal); opacity: 1; font-weight: 500; }
.sidebar-link.active::before {
  content: ""; position: absolute; left: -11px; top: 6px; bottom: 6px;
  width: 2px; border-radius: 2px; background: var(--ig-teal);
}
.sidebar-divider { height: 1px; background: rgba(255,255,255,0.12); margin: 6px 8px; }
.sidebar-section-label {
  color: rgba(255,255,255,0.4); font-size: 9.5px; text-transform: uppercase;
  letter-spacing: 0.06em; font-weight: 600; padding: 6px 8px 2px;
}

/* ---- Collapsible sidebar groups (folders) ---- */
.sidebar-group-toggle {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer; list-style: none; color: var(--ig-white);
  font-weight: 600; font-size: 13px; letter-spacing: 0.01em;
  padding: 5px 8px; border-radius: 5px;
}
.sidebar-group-toggle::-webkit-details-marker { display: none; }
.sidebar-group-icon { width: 15px; height: 15px; flex: 0 0 auto; color: var(--ig-teal); opacity: 0.85; }
.sidebar-group-toggle span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-chevron { width: 11px; height: 11px; flex: 0 0 auto; color: rgba(255,255,255,0.35); transition: transform .15s ease; }
.sidebar-group[open] > .sidebar-group-toggle .sidebar-chevron { transform: rotate(90deg); }
.sidebar-group-toggle:hover { background: rgba(255,255,255,0.06); }

/* Nested submenu: a hairline guide down the left + a small indent, instead of
   compounding padding, so four levels of nesting stay legible in a narrow rail. */
.sidebar-group-menu {
  display: flex; flex-direction: column; gap: 1px;
  margin: 1px 0 3px 12px; padding-left: 8px;
  border-left: 1px solid rgba(255,255,255,0.08);
}

/* Depth de-emphasis: a folder nested under another (Reports > Sales 3 months)
   reads lighter + smaller than a top-level folder, its leaves smaller still. */
.sidebar-group-nested > .sidebar-group-toggle { font-size: 12.5px; font-weight: 500; color: var(--ig-off-white); }
.sidebar-group-nested .sidebar-group-icon { width: 13px; height: 13px; color: rgba(255,255,255,0.5); }
.sidebar-group-nested .sidebar-link { font-size: 12px; }
/* External app links (CIP / EMS / Qiniseka QA) sit among the app *group* headers
   under CX > Apps, so give them the same bold, full-opacity white look instead of
   the muted leaf-link treatment (overrides .sidebar-link's opacity 0.66 + wt 400). */
.sidebar-link.sidebar-link-app { opacity: 1; color: var(--ig-white); font-weight: 600; font-size: 12.5px; }

.app-footer {
  padding: 8px 24px; background: var(--ig-navy-darker);
  color: var(--ig-off-white); font-size: 12px;
  border-top: 1px solid rgba(255,255,255,0.05);
}
.app-footer code { color: var(--ig-teal); }

/* ---- Buttons ---- */
.btn-export, .btn-download {
  font: 500 13px/1 inherit; padding: 8px 18px;
  background: var(--ig-teal); color: var(--ig-navy);
  border: 1px solid transparent; border-radius: 4px; cursor: pointer; white-space: nowrap;
}
.btn-export:hover, .btn-download:hover { background: #00b797; }
.btn-export:active, .btn-download:active { transform: translateY(1px); }

.btn-reload {
  font: 500 13px/1 inherit; padding: 7px 16px;
  background: var(--ig-white); color: var(--ig-navy);
  border: 1px solid var(--ig-teal); border-radius: 4px; cursor: pointer;
}
.btn-reload:hover { background: rgba(0,210,169,0.10); }

.btn-link {
  font-size: 12px; font-weight: 600; color: var(--ig-teal); text-decoration: none;
  padding: 4px 10px; border: 1px solid var(--ig-teal); border-radius: 4px; display: inline-block;
}
.btn-link:hover { background: rgba(0,210,169,0.10); }

.btn-header-reload {
  background: transparent; color: var(--ig-off-white);
  border: 1px solid rgba(255,255,255,0.35); border-radius: 4px;
  font: 500 12px/1 inherit; padding: 6px 12px; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.btn-header-reload:hover { background: rgba(0,210,169,0.18); border-color: var(--ig-teal); color: var(--ig-teal); }

/* ---- Cards & KPI tiles ---- */
.card {
  background: var(--ig-white); border: 1px solid var(--ig-border);
  border-radius: 6px; box-shadow: 0 1px 2px rgba(22,42,60,0.04);
}

.tree-table tr[data-depth] td:first-child, .tree-table tr[data-depth] td:nth-child(2) { cursor: default; }
.tree-table tr[data-depth="1"] td:nth-child(2), .tree-table tr[data-depth="2"] td:nth-child(3) { padding-left: 22px; }
.tree-table .tree-toggle { display: inline-block; width: 14px; cursor: pointer; color: var(--ig-slate); font-size: 11px; }
.tree-table tr[data-depth="0"] { cursor: pointer; font-weight: 600; }
.tree-table tr[data-depth="1"] { cursor: pointer; }

.checkbox-dropdown { position: relative; display: inline-block; }
.checkbox-dropdown summary {
  list-style: none; cursor: pointer; user-select: none;
  background: var(--ig-white); color: var(--ig-navy);
  border: 1px solid var(--ig-border); border-radius: 4px;
  padding: 7px 12px; font-size: 13px; min-width: 220px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.checkbox-dropdown summary::-webkit-details-marker { display: none; }
.checkbox-dropdown summary::after { content: "\25BE"; color: var(--ig-slate); }
.checkbox-dropdown[open] summary { border-color: var(--ig-teal); }
.checkbox-dropdown-panel {
  position: absolute; z-index: 20; top: calc(100% + 4px); left: 0;
  background: var(--ig-white); border: 1px solid var(--ig-border); border-radius: 6px;
  box-shadow: 0 4px 14px rgba(22,42,60,0.14); padding: 10px 12px;
  min-width: 260px; max-height: 260px; overflow-y: auto;
}
.checkbox-dropdown-panel label { display: block; padding: 4px 0; font-size: 13px; white-space: nowrap; }

.kpi-grid { display: grid; gap: 8px; grid-template-columns: repeat(6, minmax(0,1fr)); }
.kpi-card { background: var(--ig-white); border: 1px solid var(--ig-border); border-radius: 6px;
            padding: 8px 10px; box-shadow: 0 1px 2px rgba(22,42,60,0.04); }
.kpi-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ig-slate); margin-bottom: 4px; }
.kpi-value { font-size: 18px; font-weight: 700; color: var(--ig-navy); line-height: 1.1; }
.kpi-delta { display: inline-block; margin-top: 5px; font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 10px; }
.kpi-up   { color: #0a7d57; background: rgba(0,210,169,0.14); }
.kpi-down { color: #b42318; background: rgba(211,51,51,0.12); }
.kpi-flat { color: var(--ig-slate); background: rgba(100,116,139,0.12); }
@media (max-width: 1280px) { .kpi-grid { grid-template-columns: repeat(4, minmax(0,1fr)); } }
@media (max-width: 768px)  { .kpi-grid { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 480px)  { .kpi-grid { grid-template-columns: repeat(2, minmax(0,1fr)); } }

.filters { background: var(--ig-white); border: 1px solid var(--ig-teal); border-radius: 6px; padding: 10px 14px;
           display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }
.filters label { display: flex; flex-direction: column; gap: 3px; font-size: 10.5px; text-transform: uppercase;
                  letter-spacing: 0.04em; color: var(--ig-slate); }
.filters input, .filters select { border: 1px solid var(--ig-teal); border-radius: 4px; padding: 5px 9px; font: inherit; }
.filters input:focus, .filters select:focus { outline: none; box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }

/* Vertical filter rail (Attendance Register pages) */
.filters-layout { display: flex; gap: 14px; align-items: flex-start; }
.filters-vertical { background: var(--ig-white); border: 1px solid var(--ig-teal); border-radius: 6px;
                     padding: 10px; display: flex; flex-direction: column; gap: 10px;
                     flex: 0 0 200px; width: 200px; }
.filters-vertical label { display: flex; flex-direction: column; gap: 3px; font-size: 10.5px; text-transform: uppercase;
                           letter-spacing: 0.04em; color: var(--ig-slate); }
.filters-vertical input, .filters-vertical select { border: 1px solid var(--ig-teal); border-radius: 4px;
                                                      padding: 5px 7px; font: inherit; width: 100%; }
.filters-vertical input:focus, .filters-vertical select:focus { outline: none; box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }
.filters-content { flex: 1 1 auto; min-width: 0; }
/* .filters-vertical (a flex item) has no top margin of its own, but
   .section-title's 12px top margin pushes .filters-content's rendered text
   down that much, visually misaligning the two flex siblings' contents even
   though their boxes start at the same flex-start line. Zero it on the
   first title so the rail and the content visibly start flush together. */
.filters-content > .section-title:first-child { margin-top: 0; }
.filter-toggle-group { display: flex; border: 1px solid var(--ig-teal); border-radius: 4px; overflow: hidden; }
.filter-toggle-group button { flex: 1; padding: 6px 4px; border: none; background: var(--ig-white); color: var(--ig-slate);
                               font-size: 12px; cursor: pointer; }
.filter-toggle-group button.active { background: var(--ig-teal); color: var(--ig-navy); font-weight: 600; }

.kpi-card-mini { background: var(--ig-white); border: 1px solid var(--ig-border); border-radius: 6px;
                 padding: 10px 12px; }
.kpi-card-mini .kpi-mini-title { background: var(--ig-navy); color: #fff; font-size: 11px; font-weight: 700;
                                  text-transform: uppercase; letter-spacing: 0.03em; padding: 6px 8px;
                                  margin: -10px -12px 8px; border-radius: 6px 6px 0 0; }
.kpi-card-mini .kpi-mini-value { font-size: 22px; font-weight: 700; color: var(--ig-navy); text-align: center; }
.kpi-card-mini .kpi-mini-yesterday { font-size: 11px; color: var(--ig-slate); text-align: center; margin-top: 4px; }

.attreg-toggle { cursor: pointer; }
.attreg-toggle:hover td { background: rgba(0,210,169,0.08); }
.attreg-toggle.attreg-expanded > td strong { color: var(--ig-teal); }

.kpi-card-big { background: var(--ig-white); border: 1px solid var(--ig-navy); border-radius: 6px;
                padding: 24px 16px; text-align: center; }
.kpi-card-big .kpi-big-value { font-size: 40px; font-weight: 700; color: #1f2933; line-height: 1; }
.kpi-card-big .kpi-big-label { font-size: 13px; color: var(--ig-slate); margin-top: 8px; }

.banner-dummy {
  background: #fff7d6; border: 1px solid #e8c547; border-radius: 6px;
  padding: 8px 14px; font-size: 12.5px; color: #6c5500;
}

.banner-success {
  background: rgba(46,204,113,0.14); border: 1px solid rgba(46,204,113,0.5); border-radius: 6px;
  padding: 8px 14px; font-size: 12.5px; color: #1a6b3a;
  margin-bottom: 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  transition: opacity 0.4s ease;
}
.banner-success .btn-link { color: #1a6b3a; }

/* ---- Data tables ---- */
.table-scroll { overflow: auto; border: 1px solid var(--ig-border); border-radius: 6px; background: var(--ig-white);
                flex: 1 1 auto; min-height: 0; }

.data-table { border-collapse: separate; border-spacing: 0; width: 100%;
  font-family: 'Poppins', system-ui, sans-serif; font-size: 12.5px;
  font-feature-settings: 'tnum' 1, 'lnum' 1; color: #1f2933; }

.data-table thead th {
  background: var(--ig-teal); color: var(--ig-navy);
  position: sticky; top: 0; z-index: 2;
  padding: 5px 8px; text-align: left; font-weight: 600; white-space: nowrap;
  box-shadow: inset 0 -1px 0 rgba(22,42,60,0.20);
  border-right: 1px solid rgba(22,42,60,0.40);
}
.data-table td {
  padding: 3px 8px; white-space: nowrap;
  border-top: 1px solid rgba(0,210,169,0.35);
  border-right: 1px solid var(--ig-border);
}
.data-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table td.neg { color: var(--ig-red); }

.data-table tbody td                    { background: var(--ig-white); }
.data-table tbody tr:nth-child(even) td { background: #f7f8fa; }
.data-table tbody tr:hover td           { background: #eef5fb; }

.data-table thead th.col-total { background: var(--ig-navy); color: var(--ig-teal); font-weight: 600; }
.data-table tbody td.col-total { background: #fff7d6; font-weight: 600; border-left: 2px solid var(--ig-teal); }

/* Click-to-sort column headers (initSortableTables() in app.js) — applied
   generically to every .data-table with a single, colspan-free header row. */
.data-table thead th.sortable-col { cursor: pointer; user-select: none; }
.data-table thead th.sortable-col:hover { background: #00bda0; }
.data-table thead th.sortable-col .sort-indicator { font-size: 10px; margin-left: 2px; }

/* Frozen first column — the row label stays put while the rest of a wide
   table scrolls horizontally underneath it. Doesn't set its own background;
   the existing thead/tbody rules above (teal header, striped/hover rows)
   already cover it since they match the same cells. */
.data-table th.sticky-col, .data-table td.sticky-col {
  position: sticky; left: 0; z-index: 1;
  box-shadow: 1px 0 0 var(--ig-border);
}
.data-table thead th.sticky-col { z-index: 3; }

/* Second frozen column (Lucelle's Date + Metric pair) — offset by the first
   column's own min-width so it sits immediately to the right of it. */
.data-table th.sticky-col-2, .data-table td.sticky-col-2 {
  position: sticky; left: 100px; z-index: 1;
  box-shadow: 1px 0 0 var(--ig-border);
}
.data-table thead th.sticky-col-2 { z-index: 3; }

/* Third frozen column (e.g. Campaign Manager / Team Leader / Agent tree
   columns) — offset by the first two columns' combined min-width. Callers
   with non-default column widths should override `left` inline (see
   kerus/hourly_cm_tl_agent.html) rather than relying on this 200px default. */
.data-table th.sticky-col-3, .data-table td.sticky-col-3 {
  position: sticky; left: 200px; z-index: 1;
  box-shadow: 1px 0 0 var(--ig-border);
}
.data-table thead th.sticky-col-3 { z-index: 3; }

/* Fourth frozen column (e.g. CCM Manager / CM Manager / Team Leader / Agent
   4-level tree columns) — offset by the first three columns' combined
   min-width. Callers with non-default column widths should override `left`
   inline, same convention as sticky-col-3. */
.data-table th.sticky-col-4, .data-table td.sticky-col-4 {
  position: sticky; left: 300px; z-index: 1;
  box-shadow: 1px 0 0 var(--ig-border);
}
.data-table thead th.sticky-col-4 { z-index: 3; }

.data-table tfoot td { position: sticky; bottom: 0; background: #f5f7fa; font-weight: 600;
  color: var(--ig-navy); border-top: 2px solid var(--ig-teal); padding: 5px 8px; }

.table-search {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px;
  padding: 6px 2px; font-size: 12px; color: var(--ig-slate);
}
.table-search input {
  border: 1px solid var(--ig-teal); border-radius: 4px; padding: 5px 9px;
  font: inherit; font-size: 12px; width: 220px; max-width: 100%;
}
.table-search input:focus { outline: none; box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }

/* Power-BI-style "Focus mode" — every chart canvas and .table-scroll gets a
   small expand button (see initVisualFocus() in app.js) that pops it into a
   full-viewport modal. */
.visual-focus-wrap { position: relative; }
.visual-focus-btn {
  position: absolute; top: 4px; right: 4px; z-index: 5;
  background: rgba(255,255,255,0.92); border: 1px solid var(--ig-border);
  border-radius: 4px; width: 26px; height: 26px; padding: 0;
  font-size: 14px; line-height: 1; cursor: pointer; color: var(--ig-slate);
}
.visual-focus-btn:hover { background: #fff; color: var(--ig-navy); border-color: var(--ig-teal); }
.visual-focus-overlay {
  position: fixed; inset: 0; background: rgba(11,26,38,0.65); z-index: 1000;
  display: flex; align-items: center; justify-content: center; padding: 28px;
}
.visual-focus-modal {
  background: #fff; border-radius: 8px; padding: 20px 20px 16px;
  width: 92vw; height: 88vh; max-width: 1500px;
  display: flex; flex-direction: column; position: relative;
  box-shadow: 0 12px 40px rgba(0,0,0,0.35);
}
.visual-focus-modal .visual-focus-close {
  position: absolute; top: 8px; right: 12px; font-size: 20px; line-height: 1;
  background: none; border: none; color: var(--ig-slate); cursor: pointer; padding: 4px 8px;
}
.visual-focus-modal .visual-focus-close:hover { color: var(--ig-navy); }
.visual-focus-modal .visual-focus-body { flex: 1; overflow: auto; margin-top: 6px; min-height: 0; }
.visual-focus-modal .visual-focus-body canvas { max-width: 100%; }

.edc-group-label {
  background: var(--ig-navy); color: var(--ig-off-white);
  font-weight: 600; padding: 6px 10px; font-size: 12.5px;
  letter-spacing: 0.02em;
}

/* ---- Conditional formatting ---- */
.fmt-pct-1 { background: #f8696b; }
.fmt-pct-2 { background: #fcaa7b; }
.fmt-pct-3 { background: #ffe684; }
.fmt-pct-4 { background: #b8d97a; }
.fmt-pct-5 { background: #63be7b; }

/* ---- Loader ---- */
#page-loader {
  position: fixed; top: 0; left: 0; height: 3px; width: 0%;
  background: var(--ig-teal); z-index: 100; transition: width .2s ease;
  opacity: 0;
}
#page-loader.visible { opacity: 1; width: 70%; }
.busy-text { font-style: italic; color: var(--ig-slate); font-size: 12px; }

/* ---- Login page ---- */
.login-wrap {
  display: flex; align-items: center; justify-content: center; height: 100vh;
  background: var(--ig-off-white);
}
.login-card {
  background: var(--ig-white); border: 2px solid var(--ig-teal); border-radius: 10px;
  box-shadow: 0 4px 24px rgba(22,42,60,0.10); max-width: 380px; overflow: hidden; text-align: center;
}
.login-header {
  background: var(--ig-navy); padding: 26px 24px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
}
.login-hex { width: 32px; height: 32px; color: var(--ig-white); flex: 0 0 auto; }
.login-wordmark { color: var(--ig-white); font-weight: 700; font-size: 19px; letter-spacing: 0.02em; }
.login-wordmark-cx { color: var(--ig-teal); }
.login-body { padding: 30px 36px 8px; }
.login-title { font-size: 19px; font-weight: 700; color: var(--ig-navy); margin: 0 0 8px; }
.login-sub { font-size: 13px; color: var(--ig-slate); margin: 0 0 22px; }
.login-btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  background: var(--ig-navy); color: var(--ig-white);
  font-weight: 600; font-size: 14px; padding: 11px 20px; border-radius: 6px; text-decoration: none;
}
.login-btn:hover { background: var(--ig-navy-deep); }
.login-ms-logo { width: 18px; height: 18px; flex: 0 0 auto; }
.login-denied { color: var(--ig-red); font-size: 13px; margin: 14px 0 0; }
.login-footer-hint {
  background: var(--ig-off-white); color: var(--ig-slate); font-size: 11.5px; line-height: 1.5;
  padding: 16px 30px 24px; border-top: 1px solid var(--ig-border); margin-top: 22px;
}

/* ---- Generic per-control busy spinner — every button/dropdown app-wide,
   wired centrally in app.js (htmx:beforeRequest/afterRequest + submit).
   Positioned via JS (fixed, next to whichever control triggered the
   request), not inserted into document flow. ---- */
.inline-spinner {
  position: fixed; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid rgba(22,42,60,0.15); border-top-color: var(--ig-teal);
  animation: ask-spin 0.7s linear infinite; z-index: 300; pointer-events: none;
}

/* ---- Ask page: full-page-post spinner (Cortex round trips can take ~1min) ---- */
.ask-spinner-overlay {
  display: none; position: fixed; inset: 0; z-index: 200;
  background: rgba(22,42,60,0.55); backdrop-filter: blur(1px);
  flex-direction: column; align-items: center; justify-content: center; gap: 16px;
  color: var(--ig-white); text-align: center; padding: 20px;
}
.ask-spinner-overlay.visible { display: flex; }
.ask-spinner {
  width: 42px; height: 42px; border-radius: 50%;
  border: 4px solid rgba(255,255,255,0.25); border-top-color: var(--ig-teal);
  animation: ask-spin 0.8s linear infinite;
}
@keyframes ask-spin { to { transform: rotate(360deg); } }

/* ============================================================
   Uconnect Payment Report additions (merged in from its own
   standalone app). Everything above is Collections' own CSS.
   ============================================================ */

/* Destructive action — used by /uconnect/reconciliation's Ignore button.
   Collections had no destructive-action button before this merge. */
.btn-reject {
  font: 500 13px/1 inherit; padding: 7px 16px;
  background: var(--ig-white); color: var(--ig-red);
  border: 1px solid var(--ig-red); border-radius: 4px; cursor: pointer;
}
.btn-reject:hover { background: rgba(192, 57, 43, 0.10); }
.btn-reject:active { transform: translateY(1px); }

/* Workflow-state pills — used by /uconnect/security's Active/Inactive column. */
.status-pill { display: inline-block; padding: 3px 10px; font-size: 12px; font-weight: 600;
  border-radius: 99px; letter-spacing: 0.02em; white-space: nowrap; }
.status-approved { background: rgba(46,204,113,0.18); color: #1a6b3a; }
.status-rejected { background: rgba(231,76,60,0.18);  color: #922b1f; }

/* Busy indicator inside a split-table card while its HTMX partial loads. */
.split-loading {
  font-style: italic;
  color: var(--ig-slate);
  font-size: 12px;
  padding: 12px;
}

/* ============================================================
   Policy Search / Create Users additions (merged in from the
   standalone "Data & AI Reporting App").
   ============================================================ */

.summary-header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.summary-header h2 { margin: 0; font-size: 18px; color: var(--ig-navy); }

.table-section { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 8px; }
.table-toolbar { display: flex; align-items: center; justify-content: space-between; }
.table-toolbar h3 { margin: 0; font-size: 14px; color: var(--ig-navy); }

/* Power-BI-style single-level drill: one breadcrumb trail + a drill-up
   button, replacing the whole table body with the next/previous level
   rather than expanding a nested row underneath it (see sales3m's
   leader_view.html / provider_mom.html). */
.drill-breadcrumb { font-size: 13px; color: var(--ig-slate); }
.drill-breadcrumb .crumb-link { cursor: pointer; color: var(--ig-teal); }
.drill-breadcrumb .crumb-link:hover { text-decoration: underline; }
.drill-breadcrumb .crumb-link.current { cursor: default; color: var(--ig-navy); font-weight: 600; text-decoration: none; }
.drill-breadcrumb .crumb-sep { margin: 0 4px; color: var(--ig-border); }
tr.drill-row { cursor: pointer; font-weight: 600; }
tr.drill-row:hover { background: rgba(0, 210, 169, 0.06); }

/* Power BI's own visual-header icon buttons: drill up / click-to-drill-down
   toggle / next level. Small square icon buttons, not text links. */
.drill-icons { display: flex; gap: 2px; }
.drill-icon-btn { width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--ig-border); background: var(--ig-white); border-radius: 4px; cursor: pointer;
  font-size: 14px; line-height: 1; color: var(--ig-slate); padding: 0; }
.drill-icon-btn:hover:not(:disabled) { border-color: var(--ig-teal); color: var(--ig-teal); }
.drill-icon-btn:disabled { opacity: 0.35; cursor: default; }
.drill-icon-btn.active { background: var(--ig-teal); border-color: var(--ig-teal); color: var(--ig-white); }

.search-form { display: flex; align-items: end; gap: 12px; }
.search-form label,
.user-form label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ig-slate); display: block; margin-bottom: 6px; }
.user-form { display: flex; flex-direction: column; gap: 12px; }
.form-row { display: flex; align-items: end; gap: 12px; }

.search-busy { font-style: italic; color: var(--ig-slate); font-size: 13px; opacity: 0; transition: opacity .15s ease; }
.search-busy.htmx-request { opacity: 1; }

.pagination { display: flex; align-items: center; justify-content: center; gap: 16px; padding: 10px 0; flex: 0 0 auto; }
.pagination-status { font-size: 13px; color: var(--ig-slate); }
.btn-reload.disabled { color: var(--ig-slate); border-color: var(--ig-border); cursor: default; pointer-events: none; }

.data-freshness { color: var(--ig-teal); }
.empty-state { color: var(--ig-slate); font-style: italic; padding: 12px 4px; }

/* ============================================================
   CX / Vetting Allocation additions (merged in from its own
   standalone app, "Silver Surfer Vetting Allocation").
   ============================================================ */

.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.page-title { margin: 0; font-size: 17px; color: var(--ig-navy); }
.section-title { margin: 12px 0 6px; font-size: 13.5px; color: var(--ig-navy); }
.hint { font-size: 11.5px; color: var(--ig-slate); }
.muted { color: var(--ig-slate); }
.warn-tag {
  display: inline-block; margin-left: 6px; padding: 1px 7px; font-size: 10.5px; font-weight: 600;
  border-radius: 99px; background: rgba(211,51,51,0.12); color: #b42318; letter-spacing: 0.02em;
}
.inline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Extra status-pill tones beyond approved/rejected — allocation & vetting workflow states. */
.status-progress { background: rgba(0,210,169,0.16); color: #0a6b57; }
.status-declined  { background: rgba(231,76,60,0.18); color: #922b1f; }
.status-pending   { background: rgba(232,197,71,0.22); color: #6c5500; }
.status-neutral   { background: rgba(100,116,139,0.14); color: var(--ig-slate); }

/* ---------------------------------------------------------------------------
   Report UX (2026-08): freeze the filter bar and let each table scroll INSIDE
   itself with a frozen header, instead of scrolling the whole page. .data-table
   thead is already `position: sticky` (above), so bounding the .table-scroll
   height is what actually freezes the header. Scoped to `scroll-page` report
   dashboards; `fixed-viewport` pages already scroll their table body internally
   and pin their filters via flex, so they're intentionally left untouched.
--------------------------------------------------------------------------- */
body.scroll-page .filters,
body.scroll-page .filters-vertical {
  position: sticky;
  top: 0;
  z-index: 30;
}
body.scroll-page .table-scroll { max-height: 70vh; }

/* ---------------------------------------------------------------------------
   Report header zone (2026-08). The report NAME + filter bar are pinned
   together as one opaque strip flush under the app header, so the name stays
   visible while the body scrolls and NO row can bleed into the gap above the
   filters. The gap was app-main's 18px top padding: on scroll-page we drop it
   so the pinned strip sits flush against the (114px) app header. Templates opt
   in by wrapping their title + filters in <div class="report-head">.
--------------------------------------------------------------------------- */
body.scroll-page .app-main { padding-top: 0; }
body.scroll-page .report-head {
  position: sticky;
  top: 0;
  z-index: 35;                    /* above content (thead z:2), below app header (z:50) */
  background: var(--ig-off-white);
  padding: 10px 0 8px;
  margin-bottom: 2px;
}
/* The strip's last line is usually a muted subtitle <p>, whose UA bottom margin
   (~1em) opens a gap between the pinned title and the filters/body below it. On
   vertical-rail pages that pushed the whole .filters-layout down noticeably.
   Drop it so the filters sit snug under the title on every report page. */
body.scroll-page .report-head > :last-child { margin-bottom: 0; }
/* Inside a pinned report-head the filters ride along with it — don't also make
   them individually sticky (that double-sticks and reintroduces a seam). */
body.scroll-page .report-head .filters,
body.scroll-page .report-head .filters-vertical { position: static; top: auto; }

/* Vertical filter-rail pages (Attendance, Keru): when a .report-head precedes
   the .filters-layout, the sticky left rail must stick BELOW the pinned title,
   not behind it. --report-head-h is set by base.html to the report-head's live
   height, so the rail clears it exactly regardless of the title's height. */
body.scroll-page .report-head ~ .filters-layout .filters-vertical { top: var(--report-head-h, 0px); }

/* ---------------------------------------------------------------------------
   Searchable multi-select dropdown (templates/_multiselect.html + the handlers
   in static/app.js). Ported from the CIP app so both look the same; replaces
   the old native `<select multiple>` + "Ctrl/Cmd-click" filters. Trigger reads
   like a native select; the panel is absolutely positioned and only IT scrolls.
--------------------------------------------------------------------------- */
.ms-field { display: flex; flex-direction: column; gap: 4px; position: relative; }
.filters .ms-field { min-width: 210px; }
.ms-field .filter-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--ig-slate); font-weight: 500; }

.dropdown { position: relative; }
.dropdown-trigger {
  font: inherit; font-size: 13px; width: 100%;
  padding: 6px 10px; border: 1px solid var(--ig-teal); border-radius: 4px;
  background: var(--ig-white); cursor: pointer;
  display: flex; align-items: center; gap: 8px; text-align: left;
}
.dropdown-trigger:hover { border-color: var(--ig-navy); }
.dropdown.is-open .dropdown-trigger { box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }
.dropdown-summary { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--ig-navy); }
.dropdown-caret { color: var(--ig-slate); font-size: 11px; flex-shrink: 0; }

.dropdown-panel {
  display: none; position: absolute; top: calc(100% + 4px); left: 0;
  min-width: 100%; max-width: 420px; width: max-content;
  background: var(--ig-white); border: 1px solid var(--ig-border); border-radius: 4px;
  box-shadow: 0 6px 20px rgba(22,42,60,0.16);
  padding: 8px; z-index: 40;            /* above sticky theads (z:2) + filters (z:30) */
}
.dropdown.is-open .dropdown-panel { display: block; }
.dropdown-panel-header { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
.dropdown-panel-header .filter-actions { align-self: flex-end; }
.filter-search { font: inherit; font-size: 13px; padding: 5px 8px;
  border: 1px solid var(--ig-teal); border-radius: 3px; background: var(--ig-white); width: 100%; }
.filter-search:focus { outline: none; box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }
.filter-actions { display: inline-flex; gap: 4px; }
.filter-actions button { font: inherit; font-size: 11px; padding: 2px 8px;
  border: 1px solid var(--ig-border); background: var(--ig-off-white); color: var(--ig-slate);
  border-radius: 3px; cursor: pointer; }
.filter-actions button:hover { background: var(--ig-white); border-color: var(--ig-teal);
  color: var(--ig-navy); }

.checkbox-list { border: 1px solid var(--ig-border); background: var(--ig-white);
  border-radius: 3px; max-height: 240px; overflow-y: auto; padding: 4px 0; }
/* .filters-vertical label {display:flex;flex-direction:column;text-transform:uppercase}
   has higher specificity than .check-item alone and was overriding it whenever a
   multiselect sits inside a report filter bar, stacking the checkbox above the
   (now uppercase) text instead of beside it — force row layout back explicitly. */
.checkbox-list .check-item,
.check-item { display: flex; flex-direction: row; align-items: center; gap: 8px; padding: 3px 10px;
  font-size: 13px; text-transform: none; cursor: pointer; user-select: none; }
.check-item:hover { background: #eef5fb; }
.check-item input { margin: 0; width: auto; flex: 0 0 auto; cursor: pointer; accent-color: var(--ig-teal); }
.check-item span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.check-item.search-hidden { display: none; }   /* hidden by the search box */

/* ---------------------------------------------------------------------------
   Data-freshness pill (bottom right) — ported from CIP so both apps match.
   Markup in base.html, live "x ago" + stale state in app.js. Reports read
   30-minute Snowflake snapshots, so this tells the reader how old the numbers
   are instead of leaving them to assume "now".
--------------------------------------------------------------------------- */
.data-freshness {
  position: fixed; right: 12px; bottom: 10px; z-index: 60;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; line-height: 1; color: var(--ig-slate, #53606b);
  background: rgba(255, 255, 255, .92);
  border: 1px solid var(--ig-border, #dfe1e5); border-radius: 14px;
  padding: 4px 10px; box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
}
.data-freshness .df-dot {
  width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto;
  background: var(--ig-teal, #00d2a9);
}
.data-freshness.is-stale .df-dot { background: #e0a106; }   /* amber once overdue */
.data-freshness .df-ago { color: var(--ig-slate-2, #7a8390); }
@media (max-width: 480px) {
  .data-freshness { font-size: 10.5px; padding: 3px 8px; right: 8px; bottom: 8px; }
}

/* ---- Configuration page ----
   Report-access management. Reuses .card / .data-table / .warn-tag / the btn-*
   family; only the page's own layout lives here. */
/* No max-width: capping this at 900px left the table squashed into a column on
   a wide screen with the rest of the page empty. */
.config-page { display: flex; flex-direction: column; gap: 14px; min-height: 0; }

/* Configuration runs in fixed-viewport (see base.html), so the card holding the
   table takes the leftover height and .table-scroll does the scrolling inside
   it — one scroll region on the page, under a sticky header, instead of the
   table's scrollbar and the window's fighting each other. min-height:0 is what
   actually lets a flex item shrink below its content. */
body.fixed-viewport .config-page  { flex: 1 1 auto; }
body.fixed-viewport .config-card  { flex: 1 1 auto; min-height: 0; }
body.fixed-viewport .config-card:not(:last-child) { flex: 0 0 auto; }

/* Belt and braces: /config opts its table out of the focus wrapper, but if that
   marker is ever dropped, app.js inserts .visual-focus-wrap between the card and
   .table-scroll. Unstyled it sizes to content, the table overflows, and
   .app-main's overflow:hidden clips it with NO scrollbar. Keep it in the chain. */
/* Hard cap on the table's height, so the page fits the viewport whatever else
   happens. The fixed-viewport flex chain above already sizes .table-scroll to
   the leftover space — but that depends on every link in the chain behaving,
   and when one didn't the symptom was a second scrollbar on the window. This
   is a floor under that: an explicit ceiling that does not depend on the chain
   at all, and that applies in scroll-page too.

   420px is the page's own chrome (app header, report head, grant form, search
   bar, card padding, footer) measured at 1280x720 and 1920x1080, rounded up so
   the cap binds slightly BEFORE the flex calculation would - which is what
   keeps the window scrollbar away. min-height stops it collapsing to nothing
   on a short window; below roughly 580px tall the page will scroll, which
   beats a table too short to read. */
.config-card .table-scroll {
  max-height: calc(100vh - 420px);
  min-height: 160px;
}

body.fixed-viewport .config-card .visual-focus-wrap {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
}

.config-banner { padding: 9px 12px; border-radius: 6px; font-size: 13px; }
.config-banner-ok  { background: rgba(0,168,120,0.10); border: 1px solid rgba(0,168,120,0.35); color: #0a6b4d; }
.config-banner-err { background: rgba(200,40,40,0.08);  border: 1px solid rgba(200,40,40,0.30); color: #9b1c1c; }


.config-card { display: flex; flex-direction: column; gap: 12px; padding: 12px; min-height: 0; }
.config-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.config-card-head h2 { margin: 0; font-size: 15px; font-weight: 600; }

.config-grant-form { display: flex; flex-direction: column; gap: 5px; }
.config-label { font-size: 12px; font-weight: 500; color: #3a4750; }
.config-grant-row { display: flex; gap: 8px; align-items: center; }
.config-input {
  flex: 1 1 auto; min-width: 0; padding: 7px 10px; font: inherit; font-size: 13px;
  border: 1px solid #cbd2d9; border-radius: 6px; background: #fff; color: #1f2933;
}
.config-input:focus { outline: none; border-color: var(--ig-teal); box-shadow: 0 0 0 2px rgba(0,210,169,0.18); }
.config-hint { margin: 0; font-size: 11.5px; }

.config-col-action { width: 1%; white-space: nowrap; text-align: right; }
.config-col-action form { margin: 0; }
