body {
  font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* The Add/Edit Transaction popup's Client field has a dark background (set inline per
   field in each protected/*.html) — the browser's default placeholder color is a light
   grey that's illegible against it, and placeholder color can only be set via this
   pseudo-element (not the plain inline `style` on the input itself). */
#qaTxnClient::placeholder {
  color: #fff;
  opacity: 1;
}

.table td {
  vertical-align: middle;
}

/* Swapping a button's innerHTML for a spinner (Save Changes, Delete, etc.) can otherwise
   leave the old label text selected/highlighted for an instant if the click landed as part
   of a drag or rapid double-click — buttons should never show a text-selection highlight. */
.btn {
  user-select: none;
}

.form-control-sm, .form-select-sm {
  font-size: .875rem;
}

.table input.is-invalid, .table select.is-invalid {
  border-color: #dc3545;
}

.chart-toggle-btn {
  font-weight: 600;
}

.chart-toggle-icon {
  display: inline-block;
  transition: transform .2s ease;
}

.chart-toggle-btn[aria-expanded="true"] .chart-toggle-icon {
  transform: rotate(90deg);
}

.sortable-th {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.filter-toggle-btn {
  border: none;
  background: none;
  color: #fff;
  line-height: 1;
  font-size: .7rem;
}

.filter-toggle-icon {
  display: inline-block;
  transition: transform .2s ease;
}

.filter-toggle-btn[aria-expanded="true"] .filter-toggle-icon {
  transform: rotate(90deg);
}

.filter-row.collapsed {
  display: none;
}

/* A translucent white overlay here would render as a near-white box (nothing opaque
   shows through it), which combined with the header's white text made it invisible on
   hover — use a solid, slightly lighter dark shade instead so white text stays legible. */
.sortable-th:hover {
  background-color: #32383e;
}

.sort-icon {
  font-size: .75em;
  margin-left: 3px;
}

.app-shell {
  display: flex;
  min-height: 100vh;
}

.sidebar {
  width: 220px;
  flex-shrink: 0;
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  overflow-y: hidden;
  background-color: #212529;
  display: flex;
  flex-direction: column;
  padding: 1.25rem 1rem;
  z-index: 20;
}

.sidebar-brand {
  color: #fff;
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 1.5rem;
  padding: 0 .5rem;
}

.sidebar-link {
  display: block;
  color: rgba(255, 255, 255, 0.7);
  text-decoration: none;
  padding: .5rem .75rem;
  border-radius: .375rem;
  margin-bottom: .25rem;
  font-size: .925rem;
}

.sidebar-link:hover {
  color: #fff;
  background-color: rgba(255, 255, 255, 0.08);
}

.sidebar-link.active {
  color: #fff;
  background-color: #0d6efd;
  font-weight: 600;
}

.main-area {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin-left: 220px;
}

.topbar {
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: 10;
}

/* Frozen grid headers: the wrapper becomes its own scroll area so the thead can stick
   to its top while only the tbody rows scroll underneath. */
.table-responsive {
  max-height: 65vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* Fixed layout + auto width: each column's <th style="width:..."> is honored exactly
   (auto layout instead re-measures every row's content and silently overrides it), and
   the table sizes to the sum of those widths rather than stretching/shrinking to the
   wrapper — the wrapper's own overflow-x provides horizontal scroll when it's wider. */
.table-responsive table {
  table-layout: fixed;
  width: auto;
}

/* Grid header colored to match the sidebar, so the table's header reads as part of the
   same chrome as the nav. The filter row underneath it is a much lighter shade — present
   but visually secondary to the title row. */
.table-responsive thead th {
  position: sticky;
  z-index: 2;
  background-color: #212529;
  color: #fff;
  border-color: #32383e;
}

.table-responsive thead tr.filter-row th {
  background-color: #ced4da;
  color: #212529;
  border-color: #adb5bd;
}

.table-responsive thead .text-muted {
  color: rgba(255, 255, 255, 0.55) !important;
}

/* Discreet filter inputs: smaller and lower-contrast than a normal form control, so the
   filter row reads as a secondary, optional affordance rather than demanding attention. */
.filter-row .filter-input,
.filter-row .date-filter-input,
.filter-row .number-filter-input {
  font-size: .75rem;
  padding: .15rem .4rem;
  border-color: rgba(0, 0, 0, 0.15);
  background-color: #fff;
}

.filter-row .filter-input::placeholder,
.filter-row .number-filter-input::placeholder {
  color: rgba(0, 0, 0, 0.35);
}

.chart-card-body {
  height: 300px;
}

.sidebar-toggle-btn {
  display: none;
}

.sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 15;
}

.sidebar-backdrop.show {
  display: block;
}

/* Mobile: the sidebar becomes an off-canvas drawer instead of a permanent 220px column. */
@media (max-width: 767.98px) {
  .sidebar {
    transform: translateX(-100%);
    transition: transform .2s ease-in-out;
  }

  .sidebar.open {
    transform: translateX(0);
  }

  .main-area {
    margin-left: 0;
  }

  .sidebar-toggle-btn {
    display: inline-flex;
  }

  .table-responsive {
    max-height: 50vh;
  }

  .chart-card-body {
    height: 220px;
  }

  /* iOS Safari zooms the whole page in on focus of any input under 16px — the grid and
     forms otherwise use Bootstrap's smaller .form-control-sm sizing throughout, and the
     filter row goes even smaller still (its own selector below wins on specificity). */
  .form-control, .form-select, .form-control-sm, .form-select-sm {
    font-size: 1rem;
  }

  .filter-row .filter-input,
  .filter-row .date-filter-input,
  .filter-row .number-filter-input {
    font-size: 1rem;
  }

  .modal-dialog {
    margin: .5rem;
  }
}
