:root {
  --quids-tech-blue: #0059B1;
  --quids-burnt-rose: #904E55;
  --quids-sand: #BFB48F;
  --quids-parchment: #F2EFE9;
  --quids-ink: #04080F;

  --bs-primary: var(--quids-tech-blue);
  --bs-primary-rgb: 0, 89, 177;
  --bs-link-color: var(--quids-tech-blue);
  --bs-link-color-rgb: 0, 89, 177;
  --bs-link-hover-color: #00417f;
  --bs-danger: var(--quids-burnt-rose);
  --bs-danger-rgb: 144, 78, 85;
  --bs-body-bg: var(--quids-parchment);
  --bs-body-color: var(--quids-ink);

  /* A distinct surface colour for cards/tables/panels so they visually separate from the
     page background instead of blending into it — previously both used --bs-body-bg, so
     there was nothing for the (barely-tinted) Parchment page to contrast against. */
  --quids-surface: #faf8f3;
  --bs-border-color: rgba(4, 8, 15, 0.18);
  --bs-border-color-translucent: rgba(4, 8, 15, 0.18);

  font-size: 14px;
}

[data-bs-theme="dark"] {
  --bs-primary: #4d8fd6;
  --bs-primary-rgb: 77, 143, 214;
  --bs-link-color: #7fb1e6;
  --bs-link-color-rgb: 127, 177, 230;
  --bs-link-hover-color: #a4c8ef;
  --bs-danger: #c47b83;
  --bs-danger-rgb: 196, 123, 131;
  --bs-body-bg: var(--quids-ink);
  --bs-body-color: var(--quids-parchment);

  --quids-surface: #10161f;
  --bs-border-color: rgba(242, 239, 233, 0.18);
  --bs-border-color-translucent: rgba(242, 239, 233, 0.18);
}

@media (min-width: 768px) {
  :root {
    font-size: 16px;
  }
}

body {
  font-family: "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  margin-bottom: 60px;
}

/* Bootstrap's own .container caps at 1320px even on very wide monitors, leaving a lot of unused
   margin either side — the data tables (particularly the Tags column) benefit from the extra
   room, so give it more at the widest breakpoint. Every narrower breakpoint is untouched. */
@media (min-width: 1400px) {
  .container {
    max-width: 1600px;
  }
}

h1, h2, h3, h4, h5, h6, .navbar-brand {
  font-family: "Space Grotesk", "Instrument Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-weight: 600;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--bs-body-bg), 0 0 0 0.25rem var(--quids-tech-blue);
}

html {
  position: relative;
  min-height: 100%;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

/* ---- Bootstrap button-color overrides ----
   Bootstrap's compiled CSS hardcodes each .btn-* variant's colors as its own
   --bs-btn-* custom properties (e.g. .btn-primary sets --bs-btn-bg: #0d6efd
   directly) rather than deriving them from the root --bs-primary/--bs-danger
   variables above, so those root overrides alone never reach real buttons —
   every variant needs its own override here. .btn-success is deliberately left
   at Bootstrap's default green — "paid/confirmed" reads as a status colour
   (green = good, like the win/loss accent this table styling is modelled on),
   not a brand action colour, so it stays outside the 5-colour brand palette. */

.btn-primary {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--quids-tech-blue);
  --bs-btn-border-color: var(--quids-tech-blue);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: #00417f;
  --bs-btn-hover-border-color: #00417f;
  --bs-btn-focus-shadow-rgb: 0, 89, 177;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: #003666;
  --bs-btn-active-border-color: #003666;
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-bg: var(--quids-tech-blue);
  --bs-btn-disabled-border-color: var(--quids-tech-blue);
}

.btn-outline-primary {
  --bs-btn-color: var(--quids-tech-blue);
  --bs-btn-border-color: var(--quids-tech-blue);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--quids-tech-blue);
  --bs-btn-hover-border-color: var(--quids-tech-blue);
  --bs-btn-focus-shadow-rgb: 0, 89, 177;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--quids-tech-blue);
  --bs-btn-active-border-color: var(--quids-tech-blue);
  --bs-btn-disabled-color: var(--quids-tech-blue);
  --bs-btn-disabled-border-color: var(--quids-tech-blue);
}

.btn-danger, .btn-outline-danger {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--quids-burnt-rose);
  --bs-btn-border-color: var(--quids-burnt-rose);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: #7a4248;
  --bs-btn-hover-border-color: #7a4248;
  --bs-btn-focus-shadow-rgb: 144, 78, 85;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: #6c3b40;
  --bs-btn-active-border-color: #6c3b40;
  --bs-btn-disabled-color: #fff;
  --bs-btn-disabled-bg: var(--quids-burnt-rose);
  --bs-btn-disabled-border-color: var(--quids-burnt-rose);
}
.btn-outline-danger {
  --bs-btn-color: var(--quids-burnt-rose);
  --bs-btn-bg: transparent;
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-color: var(--quids-burnt-rose);
}

.form-check-input:checked {
  background-color: var(--quids-tech-blue);
  border-color: var(--quids-tech-blue);
}
.form-check-input:focus {
  border-color: var(--quids-tech-blue);
  box-shadow: 0 0 0 0.25rem rgba(0, 89, 177, 0.25);
}

/* ---- Quids-specific components ---- */

.quids-auth-wrap {
  display: flex;
  justify-content: center;
  padding: 4rem 1rem;
}

.quids-auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 12px;
  padding: 2rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}

/* Same look as .quids-auth-card, minus its max-width: 400px — that's sized for the narrow,
   centered login card it was built for, and was capping how wide each stat card in
   _StatsCards.cshtml could flex-fill, leaving a gap once the table below them got wider.
   Horizontal padding lives here since _StatsCards.cshtml only adds py-3 (vertical) itself. */
.quids-stat-card {
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}

.quids-savings-card {
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.quids-savings-card:hover {
  border-color: var(--bs-primary);
}

/* A closed debt — still viewable (Show closed), just visually out of the way. */
.quids-card-closed {
  opacity: 0.6;
}

.quids-auth-logo {
  display: block;
  margin: 0 auto 1.5rem;
  max-width: 160px;
  height: auto;
}

.quids-form {
  max-width: 480px;
}

.quids-alert-warning {
  background-color: rgba(191, 180, 143, 0.25);
  border: 1px solid var(--quids-sand);
  color: var(--bs-body-color);
}

.quids-badge-alert {
  background-color: var(--quids-burnt-rose);
  color: #fff;
}

.quids-badge-savings {
  background-color: var(--quids-sand);
  color: var(--quids-ink);
}

/* Subtle informational chip — doesn't compete visually with the colour-coded status badges. */
.quids-badge-outline {
  background-color: transparent;
  border: 1px solid var(--bs-border-color);
  color: var(--bs-secondary-color);
}

/* ---- Data tables (Income / Budgeted Outgoings) ---- */

.quids-table-wrap {
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 12px;
  /* overflow-x: auto (not hidden) so narrow/mobile viewports can scroll to the columns that
     don't fit — this element also carries Bootstrap's .table-responsive class, and since both
     rules target the same element, whichever loads last (this stylesheet) used to win outright,
     silently killing the horizontal scroll and cutting off the right side of every table.
     overflow-y stays hidden to keep clipping the table's corners to the wrapper's border-radius. */
  overflow-x: auto;
  overflow-y: hidden;
}

.quids-table-wrap .table {
  margin-bottom: 0;
}

.quids-table-wrap thead th {
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--bs-secondary-color);
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--bs-border-color);
  white-space: nowrap;
}

.quids-table-wrap tbody td {
  padding: 0.75rem 1rem;
  vertical-align: middle;
  border-bottom: 1px solid var(--bs-border-color);
  border-top: none;
}

.quids-table-wrap tbody tr:last-child td {
  border-bottom: none;
}

.quids-table-wrap tbody tr {
  transition: background-color 0.15s ease;
}

.quids-table-wrap tbody tr:hover td {
  background-color: rgba(0, 89, 177, 0.06);
}

/* Signals a row opens something on click (e.g. Budget's "view linked transactions") — the
   whole-row hover highlight above already applies to every row, so this just adds the cursor. */
.quids-clickable-row {
  cursor: pointer;
}

/* Left accent bar per row, colour keyed to the row's status badge.
   "Paid" uses var(--bs-success) (Bootstrap's default green, unoverridden) so it
   always matches whatever .text-bg-success/.btn-success render as. */
.quids-row-accent-paid td:first-child { box-shadow: inset 3px 0 0 var(--bs-success); }
.quids-row-accent-alert td:first-child { box-shadow: inset 3px 0 0 var(--quids-burnt-rose); }
.quids-row-accent-upcoming td:first-child { box-shadow: inset 3px 0 0 var(--quids-sand); }
.quids-row-accent-neutral td:first-child { box-shadow: inset 3px 0 0 var(--bs-border-color); }
.quids-row-accent-primary td:first-child { box-shadow: inset 3px 0 0 var(--quids-tech-blue); }

/* Removes the native up/down spinner buttons some browsers render on <input type="number"> —
   easy to mistake for a mini scrollbar, and this app already blurs the field on wheel-scroll
   instead (see savings.js), so the spinner buttons are redundant as well as confusing. */
.quids-no-spinner::-webkit-outer-spin-button,
.quids-no-spinner::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.quids-no-spinner {
  -moz-appearance: textfield;
}

/* Small inline info-icon tooltip trigger, e.g. next to a checkbox label needing a short explainer. */
.quids-info-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  padding: 0;
  margin-left: 0.35rem;
  color: var(--bs-secondary-color);
  cursor: help;
  vertical-align: middle;
}
.quids-info-icon:hover,
.quids-info-icon:focus {
  color: var(--bs-body-color);
}

/* ---- Mobile list view: stacked cards instead of a horizontally-scrolling table ----
   Below the sm breakpoint, .quids-table-wrap tables drop their header row and each <tr>
   becomes its own card; every <td> renders as a small uppercase label (from its data-label
   attribute, set once per column in the markup) above its content. Replaces the old
   horizontal-scroll fallback, which read as clunky with this many columns on a touch device.
   The row's left accent bar moves from the first <td> (a box-shadow, meaningless once that
   cell is just the top item in a stacked card) to the card itself, as a real border. */
@media (max-width: 575.98px) {
  .quids-table-wrap {
    background: transparent;
    border: none;
    overflow: visible;
  }
  .quids-table-wrap table,
  .quids-table-wrap tbody,
  .quids-table-wrap tr,
  .quids-table-wrap td {
    display: block;
    width: 100%;
  }
  .quids-table-wrap thead {
    display: none;
  }
  .quids-table-wrap tbody tr {
    background: var(--quids-surface);
    border: 1px solid var(--bs-border-color);
    border-left-width: 3px;
    border-radius: 12px;
    padding: 0.25rem 0.9rem;
    margin-bottom: 0.75rem;
  }
  .quids-table-wrap tbody tr:last-child {
    margin-bottom: 0;
  }
  .quids-table-wrap tbody td {
    padding: 0.55rem 0;
  }
  .quids-table-wrap tbody tr td:first-child {
    box-shadow: none; /* superseded by the card's own left border below */
  }
  /* Scoped to match (rather than lose to) .quids-table-wrap tbody tr's own border shorthand
     above — a bare .quids-row-accent-* selector has lower specificity than that rule, so its
     border-left-color was silently never winning and every card rendered with the same
     neutral border regardless of status. */
  .quids-table-wrap tbody tr.quids-row-accent-paid { border-left-color: var(--bs-success); }
  .quids-table-wrap tbody tr.quids-row-accent-alert { border-left-color: var(--quids-burnt-rose); }
  .quids-table-wrap tbody tr.quids-row-accent-upcoming { border-left-color: var(--quids-sand); }
  .quids-table-wrap tbody tr.quids-row-accent-neutral { border-left-color: var(--bs-border-color); }
  .quids-table-wrap tbody tr.quids-row-accent-primary { border-left-color: var(--quids-tech-blue); }
  .quids-table-wrap td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bs-secondary-color);
    margin-bottom: 0.2rem;
  }
  .quids-table-wrap td:empty::before {
    content: none; /* an empty cell (e.g. no Round Up this row) shouldn't show a bare label */
  }
  .quids-row-tag-editor {
    min-width: 0; /* the table-layout floor doesn't apply once the cell is full-width */
  }
}

/* ---- Global search (nav box, site.js) ---- */
.quids-global-search {
  width: 100%;
  max-width: 260px;
}

.quids-search-results {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1050; /* above the navbar's own stacking context */
  margin-top: 0.25rem;
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 12px;
  box-shadow: 0 0.5rem 1rem rgba(4, 8, 15, 0.15);
  max-height: 60vh;
  overflow-y: auto;
}

.quids-search-group-label {
  padding: 0.5rem 0.9rem 0.25rem;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
}

.quids-search-result {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.5rem 0.9rem;
  color: var(--bs-body-color);
  text-decoration: none;
}

.quids-search-result:hover {
  background-color: rgba(0, 89, 177, 0.06);
}

.quids-search-result-detail {
  flex-shrink: 0;
  font-size: 0.8rem;
  color: var(--bs-secondary-color);
}

/* ---- Bootstrap Table (self-hosted, server-paginated lists) ----
   Every list that can hold a meaningful amount of data (Spending, Budget, Income, the Savings
   bucket/budget-item transaction-history modals, Admin Users) is a bootstrap-table instance
   mounted inside the same .table-responsive.quids-table-wrap wrapper the old server-rendered
   tables used, via window.quidsInitTable (quids-table.js). Its toolbar/pagination chrome renders
   as plain Bootstrap form controls (form-control, .pagination, .btn-secondary) — this app never
   themed most of those (only .btn-primary/.btn-outline-primary/.btn-danger), so left alone they
   fall back to flat Bootstrap grey. These rules fill that gap, plus remove the double
   border/radius bootstrap-table's own container adds (the wrapper already provides one) and give
   its card-view output (used below the mobile breakpoint, toggled by quids-table.js) the same
   look the old data-label media query gave a plain server-rendered table. */
.quids-table-wrap .fixed-table-container {
  border: none;
  background: transparent;
}

.quids-table-wrap .fixed-table-toolbar {
  padding: 0.85rem 1rem 0;
}

.quids-table-wrap .fixed-table-toolbar .search input {
  font-size: 0.85rem;
  /* Bootstrap's .form-control defaults its background to --bs-body-bg (the page background),
     not this surface panel's own --quids-surface — one shade darker than the bar it sits in,
     which reads as a stray "hole" cut into it. */
  background-color: var(--quids-surface);
  border-color: var(--bs-border-color);
  color: var(--bs-body-color);
}

.quids-table-wrap .fixed-table-pagination {
  padding: 0.6rem 1rem 0.85rem;
  border-top: 1px solid var(--bs-border-color);
  margin-top: 0.5rem;
  flex-wrap: wrap;
  row-gap: 0.5rem;
}

/* Bootstrap's own un-themed defaults, not bootstrap-table's — this app only ever restyled
   .btn-primary/.btn-danger, so the pagination Prev/Next/page-number buttons (Bootstrap's
   .pagination component) and the "rows per page" dropdown trigger (a plain .btn-secondary)
   both fall back to flat Bootstrap grey, which reads as a much more "boxed" panel than
   anything else in the app. Reskin both the same way .btn-primary/.btn-outline-primary
   already are elsewhere — local --bs-*/--bs-btn-* variable overrides, not fighting specificity
   on the base Bootstrap rules directly. */
.quids-table-wrap {
  --bs-pagination-bg: transparent;
  --bs-pagination-color: var(--bs-body-color);
  --bs-pagination-border-color: var(--bs-border-color);
  --bs-pagination-hover-bg: rgba(0, 89, 177, 0.06);
  --bs-pagination-hover-color: var(--bs-body-color);
  --bs-pagination-hover-border-color: var(--bs-border-color);
  --bs-pagination-active-bg: var(--quids-tech-blue);
  --bs-pagination-active-border-color: var(--quids-tech-blue);
  --bs-pagination-disabled-bg: transparent;
  --bs-pagination-disabled-color: var(--bs-secondary-color);
  --bs-pagination-disabled-border-color: var(--bs-border-color);
}

.quids-table-wrap .fixed-table-pagination .btn-secondary {
  --bs-btn-bg: transparent;
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-hover-bg: rgba(0, 89, 177, 0.06);
  --bs-btn-hover-color: var(--bs-body-color);
  --bs-btn-hover-border-color: var(--bs-border-color);
  --bs-btn-active-bg: rgba(0, 89, 177, 0.1);
  --bs-btn-active-color: var(--bs-body-color);
}

/* Card view (mobile) — mirrors the retired .quids-table-wrap media query 1:1, just targeting
   bootstrap-table's own generated card markup (td.card-view/.card-view-title/.card-view-value)
   instead of data-label. The accent-left-border still comes from the same quids-row-accent-*
   class every rowStyle callback sets on <tr> — :has() picks out a card-mode row without
   quids-table.js needing to add a second marker class itself. */
.quids-table-wrap tbody tr:has(td.card-view) {
  display: block;
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-left-width: 3px;
  border-radius: 12px;
  padding: 0.25rem 0.9rem;
  margin-bottom: 0.75rem;
  box-shadow: none;
}
.quids-table-wrap tbody tr:has(td.card-view):last-child {
  margin-bottom: 0;
}
.quids-table-wrap tbody tr:has(td.card-view) td.card-view {
  padding: 0.55rem 0;
  border: none;
}
.quids-table-wrap tbody tr:has(td.card-view).quids-row-accent-paid { border-left-color: var(--bs-success); }
.quids-table-wrap tbody tr:has(td.card-view).quids-row-accent-alert { border-left-color: var(--quids-burnt-rose); }
.quids-table-wrap tbody tr:has(td.card-view).quids-row-accent-upcoming { border-left-color: var(--quids-sand); }
.quids-table-wrap tbody tr:has(td.card-view).quids-row-accent-neutral { border-left-color: var(--bs-border-color); }
.quids-table-wrap tbody tr:has(td.card-view).quids-row-accent-primary { border-left-color: var(--quids-tech-blue); }
.quids-table-wrap .card-view-title {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
  min-width: 40% !important;
}

/* Circular icon-only action buttons (confirm/mark-paid, actions menu trigger). */
.quids-icon-btn {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid var(--bs-border-color);
  background: transparent;
  color: var(--bs-body-color);
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.quids-icon-btn:hover {
  background-color: rgba(127, 127, 127, 0.14);
}
.quids-icon-btn-active {
  background-color: var(--bs-success);
  border-color: var(--bs-success);
  color: #fff;
}
.quids-icon-btn-active:hover {
  filter: brightness(90%);
}

/* ---- Live password requirements checklist ---- */

.quids-password-requirements [data-rule] {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--bs-secondary-color);
  transition: color 0.15s ease;
}

.quids-req-icon {
  display: inline-flex;
  width: 1em;
  justify-content: center;
}
.quids-req-icon::before {
  content: "\2022"; /* bullet — unmet */
}

.quids-password-requirements [data-rule].quids-req-met {
  color: var(--bs-success);
}
.quids-password-requirements [data-rule].quids-req-met .quids-req-icon::before {
  content: "\2713"; /* checkmark — met */
}

/* ---- Payment progress bar (Budget page) — shown for every row, not just partial ones.
   Total sits to the right of the bar; the bar itself is a two-segment label: the filled (paid)
   side and the track (remaining) side each show their own amount, so the whole row reads without
   needing separate "£X of £Y" text underneath. ---- */
.quids-progress-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.quids-progress-total {
  flex: 0 0 auto;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.quids-progress {
  height: 22px;
  width: 100%;
  min-width: 140px;
  background-color: rgba(127, 127, 127, 0.2);
  border-radius: 6px;
}
.quids-progress .progress-bar {
  align-items: center;
  font-size: 0.7rem;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
}
.quids-progress-bar {
  background-color: var(--quids-sand);
}
/* Sand is too light for white text to read well — dark ink instead, no shadow needed on a light
   fill. Scoped as `.quids-progress .quids-progress-bar` (not just `.quids-progress-bar`) to match
   the specificity of `.quids-progress .progress-bar` above, which it needs to win against. */
.quids-progress .quids-progress-bar {
  color: var(--quids-ink);
  text-shadow: none;
}
.quids-progress-bar-paid {
  background-color: var(--bs-success);
}
.quids-progress-remaining {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}

/* ---- Month nav (Budget/Income/Spending/Calendar) — one connected control, not full-width ---- */
.quids-month-nav-group {
  width: auto;
  flex: 0 0 auto;
}
.quids-month-nav-group .form-select {
  flex: 0 0 auto;
  width: auto;
}

/* ---- Calendar view ---- */
.quids-calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
  background: var(--bs-border-color);
  border: 1px solid var(--bs-border-color);
  border-radius: 12px;
  overflow: hidden;
}
.quids-calendar-weekday {
  background: var(--quids-surface);
  text-align: center;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--bs-secondary-color);
  padding: 0.5rem 0;
}
.quids-calendar-cell {
  background: var(--quids-surface);
  min-height: 110px;
  padding: 0.4rem;
}
.quids-calendar-cell-outside { opacity: 0.4; }
.quids-calendar-cell-has-entries { cursor: pointer; }
.quids-calendar-cell-has-entries:hover { background-color: rgba(0, 89, 177, 0.06); }
.quids-calendar-cell-today { box-shadow: inset 0 0 0 2px var(--quids-tech-blue); }
/* --bs-primary rather than a literal white/dark value — it's already theme-aware
   (Tech Blue in light mode, a lighter blue in dark mode) so it has good contrast
   against the surface colour in both themes, unlike a fixed white. */
.quids-calendar-today-number { color: var(--bs-primary); font-weight: 600; }

/* Custom pill style rather than Bootstrap's btn-check + btn-outline-secondary — that
   pairing fills to a solid grey background when checked, which washed out the small
   domain-colour dot inside. Dots stay fully saturated in both states; "off" just dims
   the whole pill via opacity so the colour key stays legible either way. */
.quids-calendar-filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  font-size: 0.85rem;
  background: var(--quids-surface);
  color: var(--bs-body-color);
  cursor: pointer;
  user-select: none;
}
.btn-check:not(:checked) + .quids-calendar-filter-pill { opacity: 0.45; }
.btn-check:focus-visible + .quids-calendar-filter-pill { box-shadow: 0 0 0 0.2rem rgba(0, 89, 177, 0.25); }
.quids-calendar-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 3px;
  flex-shrink: 0;
}
.quids-calendar-entry {
  display: flex;
  align-items: center;
  gap: 2px;
  font-size: 0.7rem;
  line-height: 1.4;
}
.quids-calendar-entry-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.quids-calendar-entry-amount {
  flex-shrink: 0;
  color: var(--bs-secondary-color);
}
.quids-calendar-entry-more {
  font-size: 0.7rem;
  color: var(--bs-secondary-color);
}
.quids-calendar-dot-income { background-color: var(--bs-success); }
.quids-calendar-dot-outgoings { background-color: var(--quids-tech-blue); }
.quids-calendar-dot-actuals { background-color: var(--quids-sand); }

/* ---- Tag chip input (Income/Budget/Spending entry modals, row Tags… quick editor) ----
   Chips and the text input are all direct children of .quids-tag-chip-input and share one
   flex-wrap row (see site.js tagChipsRender), so the input sits to the right of existing
   chips instead of dropping to its own line underneath them — a row only grows taller once
   its chips + input genuinely can't fit on one line, not the moment any chip exists. */
.quids-tag-chip-input {
  position: relative; /* anchors the suggestions overlay below */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  padding: 0.3rem 0.5rem;
  background: var(--bs-body-bg);
}
.quids-tag-chip-input:focus-within {
  border-color: var(--quids-tech-blue);
  box-shadow: 0 0 0 0.15rem rgba(0, 89, 177, 0.2);
}
.quids-tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  background: rgba(0, 89, 177, 0.12);
  color: var(--quids-tech-blue);
  border-radius: 999px;
  padding: 0.15rem 0.5rem 0.15rem 0.6rem;
  font-size: 0.8rem;
  flex-shrink: 0;
}
.quids-tag-chip-remove {
  border: none;
  background: none;
  color: inherit;
  line-height: 1;
  padding: 0;
  font-size: 0.9rem;
  cursor: pointer;
}
.quids-tag-chip-input input {
  flex: 1 1 80px;
  min-width: 80px;
  border: none;
  outline: none;
  padding: 0.15rem 0;
  background: transparent;
  color: var(--bs-body-color);
  font-size: 0.9rem;
}

/* Row tag editors: an auto-layout table sizes each column to whatever its content currently
   needs, so with no explicit width the Tags column itself was growing the moment a chip was
   added — not the box "resizing" so much as the whole column jittering wider. A generous
   min-width (room for at least 3 short tags + the input on one line, on desktop) pins the
   column to a stable floor regardless of tag count; .table-responsive's existing horizontal
   scroll (already how every table in this app handles narrow viewports) takes over below
   that rather than the column shrinking. The input itself stays a modest fixed size rather
   than flex-growing to fill leftover width, so a near-empty box doesn't look oversized;
   flex-wrap on the shared base rule still lets the row grow taller — never wider, never
   scrolling — on the rare row with more tags than even this width can hold on one line. */
/* Was flex-wrap (the shared base rule) letting a many-tag row grow taller than its neighbours —
   fine for a single server-rendered table, but with paginated rows now swapping in and out, a
   row height that depends on which rows happen to land on the current page makes the whole
   table visibly grow/shrink between pages. Pinning to one line + horizontal scroll keeps every
   row (and so the table itself) a constant height regardless of tag count; the always-visible
   input at the end is still reachable by scrolling the chip strip, same as scrolling to it would
   be if it just wrapped below instead. */
.quids-row-tag-editor {
  min-width: 300px;
  flex-wrap: nowrap;
  overflow-x: auto;
  overflow-y: hidden;
}
.quids-row-tag-editor input {
  flex: 0 0 90px;
  width: 90px;
  min-width: 90px;
}

/* Existing tags you haven't picked yet, offered as one-click adds — so re-using a tag never
   requires typing it out again. Re-rendered from the same list backing the <datalist>.
   Hidden until the input is active and shown as a floating overlay rather than inline content,
   so a row with many unused tags doesn't grow taller than a row with none.

   position: fixed (not absolute) — a row's tag box lives inside .quids-table-wrap, which sets
   overflow: hidden to clip the table's rounded corners, and would clip an absolutely-positioned
   overlay the same way. top/left/width are set in JS (site.js positionTagSuggestions) from the
   input's own getBoundingClientRect() on focus, the same "escape a clipping ancestor" fix
   already used for the row action dropdowns via Popper's fixed strategy — just done by hand here
   since this isn't a Bootstrap component. */
.quids-tag-suggestions {
  display: none;
  position: fixed;
  padding: 0.4rem;
  max-height: 160px;
  overflow-y: auto;
  flex-wrap: wrap;
  gap: 0.25rem;
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 1060;
}
.quids-tag-chip-input:focus-within .quids-tag-suggestions {
  display: flex;
}
.quids-tag-suggestion {
  border: 1px dashed var(--bs-border-color);
  background: transparent;
  color: var(--bs-secondary-color);
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  font-size: 0.75rem;
  cursor: pointer;
}
.quids-tag-suggestion:hover {
  border-color: var(--quids-tech-blue);
  color: var(--quids-tech-blue);
}

/* Name recall on the Log Spending modal — previously-used Actual Outgoing names, offered as
   a click-to-fill list styled to match the rest of the app instead of the browser's own
   native autofill/datalist popup. position: absolute is fine here (unlike the row tag
   suggestions) since this only ever appears inside a modal, never a clipped table cell. */
.quids-name-suggestions {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  margin-top: 0.25rem;
  max-height: 180px;
  overflow-y: auto;
  background: var(--quids-surface);
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.quids-name-suggestions.show {
  display: block;
}
.quids-name-suggestion-item {
  padding: 0.4rem 0.6rem;
  cursor: pointer;
  font-size: 0.9rem;
}
.quids-name-suggestion-item:hover {
  background-color: rgba(0, 89, 177, 0.1);
}

/* ---- Floating quick-add button (site-wide) ---- */
.quids-fab-wrap {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem; /* default rest position; nudged up by site.js when the footer scrolls into view */
  z-index: 1040; /* above page content, below modals (1055) and toasts (1080) */
  /* No transition here deliberately — on short pages the footer is already in view on load,
     so an animated slide would visibly jump the button right after every page navigation. */
}
.quids-fab {
  width: 56px;
  height: 56px;
  font-size: 1.75rem;
  line-height: 1;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}

/* Sits above the FAB's resting position (1.5rem + 56px + breathing room) so a toast
   never stacks on top of the quick-add button — both live bottom-right. */
.quids-toast-container {
  bottom: 6.5rem;
}

/* ---- Nav username menu ----
   Given its own background/border/text colour rather than relying on the navbar's own
   light/dark theming, which wasn't tracking the page theme toggle correctly and read as
   low-contrast in dark mode. */
.quids-user-menu-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.75rem;
  border-radius: 999px;
  border: 1px solid var(--bs-border-color);
  background-color: rgba(127, 127, 127, 0.1);
  color: var(--bs-body-color) !important;
}
.quids-user-menu-toggle:hover,
.quids-user-menu-toggle:focus {
  background-color: rgba(127, 127, 127, 0.18);
  color: var(--bs-body-color) !important;
}

/* ---- Hold-to-delete button (site.js initHoldToDelete) ----
   The progress "fill" is a separate absolutely-positioned child (.quids-hold-delete-fill),
   not the button's own background — a previous attempt at this exact pattern had the fill
   done via the button's background-color, and Bootstrap's/this stylesheet's own :hover/:active/
   :focus rules kept repainting that background mid-hold, killing the animation. Locking every
   interactive state's background/border/color to the same fixed values (via !important) means
   nothing but JS explicitly setting the fill element's width can ever change how this looks. */
.quids-hold-delete {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  border: 1px solid var(--quids-burnt-rose);
  background: var(--quids-burnt-rose);
  color: #fff;
  border-radius: 8px;
  padding: 0.5rem 1rem;
  font-weight: 600;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}
.quids-hold-delete,
.quids-hold-delete:hover,
.quids-hold-delete:focus,
.quids-hold-delete:focus-visible,
.quids-hold-delete:active {
  background: var(--quids-burnt-rose) !important;
  border-color: var(--quids-burnt-rose) !important;
  color: #fff !important;
  box-shadow: none !important;
  outline: none !important;
}
.quids-hold-delete-fill {
  position: absolute;
  inset: 0;
  width: 0%;
  background: rgba(255, 255, 255, 0.35);
  z-index: 0;
  pointer-events: none;
}
.quids-hold-delete-label {
  position: relative;
  z-index: 1;
}

/* ---- Settings: sections instead of boxed cards ----
   Plain full-width sections separated by a divider, rather than narrow max-width cards that
   clump to the left of the page on wide viewports. */
.quids-settings-section {
  padding: 1.5rem 0;
}
.quids-settings-section:first-child {
  padding-top: 0;
}
.quids-settings-section + .quids-settings-section {
  border-top: 1px solid var(--bs-border-color);
}
.quids-settings-section .quids-form,
.quids-settings-section form {
  max-width: 480px;
}

/* ---- Global loading overlay (site.js quidsShowLoading/quidsHideLoading) ----
   Above modals (Bootstrap's .modal is 1055) and toasts, so it can block the whole screen even
   mid-modal (e.g. a hold-to-delete button inside one). The three arcs share the exact three
   colours the Q in the navbar logo is built from — the same brand mark, just spun into a
   loading indicator instead of the static wordmark. */
.quids-loading-overlay {
  display: none;
  position: fixed;
  inset: 0;
  align-items: center;
  justify-content: center;
  background: rgba(4, 8, 15, 0.5);
  z-index: 1090;
}
.quids-loading-overlay.quids-loading-visible {
  display: flex;
}
.quids-loading-spinner {
  width: 64px;
  height: 64px;
  animation: quids-spin 1s linear infinite;
}
@keyframes quids-spin {
  to {
    transform: rotate(360deg);
  }
}
