/* Rad Consultants — Canonical Table Styles
   Canonical source: /var/www/_shared/components/tables.css
   Usage in any project:
     1. ln -s /var/www/_shared /path/to/project/_shared
     2. <link rel="stylesheet" href="_shared/components/tables.css">
     3. <script src="_shared/components/resizable-table.js"></script>
     4. Add class="data-table" to <table>, class="data-table-wrap" to its container.

   Override colors per project with CSS variables (before or after this file):
     --table-header-bg      default: RAD navy #1E3A8A
     --table-header-text    default: white
     --table-header-hover   default: #2D4EAA
     --table-row-odd        default: white
     --table-row-even       default: light navy tint #EEF2FF
     --table-row-hover      default: #DBEAFE
     --table-cell-text      default: near-black #0F172A
     --table-border         default: rgba(0,0,0,0.07)
*/

:root {
  --table-header-bg:    #1E3A8A;
  --table-header-text:  #FFFFFF;
  --table-header-hover: #2D4EAA;
  --table-row-odd:      #FFFFFF;
  --table-row-even:     #EEF2FF;
  --table-row-hover:    #DBEAFE;
  --table-cell-text:    #0F172A;
  --table-border:       rgba(0, 0, 0, 0.07);
}

/* Container — clips overflow so table scrolls inside, footer stays visible */
.data-table-wrap {
  overflow: hidden;
  border: 1px solid var(--border, rgba(0,0,0,0.1));
  border-radius: 8px;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Inner scroll div inserted by resizable-table.js — thead sticky works here */
.table-body-scroll {
  flex: 1;
  min-height: 0;
  overflow: auto;
}

/* Parent containers that hold .data-table-wrap need flex column so wrap fills height */
[id$="TableWrap"] {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px; /* minimum per global table rules */
  line-height: 1.4;
}

/* Rule 10: dark header bg */
.data-table thead {
  background: var(--table-header-bg);
  position: sticky;
  top: 0;
  z-index: 1;
}

/* Rule 7: headers 1-2px larger; Rule 11: light header text; Rule padding >=12px/16px */
.data-table th {
  position: relative;
  padding: 12px 16px;
  text-align: left;
  font-weight: 700;
  color: var(--table-header-text);
  font-size: 16px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 2px solid rgba(0, 0, 0, 0.15);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 24px; /* Rule 4: columns shrinkable to 24px floor */
}

/* Header labels wrap to two lines and ellipsize anything past that, so dragging a
   column narrow costs the label's tail rather than growing the header row line by
   line. Clamping this inner span (injected by resizable-table.js) rather than the
   th keeps the absolutely-positioned resize handle and sort caret out of the box.
   white-space is reset because the th above sets nowrap for headers that hold
   markup instead of plain text. */
.data-table th .rt-th-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* A table that has to keep its header exactly one row tall says so on the table:
   data-rt-header-lines="1". resizable-table.js reads the same attribute and gives
   each column enough width for its label on one line, so this clamp only catches
   a column the reader has since dragged narrower. */
.data-table[data-rt-header-lines="1"] th .rt-th-text {
  -webkit-line-clamp: 1;
  line-clamp: 1;
  white-space: nowrap;
  overflow-wrap: normal;
}

.data-table th:hover {
  background: var(--table-header-hover);
}

.data-table th .sort-icon {
  display: inline-block;
  margin-left: 4px;
  opacity: 0.6;
  font-size: 11px;
}
.data-table th.sorted .sort-icon { opacity: 1; }

/* Sort affordance for resizable-table.js, which sets aria-sort. A column with no
   indicator reads as unsortable, so every sortable header shows a dim caret at
   rest and a solid one when it is the active sort. Rendered as a pseudo-element
   with its own padding reservation, so turning it on never re-wraps a header. */
.data-table[data-rt-sort] th:not(.col-check):not([data-nosort]) { padding-right: 30px; }
.data-table[data-rt-sort] th:not(.col-check):not([data-nosort])::after {
  content: "\2195";                       /* up-down arrow: sortable, not yet sorted */
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  opacity: 0.45;
  pointer-events: none;
}
/* Same specificity as the resting caret above, or the neutral arrow wins on
   specificity and the active sort direction never shows (2026-08-26). */
.data-table[data-rt-sort] th[aria-sort="ascending"]:not(.col-check):not([data-nosort])::after {
  content: "\2191"; opacity: 1;
}
.data-table[data-rt-sort] th[aria-sort="descending"]:not(.col-check):not([data-nosort])::after {
  content: "\2193"; opacity: 1;
}
.data-table th[data-nosort] { cursor: default; }
.data-table[data-rt-sort] th[data-nosort]:hover { background: var(--table-header-bg); }

/* ---- selection: row checkboxes + the action bar above the table ----
   The bar is ALWAYS in the layout. Toggling it with [hidden] is what makes the
   table jump down the moment the first row is ticked, so the hidden state here
   only turns off paint, never layout. This also retrofits every app that already
   toggles .bulk-bar[hidden]: the specificity beats a bare [hidden] rule, so they
   stop shifting without touching their markup. */
.table-actions,
.bulk-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 40px;
  margin-bottom: 8px;
  flex-shrink: 0;
}
.table-actions[hidden],
.bulk-bar[hidden] {
  display: flex !important;
  visibility: hidden;
}
/* Resting text ("Select rows to act on them"). Swapped for the buttons on
   selection, in the same bar, so nothing moves either way. */
.table-actions .table-actions-idle,
.bulk-bar .table-actions-idle {
  font-size: 14px;
  opacity: 0.7;
}
/* Selected rows live below the banding and hover rules further down this file
   (same specificity, later wins), so the selectors here are qualified enough to
   beat both. Without that, an even-numbered row loses its selected background
   the moment it is banded, and every selected row loses it under the cursor. */
.data-table tbody tr.tr-selected,
.data-table tbody tr.tr-selected:hover,
.data-table tbody tr.tr-selected:nth-child(even),
.data-table tbody tr.selected,
.data-table tbody tr.selected:hover,
.data-table tbody tr.selected:nth-child(even) {
  background: var(--table-row-selected, var(--table-row-hover));
  box-shadow: inset 3px 0 0 var(--table-header-bg);
}

/* Resize handle injected by resizable-table.js (class: rt-handle) or tableResize.js (col-resize-handle) */
.data-table th .rt-handle,
.data-table th .col-resize-handle {
  position: absolute;
  top: 0;
  right: 0;
  width: 8px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  background: transparent;
  z-index: 2;
}
.data-table th .rt-handle:hover,
.data-table th .col-resize-handle:hover,
.data-table.col-resizing th .rt-handle,
.data-table.col-resizing th .col-resize-handle {
  background: rgba(255, 255, 255, 0.25);
}
.data-table.col-resizing,
.data-table.col-resizing * {
  cursor: col-resize !important;
  user-select: none !important;
}

/* Rule 5: truncate with ellipsis; Rule 13: single dark cell text color */
.data-table td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--table-border);
  color: var(--table-cell-text);
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Force dark text in cells; leave badges/pills/status chips with their own color */
.data-table td,
.data-table td a,
.data-table td span:not(.badge):not([class*="badge"]):not([class*="status"]):not([class*="chip"]):not([class*="pill"]) {
  color: var(--table-cell-text) !important;
}

/* Rules 12: banded rows */
.data-table tbody tr {
  transition: background 0.1s;
  background: var(--table-row-odd);
}
.data-table tbody tr:nth-child(even) {
  background: var(--table-row-even);
}
/* Row hover lift */
.data-table tbody tr:hover {
  background: var(--table-row-hover);
}
.data-table tbody tr:last-child td {
  border-bottom: none;
}

/* Table footer / pagination */
.table-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-top: 1px solid var(--table-border);
  font-size: 13px;
  flex-shrink: 0;
}

.pagination {
  display: flex;
  align-items: center;
  gap: 4px;
}

.pagination button {
  border: 1px solid var(--border, rgba(0,0,0,0.15));
  border-radius: 6px;
  padding: 4px 10px;
  font-size: 13px;
  cursor: pointer;
  transition: all 0.15s;
  background: transparent;
}
.pagination button:hover:not(:disabled) { opacity: 0.8; }
.pagination button:disabled { opacity: 0.3; cursor: not-allowed; }

.rows-per-page {
  display: flex;
  align-items: center;
  gap: 6px;
}
.rows-per-page select {
  width: auto;
  padding: 3px 8px;
  font-size: 13px;
  min-width: 60px;
}

/* Numeric columns centre. resizable-table.js detects them and emits a positional
   rule per table (a class on each <td> would not survive the app rebuilding its
   tbody); these classes are the manual equivalent for markup that wants to say so
   outright, and `data-align` on the <th> overrides the detection either way. */
.data-table th.col-num,
.data-table td.col-num,
.data-table th[data-align="center"] { text-align: center; }
.data-table th[data-align="right"]  { text-align: right; }
.data-table th[data-align="left"]   { text-align: left; }

/* Utility truncation helpers */
.data-table .truncate    { max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .truncate-lg { max-width: 320px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Checkbox column — fixed 44px, no resize handle, no sort cursor, no ellipsis */
.data-table th.col-check,
.data-table td.col-check {
  width: 44px;
  min-width: 44px;
  max-width: 44px;
  text-align: center;
  padding-left: 4px;
  padding-right: 4px;
  overflow: visible;
  text-overflow: unset;
}
.data-table th.col-check {
  cursor: default;
}
.data-table td.col-check {
  cursor: default;
}
