/* =============================================================
   DESIGN TOKENS — Phase 1A (issue #57)
   Translated from the Magic Patterns reference at
   ~/dev/LandlordPro-MagicPatters-Design/. Slate-ink neutrals +
   teal primary + tinted status accents. Used both directly
   (.kpi-card, .badge-tint-*) and via Bootstrap variable
   overrides (--bs-primary, --bs-body-bg, etc.) so existing
   Bootstrap components inherit the palette without HTML edits.
   ============================================================= */
:root {
  /* Slate (neutrals) */
  --color-slate-50:  #f8fafc;
  --color-slate-100: #f1f5f9;
  --color-slate-200: #e2e8f0;
  --color-slate-300: #cbd5e1;
  --color-slate-400: #94a3b8;
  --color-slate-500: #64748b;
  --color-slate-600: #475569;
  --color-slate-700: #334155;
  --color-slate-800: #1e293b;
  --color-slate-900: #0f172a;

  /* Teal (primary) */
  --color-primary-50:  #f0fdfa;
  --color-primary-100: #ccfbf1;
  --color-primary-200: #99f6e4;
  --color-primary-300: #5eead4;
  --color-primary-400: #2dd4bf;
  --color-primary-500: #14b8a6;
  --color-primary-600: #0d9488;
  --color-primary-700: #0f766e;
  --color-primary-800: #115e59;
  --color-primary-900: #134e4a;

  /* Status accents */
  --color-emerald-100: #d1fae5; --color-emerald-200: #a7f3d0;
  --color-emerald-300: #6ee7b7; --color-emerald-500: #10b981;
  --color-emerald-600: #059669; --color-emerald-700: #047857;
  --color-emerald-800: #065f46;
  --color-amber-100:   #fef3c7; --color-amber-200:   #fde68a;
  --color-amber-300:   #fcd34d; --color-amber-500:   #f59e0b;
  --color-amber-600:   #d97706; --color-amber-700:   #b45309;
  --color-amber-800:   #92400e;
  --color-red-100:     #fee2e2; --color-red-200:     #fecaca;
  --color-red-300:     #fca5a5; --color-red-400:     #f87171; --color-red-500:     #ef4444;
  --color-red-600:     #dc2626; --color-red-700:     #b91c1c;
  --color-red-800:     #991b1b;
  --color-sky-100:     #e0f2fe; --color-sky-200:     #bae6fd;
  --color-sky-300:     #7dd3fc; --color-sky-500:     #0ea5e9;
  --color-sky-600:     #0284c7; --color-sky-700:     #0369a1;
  --color-sky-800:     #075985;
  /* Indigo — used by the .badge-tint-info family. Sits clearly
     apart from primary (teal) on the cool-blue spectrum, where
     sky was too close to teal and read as "muted teal" instead
     of a distinct info status. */
  --color-indigo-200:  #c7d2fe; --color-indigo-300:   #a5b4fc;
  --color-indigo-700:  #4338ca; --color-indigo-800:   #3730a3;

  /* Bootstrap overrides — light theme. Card bg stays white;
     body bg goes slate-50 so white cards read as elevated. */
  --bs-primary:         var(--color-primary-600);
  --bs-primary-rgb:     13, 148, 136;
  --bs-body-bg:         var(--color-slate-50);
  --bs-body-color:      var(--color-slate-900);
  --bs-body-color-rgb:  15, 23, 42;
  --bs-secondary-color: var(--color-slate-500);
  --bs-secondary-bg:    var(--color-slate-100);
  --bs-tertiary-bg:     var(--color-slate-50);
  /* Border palette. --bs-border-color = slate-300 reads cleanly on
     form controls (the original slate-200 was too faint and made
     inputs/selects/checkboxes look washed out). The lighter
     slate-200 is exposed as --lp-divider for intra-card dividers
     (section-card header rule, checklist row separators) — those
     should stay subtle. */
  --bs-border-color:    var(--color-slate-300);
  --lp-divider:         var(--color-slate-200);
  --bs-card-bg:         #ffffff;
  --bs-link-color:      var(--color-primary-600);
  --bs-link-color-rgb:  13, 148, 136;
  --bs-link-hover-color: var(--color-primary-700);

  /* Status colour tokens — deepen Bootstrap's bright defaults to
     -700 shades so .text-*, .bg-*, .alert-*-emphasis surfaces match
     the deepened tinted-badge family + the .btn-* per-class
     overrides (btn-warning/danger/success/info all use the same
     -700 shades). Single point of truth — change any of these once
     to repaint the corresponding utility class app-wide. */
  --bs-warning:     var(--color-amber-700);
  --bs-warning-rgb: 180, 83, 9;
  --bs-danger:      var(--color-red-700);
  --bs-danger-rgb:  185, 28, 28;
  --bs-success:     var(--color-emerald-700);
  --bs-success-rgb: 4, 120, 87;
  --bs-info:        var(--color-indigo-700);
  --bs-info-rgb:    67, 56, 202;

  /* Sidebar palette tokens — follow page theme. Light mode = white
     panel on the slate-50 page bg; dark mode = slate-900 panel
     (matches the prior always-dark behaviour). The active-state
     fill (primary-600 + white text) stays the same in both modes,
     so it isn't tokenised here. */
  --sidebar-bg:             #ffffff;
  --sidebar-border:         var(--color-slate-200);
  --sidebar-text:           var(--color-slate-700);
  --sidebar-text-strong:    var(--color-slate-900);
  --sidebar-text-muted:     var(--color-slate-500);
  --sidebar-icon:           var(--color-slate-500);
  --sidebar-hover-bg:       var(--color-slate-100);
  --sidebar-avatar-bg:      var(--color-slate-200);
  --sidebar-avatar-text:    var(--color-slate-700);
  --sidebar-search-bg:      var(--color-slate-50);
  --sidebar-search-focus-bg: #ffffff;
  --sidebar-scrollbar:      var(--color-slate-300);

  /* Tinted status pills. Mid-band saturation: each token sits one
     rung bolder than the original MP-style 100/700/200 (which read
     too pale on dense table surfaces). Edit any --tint-*-{bg,fg,
     border} here to repaint a status across the entire app. */
  --tint-success-bg:     var(--color-emerald-200);
  --tint-success-fg:     var(--color-emerald-800);
  --tint-success-border: var(--color-emerald-300);
  --tint-warning-bg:     var(--color-amber-200);
  --tint-warning-fg:     var(--color-amber-800);
  --tint-warning-border: var(--color-amber-300);
  --tint-danger-bg:      var(--color-red-200);
  --tint-danger-fg:      var(--color-red-800);
  --tint-danger-border:  var(--color-red-300);
  --tint-info-bg:        var(--color-indigo-200);
  --tint-info-fg:        var(--color-indigo-800);
  --tint-info-border:    var(--color-indigo-300);
  --tint-neutral-bg:     var(--color-slate-200);
  --tint-neutral-fg:     var(--color-slate-800);
  --tint-neutral-border: var(--color-slate-300);
  --tint-primary-bg:     var(--color-primary-100);
  --tint-primary-fg:     var(--color-primary-800);
  --tint-primary-border: var(--color-primary-300);
}

/* Identity Login page — tighten the auxiliary-link stack under the
   Log in button. The default scaffold wraps each of "Forgot your
   password? / Register / Resend email confirmation" in a <p> with
   Bootstrap's 1rem margin-bottom, and the button-div has no
   compensating bottom margin — so the gap between the button and
   the first link, and between links, reads as loose. Scoped via
   :has() so only the Login page is affected. */
div:has(> #login-submit) + div {
  margin-top: 0.5rem;
}
div:has(> #login-submit) + div p {
  margin-bottom: 0.375rem;
}
div:has(> #login-submit) + div p:last-child {
  margin-bottom: 0;
}

/* Clean list-table pattern (MP-inspired). Apply to a <table> by
   adding `.lp-table` (instead of Bootstrap's `.table`). Brings:
     • Header band — slate-50/70 bg, uppercase tracking-wider,
       smaller semibold text in secondary colour
     • Row dividers via --lp-divider (subtle), no vertical lines,
       no outer border (matches our current open-list aesthetic)
     • Slight padding bump on cells for breathing room without
       crossing into airy territory
     • Subtle full-row hover with colour transition
   Pair with .table-responsive wrapper if a list overflows at
   narrow widths. Don't combine with .table — they fight on
   padding and borders. */
.lp-table {
  width: 100%;
  margin-bottom: 0;
  color: var(--bs-body-color);
  font-size: 0.875rem;
  border-collapse: collapse;
}
.lp-table thead th {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--bs-secondary-color);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.75rem 1rem;
  background-color: color-mix(in srgb, var(--color-slate-50) 70%, transparent);
  border: 0;
  border-bottom: 1px solid var(--lp-divider);
  text-align: left;
  white-space: nowrap;
}
.lp-table tbody td {
  padding: 0.875rem 1rem;
  vertical-align: middle;
  border: 0;
  border-bottom: 1px solid var(--lp-divider);
}
.lp-table tbody tr:last-child td {
  border-bottom: 0;
}
.lp-table tbody tr {
  transition: background-color 120ms;
}
.lp-table tbody tr:hover {
  background-color: color-mix(in srgb, var(--color-slate-50) 60%, transparent);
}
[data-bs-theme="dark"] .lp-table thead th {
  background-color: color-mix(in srgb, var(--color-slate-800) 50%, transparent);
}
[data-bs-theme="dark"] .lp-table tbody tr:hover {
  background-color: color-mix(in srgb, var(--color-slate-800) 40%, transparent);
}

[data-bs-theme="dark"] {
  /* Bootstrap overrides — dark theme. Slate-900 page on
     slate-800 cards keeps the elevation read. */
  --bs-primary:         var(--color-primary-500);
  --bs-primary-rgb:     20, 184, 166;
  --bs-body-bg:         var(--color-slate-900);
  --bs-body-color:      var(--color-slate-200);
  --bs-body-color-rgb:  226, 232, 240;
  --bs-secondary-color: var(--color-slate-400);
  --bs-secondary-bg:    var(--color-slate-800);
  --bs-tertiary-bg:     var(--color-slate-800);
  --bs-border-color:    var(--color-slate-700);
  --lp-divider:         var(--color-slate-800);
  --bs-card-bg:         var(--color-slate-800);
  --bs-link-color:      var(--color-primary-400);
  --bs-link-color-rgb:  45, 212, 191;
  --bs-link-hover-color: var(--color-primary-300);

  /* Status tints in dark mode use a translucent fill of the
     500-weight + a brighter foreground so they read on a dark
     surface without lighting up the row. */
  --tint-success-bg:     rgba(16, 185, 129, 0.15);
  --tint-success-fg:     var(--color-emerald-300);
  --tint-success-border: rgba(16, 185, 129, 0.3);
  --tint-warning-bg:     rgba(245, 158, 11, 0.15);
  --tint-warning-fg:     var(--color-amber-300);
  --tint-warning-border: rgba(245, 158, 11, 0.3);
  --tint-danger-bg:      rgba(239, 68, 68, 0.15);
  --tint-danger-fg:      #fca5a5;
  --tint-danger-border:  rgba(239, 68, 68, 0.3);
  --tint-info-bg:        rgba(14, 165, 233, 0.15);
  --tint-info-fg:        #7dd3fc;
  --tint-info-border:    rgba(14, 165, 233, 0.3);
  --tint-neutral-bg:     var(--color-slate-700);
  --tint-neutral-fg:     var(--color-slate-200);
  --tint-neutral-border: var(--color-slate-600);
  --tint-primary-bg:     rgba(20, 184, 166, 0.15);
  --tint-primary-fg:     var(--color-primary-300);
  --tint-primary-border: rgba(20, 184, 166, 0.3);

  /* Sidebar dark-mode overrides. Original always-dark palette
     preserved for users who pick dark theme. */
  --sidebar-bg:             var(--color-slate-900);
  --sidebar-border:          var(--color-slate-800);
  --sidebar-text:            var(--color-slate-300);
  --sidebar-text-strong:     #ffffff;
  --sidebar-text-muted:      var(--color-slate-400);
  --sidebar-icon:            var(--color-slate-400);
  --sidebar-hover-bg:        var(--color-slate-800);
  --sidebar-avatar-bg:       var(--color-slate-700);
  --sidebar-avatar-text:     #ffffff;
  --sidebar-search-bg:       var(--color-slate-800);
  --sidebar-search-focus-bg: var(--color-slate-700);
  --sidebar-scrollbar:       var(--color-slate-700);
}

html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

/* Inter as the body font, with system fallbacks. Links to the
   Google Fonts CSS are in _Layout.cshtml so it's preloaded. */
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Focus ring — palette-aligned teal. The doubled box-shadow gives a
   2px halo of the page bg between the element and the ring, so the
   ring reads cleanly against a control sitting against a card or
   page background. .form-select must be listed explicitly — it has
   its OWN .form-select:focus rule in Bootstrap that overrides
   .form-control:focus, so without listing it here it falls back to
   Bootstrap's hardcoded blue. */
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus,
.form-control:focus, .form-select:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--bs-body-bg),
              0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.4);
}

/* Bootstrap-blue mop-up. Bootstrap 5.3 hardcodes #86b7fe (focus
   border) and #0d6efd (checked/active bg) directly in several form
   rules — NOT via the global --bs-primary token — so they survive
   our root-level override. Restate each one. See
   docs/design-tokens.md for the full catalogue. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
  border-color: var(--color-primary-500);
}
.form-check-input:checked,
.form-check-input[type=checkbox]:indeterminate {
  background-color: var(--color-primary-600);
  border-color: var(--color-primary-600);
}
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-select:focus,
[data-bs-theme="dark"] .form-check-input:focus {
  border-color: var(--color-primary-400);
}
[data-bs-theme="dark"] .form-check-input:checked,
[data-bs-theme="dark"] .form-check-input[type=checkbox]:indeterminate {
  background-color: var(--color-primary-500);
  border-color: var(--color-primary-500);
}

/* Same compile-time blue trapped in pagination, progress, list-group,
   dropdown, and the soft "primary" alert variant. Only the classes we
   actually use in the app get overrides; the rest are intentionally
   left alone (when we use them, add an override and document here). */
.pagination {
  --bs-pagination-color:              var(--bs-link-color);
  --bs-pagination-hover-color:        var(--bs-link-hover-color);
  --bs-pagination-focus-color:        var(--bs-link-hover-color);
  --bs-pagination-active-bg:          var(--color-primary-600);
  --bs-pagination-active-border-color: var(--color-primary-600);
}
.progress {
  --bs-progress-bar-bg: var(--color-primary-600);
}
[data-bs-theme="dark"] .progress {
  --bs-progress-bar-bg: var(--color-primary-500);
}
.dropdown-menu {
  --bs-dropdown-link-active-bg:    var(--color-primary-600);
  --bs-dropdown-link-active-color: #fff;
}
.alert-primary {
  --bs-alert-color: var(--color-primary-700);
  --bs-alert-bg:    var(--color-primary-50);
  --bs-alert-border-color: var(--color-primary-200);
  --bs-alert-link-color: var(--color-primary-800);
}
[data-bs-theme="dark"] .alert-primary {
  --bs-alert-color: var(--color-primary-200);
  --bs-alert-bg:    rgba(20, 184, 166, 0.15);
  --bs-alert-border-color: var(--color-primary-700);
}

html {
  position: relative;
  min-height: 100%;
}

body {
  margin-bottom: 60px;
}

/* Lighter placeholder text app-wide. Bootstrap's default leaves
   placeholders dark enough to be mistaken for real input — making
   them more transparent removes the ambiguity. */
.form-control::placeholder,
.form-select::placeholder,
textarea::placeholder {
  opacity: 0.45;
}

.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;
}

/* Fixed-width status/priority/state pills so "Medium" and "High"
   don't visibly differ. Works with both Bootstrap .badge (inline-
   block) and our .badge-tint (inline-flex) — justify-content centers
   for the flex case, text-align centers for the inline-block case.
   Two sizing variants:
     .badge-fixed       6rem  — short labels (Active, Pending, Open, Empty…)
     .badge-fixed-wide 10rem  — longer multi-word labels (Partially Occupied…)
   Use the wide variant whenever the column contains a label that
   wouldn't fit 6rem; mixing variants in one column defeats the
   alignment goal. */
.badge-fixed {
  display: inline-block;
  min-width: 6rem;
  text-align: center;
  justify-content: center;
}
.badge-fixed-wide {
  display: inline-block;
  min-width: 10rem;
  text-align: center;
  justify-content: center;
}

/* Underline navbar links on hover so the right-side toolbar items
   (theme, user, logout) read as clickable, not as labels. Bootstrap's
   nav-link only changes color on hover by default, which is too subtle. */
.navbar .nav-link:hover {
  text-decoration: underline;
  text-underline-offset: 0.25rem;
}

/* Multi-line truncate. Bootstrap ships only single-line .text-truncate;
   for descriptions/notes that legitimately span a few lines we want the
   element to cap at N lines with an ellipsis. Pair with a tooltip
   (data-bs-toggle="tooltip") to keep the full text reachable on hover. */
.text-clamp-1,
.text-clamp-2,
.text-clamp-3,
.text-clamp-4,
.text-clamp-5 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.text-clamp-1 { -webkit-line-clamp: 1; line-clamp: 1; }
.text-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.text-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }
.text-clamp-4 { -webkit-line-clamp: 4; line-clamp: 4; }
.text-clamp-5 { -webkit-line-clamp: 5; line-clamp: 5; }

/* Uniform-width primary action buttons in card headers (Add Unit,
   Add Utility, Upload, Manage Photos, View Occupancy, etc.). Fixed
   width so every button is identical regardless of text length —
   min-width on its own lets longer-text buttons grow past the floor,
   which reads as visually mismatched. */
.btn-card-action {
  width: 11rem;
  white-space: nowrap;
}

/* Paired-button variant for card headers that show two short-label
   actions side by side (Address: Map + Copy). Half the width of
   .btn-card-action so the pair occupies roughly the same footprint
   as a single full-width action button. */
.btn-card-action-pair {
  width: 5.5rem;
  white-space: nowrap;
}

/* "Looks disabled" but stays interactive enough for Bootstrap tooltips
   to fire (the real `disabled` attribute / `.disabled` class sets
   pointer-events: none, which kills hover events). We use this when
   the control should appear inert but we still need to explain *why*
   on hover via a tooltip. The aria-disabled attribute carries the
   semantic state for screen readers. */
.btn-faux-disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
.btn-faux-disabled:hover,
.btn-faux-disabled:focus,
.btn-faux-disabled:active {
  opacity: 0.55;
}

/* Ledger-style accounting rules on the Rent Ledger footer rows. The
   classic conventions: single rule above a subtotal (separates the
   addends), double underline below a final total. Applied per cell on
   the numerical columns (Expected, Paid, Balance), not the row, so
   the leading "Window total" / "Lifetime billed" labels stay clean.
   `currentColor` matches whatever foreground the table is using, so
   the rules invert correctly under data-bs-theme="dark". */
.ledger-subtotal td,
.ledger-subtotal th {
  border-top: 2px solid currentColor;
  font-weight: 600;
}
.ledger-total td,
.ledger-total th {
  border-bottom: 3px double currentColor;
  font-weight: 700;
}
/* Overlap (overpayment) ledger sub-row: a theme-aware amber highlight so a
   likely duplicate stands out. The wash is lighter than .badge-tint-warning so
   the row's "Overlap" badge keeps its contrast; amber text + a left accent bar
   carry the highlight in both light and dark. */
.ledger-payment-subrow.is-overlap td {
  background-color: rgba(245, 158, 11, 0.09);
  color: var(--tint-warning-fg);
}
[data-bs-theme="dark"] .ledger-payment-subrow.is-overlap td {
  background-color: rgba(245, 158, 11, 0.14);
}
.ledger-payment-subrow.is-overlap td:first-child {
  box-shadow: inset 3px 0 0 var(--bs-warning);
}

/* The label cells ("Window total" / "Lifetime billed") don't get the
   accounting rules — only the numerical cells. */
.ledger-subtotal td:first-child,
.ledger-subtotal th:first-child {
  border-top: 0;
}
.ledger-total td:first-child,
.ledger-total th:first-child {
  border-bottom: 0;
}
/* Empty trailing cells (the "Question this" column on tenant view) also
   shouldn't carry the rules. */
.ledger-subtotal td:last-child,
.ledger-subtotal th:last-child {
  border-top: 0;
}
.ledger-total td:last-child,
.ledger-total th:last-child {
  border-bottom: 0;
}

/* Card-header height parity: reserves vertical space for a default-size
   action button on the right so text-only headers don't read as visually
   shorter than headers with buttons. Without this, the Documents card
   header (with an Upload button) sits ~12 px taller than e.g. an
   Occupancy History card header (text-only). Setting flex + center also
   vertically centers the title in either case. Existing card-headers
   that already opt into d-flex are no-ops here. */
.card-header {
  min-height: 3.25rem;
  display: flex;
  align-items: center;
}

/* Bootstrap 5.3's .table-light variant hardcodes cream-on-dark-text
   colors and doesn't re-route via data-bs-theme="dark", so a thead with
   class="table-light" lands as a bright light band in an otherwise dark
   UI (Property Dashboard "Units" table is the canonical surfacing).
   Override under the dark-theme selector by remapping the table-scoped
   --bs-table-* CSS vars onto theme-aware tokens. Light mode is untouched. */
[data-bs-theme="dark"] .table-light {
  --bs-table-color: var(--bs-body-color);
  --bs-table-bg: var(--bs-tertiary-bg);
  --bs-table-border-color: var(--bs-border-color);
  --bs-table-striped-bg: var(--bs-secondary-bg);
  --bs-table-striped-color: var(--bs-body-color);
  --bs-table-active-bg: var(--bs-secondary-bg);
  --bs-table-active-color: var(--bs-body-color);
  --bs-table-hover-bg: var(--bs-secondary-bg);
  --bs-table-hover-color: var(--bs-body-color);
}

/* Disabled buttons are unambiguously gray regardless of source variant.
   Bootstrap's default leaves an outline-primary:disabled looking like a
   washed-out clay/blue at 65% opacity — which reads close enough to the
   active state that a quick scan can't tell what's clickable. The rule
   here forces every disabled .btn to drop its variant color and adopt
   the secondary text + neutral border, then dims with opacity so the
   button is unmistakably inactive. Filled variants keep their fill but
   the fill becomes a neutral surface. */
.btn-outline-primary:disabled,  .btn-outline-primary.disabled,
.btn-outline-success:disabled,  .btn-outline-success.disabled,
.btn-outline-warning:disabled,  .btn-outline-warning.disabled,
.btn-outline-danger:disabled,   .btn-outline-danger.disabled,
.btn-outline-info:disabled,     .btn-outline-info.disabled,
.btn-outline-secondary:disabled, .btn-outline-secondary.disabled {
  color: var(--bs-secondary-color);
  border-color: var(--bs-border-color);
  background-color: transparent;
  opacity: 0.55;
}
.btn-primary:disabled, .btn-primary.disabled,
.btn-success:disabled, .btn-success.disabled,
.btn-warning:disabled, .btn-warning.disabled,
.btn-danger:disabled,  .btn-danger.disabled,
.btn-info:disabled,    .btn-info.disabled {
  color: var(--bs-secondary-color);
  background-color: var(--bs-secondary-bg);
  border-color: var(--bs-border-color);
  opacity: 0.65;
}

/* Photo upload drop zone — dashed border, color-shift on dragover.
   The label wraps a hidden <input type="file">, so clicking the zone
   opens the native picker and dragging files onto it fires the JS
   drop handler. is-dragover class is toggled by JS on dragenter/leave. */
.photo-dropzone {
  border: 2px dashed var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background-color: var(--bs-body-bg);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
  min-height: 8rem;
}
.photo-dropzone:hover {
  border-color: var(--bs-primary);
  background-color: var(--bs-tertiary-bg);
}
.photo-dropzone.is-dragover {
  border-color: var(--bs-primary);
  background-color: var(--bs-primary-bg-subtle);
}
/* Disabled preview state — used on the /MyData Import card to show
   what the upload affordance will look like once #84 (Phase 2 data
   import) lands. Non-interactive: dimmed, no hover/click affordance. */
.photo-dropzone.is-disabled {
  cursor: not-allowed;
  opacity: 0.55;
  pointer-events: none;
}

/* =============================================================
   PHASE 1A UTILITIES — KPI tiles, tint pills, severity badges,
   page-mount fade-in. Additive — existing Bootstrap badges and
   cards keep working unchanged.
   ============================================================= */

/* Tinted status pill family. Used wherever Bootstrap's .badge
   .bg-success / .bg-warning / etc. felt too saturated. The fg/bg
   pair flips automatically for dark mode via the tokens above.
   Corner radius is rounded-md (0.375rem) per the MagicPatterns
   reference — tighter than a full pill, which reads as more
   deliberate against table rows and card surfaces. */
.badge-tint {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.625rem;
  border-radius: 0.375rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.2;
  border: 1px solid transparent;
}
.badge-tint-success { background-color: var(--tint-success-bg); color: var(--tint-success-fg); border-color: var(--tint-success-border); }
.badge-tint-warning { background-color: var(--tint-warning-bg); color: var(--tint-warning-fg); border-color: var(--tint-warning-border); }
.badge-tint-danger  { background-color: var(--tint-danger-bg);  color: var(--tint-danger-fg);  border-color: var(--tint-danger-border); }
.badge-tint-info    { background-color: var(--tint-info-bg);    color: var(--tint-info-fg);    border-color: var(--tint-info-border); }
.badge-tint-neutral { background-color: var(--tint-neutral-bg); color: var(--tint-neutral-fg); border-color: var(--tint-neutral-border); }
.badge-tint-primary { background-color: var(--tint-primary-bg); color: var(--tint-primary-fg); border-color: var(--tint-primary-border); }

/* Icon-only variant — the compact mobile form of .badge-tint. Reuses
   the same tint + border tokens, squared off so a lone status icon
   reads as a status pip where a labelled badge would crowd a narrow
   table row. */
.badge-tint-icon {
  justify-content: center;
  padding: 0.25rem;
  min-width: 1.75rem;
  font-size: 0.875rem;
}

/* Severity badges — solid filled rectangles. Distinct visual
   layer from status pills so a row's severity reads at a glance
   without conflating with status. Matches the reference design. */
.badge-severity {
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.625rem;
  border-radius: 0.25rem;
  font-size: 0.75rem;
  font-weight: 500;
  color: #fff;
}
.badge-severity-low    { background-color: var(--color-emerald-500); }
.badge-severity-medium { background-color: var(--color-sky-500); }
.badge-severity-high   { background-color: var(--color-amber-500); }

/* KPI tile — big number, uppercase label, optional subtitle.
   Used on Portfolio Overview and Property Dashboard headlines. */
.kpi-card {
  background-color: var(--bs-card-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 1rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.kpi-card-value {
  font-size: 1.875rem;
  font-weight: 600;
  line-height: 1.1;
  margin-bottom: 0.25rem;
  color: var(--bs-body-color);
}
.kpi-card-label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--bs-secondary-color);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.kpi-card-subtitle {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
  margin-top: 0.25rem;
}
/* Coloured-border accents for headline KPIs (e.g. red border on
   "Open Issues" so a non-zero value reads as something to act on). */
.kpi-card-accent-primary { border-color: var(--color-primary-200); }
.kpi-card-accent-primary .kpi-card-value { color: var(--color-primary-600); }
.kpi-card-accent-danger  { border-color: var(--color-red-200); }
.kpi-card-accent-danger  .kpi-card-value { color: var(--color-red-600); }
.kpi-card-accent-warning { border-color: var(--color-amber-200); }
.kpi-card-accent-warning .kpi-card-value { color: var(--color-amber-600); }
[data-bs-theme="dark"] .kpi-card-accent-primary { border-color: rgba(20, 184, 166, 0.4); }
[data-bs-theme="dark"] .kpi-card-accent-primary .kpi-card-value { color: var(--color-primary-400); }
[data-bs-theme="dark"] .kpi-card-accent-danger  { border-color: rgba(239, 68, 68, 0.4); }
[data-bs-theme="dark"] .kpi-card-accent-danger  .kpi-card-value { color: #fca5a5; }
[data-bs-theme="dark"] .kpi-card-accent-warning { border-color: rgba(245, 158, 11, 0.4); }
[data-bs-theme="dark"] .kpi-card-accent-warning .kpi-card-value { color: var(--color-amber-300); }

/* Page-mount fade-in. Apply at the wrapping element of a page's
   main content — replaces the framer-motion fade in the reference
   design. Honours prefers-reduced-motion. */
.fade-in-up {
  animation: fade-in-up 0.3s ease-out;
}
@keyframes fade-in-up {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .fade-in-up { animation: none; }
}

/* =============================================================
   PHASE 1B — APP SHELL: SIDEBAR + TOPBAR
   PM / Admin shell (issue #57). Tenant + anonymous users keep the
   existing top-navbar layout, branched in _Layout.cshtml.
   ============================================================= */

/* Body resets when the sidebar shell is in play. The pre-existing
   margin-bottom: 60px footer-clearance rule was for the old layout
   where main > footer stacked; in the shell layout the footer is
   inside .app-main, so we don't need it. */
body.has-app-shell {
  margin-bottom: 0;
  background-color: var(--bs-body-bg);
}

/* Sidebar — fixed-position panel on the left. Palette flips with
   the page theme via the --sidebar-* tokens (light = white panel
   on slate-50 page bg; dark = slate-900 panel). The active-state
   fill stays primary-600 + white in both modes. */
.app-sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 16rem;
  background-color: var(--sidebar-bg);
  color: var(--sidebar-text);
  display: flex;
  flex-direction: column;
  z-index: 1030;
  border-right: 1px solid var(--sidebar-border);
}
.app-sidebar-inner {
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
}
.app-sidebar-brand {
  height: 4rem;
  display: flex;
  align-items: center;
  padding: 0 1.5rem;
  border-bottom: 1px solid var(--sidebar-border);
  flex-shrink: 0;
}
.app-sidebar-brand-link {
  color: var(--sidebar-text-strong);
  font-weight: 700;
  font-size: 1.125rem;
  letter-spacing: -0.025em;
  text-decoration: none;
}
.app-sidebar-brand-link:hover { color: var(--sidebar-text-strong); }

.app-sidebar-nav {
  flex: 1;
  overflow-y: auto;
  padding: 1rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.app-sidebar-section-label {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--sidebar-text-muted);
  padding: 0.75rem 0.75rem 0.375rem;
}
.app-sidebar-section-label:first-child { padding-top: 0; }

.app-sidebar-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  color: var(--sidebar-text);
  text-decoration: none;
  font-size: 0.875rem;
  font-weight: 500;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.app-sidebar-link i.bi {
  width: 1.25rem;
  font-size: 1rem;
  color: var(--sidebar-icon);
  flex-shrink: 0;
}
.app-sidebar-link:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-text-strong);
}
.app-sidebar-link:hover i.bi { color: var(--sidebar-text-strong); }
.app-sidebar-link.active {
  background-color: var(--color-primary-600);
  color: #fff;
}
.app-sidebar-link.active i.bi { color: #fff; }
.app-sidebar-link-label { flex: 1; }
.app-sidebar-count-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.05rem 0.45rem;
  background-color: var(--color-red-500);
  color: #fff;
  border-radius: 9999px;
  font-size: 0.7rem;
  font-weight: 600;
  flex-shrink: 0;
}
.app-sidebar-divider {
  height: 1px;
  background-color: var(--sidebar-border);
  margin: 0.75rem 0.5rem;
}

.app-sidebar-user {
  border-top: 1px solid var(--sidebar-border);
  padding: 0.75rem;
  flex-shrink: 0;
}
.app-sidebar-user-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem;
  border-radius: 0.375rem;
  color: var(--sidebar-text);
  text-decoration: none;
  margin-bottom: 0.25rem;
}
.app-sidebar-user-link:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-text-strong);
}
.app-sidebar-avatar {
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background-color: var(--sidebar-avatar-bg);
  color: var(--sidebar-avatar-text);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.875rem;
  flex-shrink: 0;
}
.app-sidebar-user-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.app-sidebar-user-name {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--sidebar-text-strong);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-sidebar-user-role {
  font-size: 0.75rem;
  color: var(--sidebar-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-sidebar-logout-form { margin: 0; }
.app-sidebar-logout-btn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.5rem;
  background: none;
  border: 0;
  color: var(--sidebar-text-muted);
  font-size: 0.875rem;
  border-radius: 0.375rem;
  text-align: left;
}
.app-sidebar-logout-btn:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-text-strong);
}

/* Sidebar theme row — full-width row with "Theme" label on the
   left and the .theme-switch component on the right. Same visual
   rhythm as user-link / logout-btn so the user block reads as a
   coherent stack. */
.app-sidebar-theme-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem;
  margin-bottom: 0.25rem;
}
.app-sidebar-theme-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--sidebar-text-muted);
}

/* =============================================================
   THEME SWITCH (Phase 1B follow-up — issue #57)
   Two-option pill switch with a sliding thumb. Self-contained;
   the JS in _Layout.cshtml wires every .theme-switch instance
   on the page to the same apply() function so renders in the
   sidebar + offcanvas + tenant navbar stay in lockstep.

   Track is context-aware: dark in sidebar (always), inherits
   --bs-tertiary-bg in normal flow (tenant navbar etc.). The
   thumb is teal regardless.
   ============================================================= */
.theme-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  background-color: var(--bs-tertiary-bg);
  border-radius: 9999px;
  padding: 2px;
  flex-shrink: 0;
}
.app-sidebar .theme-switch,
.app-sidebar-offcanvas .theme-switch {
  background-color: var(--sidebar-hover-bg);
}
.theme-switch-option {
  position: relative;
  z-index: 2;
  width: 1.875rem;
  height: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  border-radius: 9999px;
  color: var(--bs-secondary-color);
  cursor: pointer;
  transition: color 0.2s ease;
  padding: 0;
}
.app-sidebar .theme-switch-option,
.app-sidebar-offcanvas .theme-switch-option {
  color: var(--sidebar-text-muted);
}
.theme-switch-option i.bi {
  font-size: 0.875rem;
  line-height: 1;
}
.theme-switch-option[aria-checked="true"] {
  color: #fff;
}
.theme-switch-option:focus-visible {
  outline: 2px solid var(--color-primary-500);
  outline-offset: 2px;
}
.theme-switch-thumb {
  position: absolute;
  z-index: 1;
  top: 2px;
  bottom: 2px;
  left: 2px;
  width: 1.875rem;
  background-color: var(--color-primary-600);
  border-radius: 9999px;
  transition: transform 0.2s ease;
  pointer-events: none;
}
.theme-switch.is-dark .theme-switch-thumb {
  transform: translateX(1.875rem);
}

/* Mobile offcanvas mirror — same palette as the desktop sidebar
   (flips with the page theme via the --sidebar-* tokens). The
   close button uses btn-close-white only when the sidebar is
   dark; the markup carries that class always, but in light mode
   the dark .btn-close looks better. We invert it via a class
   bound to the theme. */
.app-sidebar-offcanvas {
  background-color: var(--sidebar-bg);
  color: var(--sidebar-text);
  width: 16rem;
}
.app-sidebar-offcanvas .offcanvas-header {
  border-bottom: 1px solid var(--sidebar-border);
}
.app-sidebar-offcanvas .offcanvas-title {
  color: var(--sidebar-text-strong);
  font-weight: 700;
}
/* In light mode strip the .btn-close-white inversion so the X
   uses the default dark glyph that's readable on a white panel. */
[data-bs-theme="light"] .app-sidebar-offcanvas .btn-close-white {
  filter: none;
}

/* Main content column. Margin-left equals the sidebar width on
   desktop (≥992px); on mobile the sidebar is offcanvas-only and
   the main content is full-width. */
.app-main {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
@media (min-width: 992px) {
  .app-main { margin-left: 16rem; }
}

/* Topbar — purely a mobile-only host for the sidebar hamburger.
   Hidden on desktop ≥992px (the sidebar is always visible there
   and the theme toggle lives in the sidebar's user block, so the
   topbar has no remaining purpose on wide screens). Hiding it
   gives the main content area the full page height. */
.app-topbar {
  position: sticky;
  top: 0;
  height: 3.5rem;
  background-color: var(--bs-card-bg);
  border-bottom: 1px solid var(--bs-border-color);
  display: flex;
  align-items: center;
  padding: 0 1rem;
  z-index: 100;
  flex-shrink: 0;
}
@media (min-width: 992px) {
  .app-topbar { display: none; }
}
.app-topbar-toggle {
  font-size: 1.25rem;
  padding: 0.25rem 0.5rem;
  color: var(--bs-body-color);
  text-decoration: none;
}

.app-main-content {
  flex: 1;
  padding: 2rem;
}
@media (max-width: 575.98px) {
  .app-main-content { padding: 1rem; }
}

.app-footer {
  padding: 1.5rem 2rem;
  border-top: 1px solid var(--bs-border-color);
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
}
.app-footer a { color: var(--bs-link-color); text-decoration: none; }
.app-footer a:hover { color: var(--bs-link-hover-color); text-decoration: underline; }

/* =============================================================
   PHASE 1B FOLLOW-UP — COLLAPSIBLE SIDEBAR GROUPS
   Collapsible "Properties" sidebar section with sub-list of
   per-property quick-jumps (#57). Pattern is reusable for any
   future sidebar section that needs sub-items.
   ============================================================= */

.app-sidebar-group {
  position: relative;
}

/* Left-edge accent bar when a sub-item is active. The parent
   row itself does NOT take the full teal fill — that's reserved
   for the leaf-level active item. The accent bar communicates
   "you're inside this section" without competing with the
   actual active leaf. */
.app-sidebar-group.has-active-child::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.25rem;
  bottom: 0.25rem;
  width: 3px;
  background-color: var(--color-primary-500);
  border-radius: 0 2px 2px 0;
}
.app-sidebar-group.has-active-child .app-sidebar-group-toggle { color: var(--sidebar-text-strong); }
.app-sidebar-group.has-active-child .app-sidebar-group-toggle i.bi { color: var(--sidebar-text-strong); }

/* The toggle row itself — styled to match .app-sidebar-link
   exactly so it reads as "the same kind of thing" as a flat
   nav link. The chevron at the right indicates collapsibility. */
.app-sidebar-group-toggle {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  color: var(--sidebar-text);
  font-size: 0.875rem;
  font-weight: 500;
  background: none;
  border: 0;
  width: 100%;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.app-sidebar-group-toggle:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-text-strong);
}
.app-sidebar-group-toggle:hover i.bi { color: var(--sidebar-text-strong); }
.app-sidebar-group-toggle:focus-visible {
  outline: 2px solid var(--color-primary-500);
  outline-offset: -2px;
}
.app-sidebar-group-toggle i.bi {
  width: 1.25rem;
  font-size: 1rem;
  color: var(--sidebar-icon);
  flex-shrink: 0;
}
.app-sidebar-group-toggle .app-sidebar-link-label { flex: 1; }

.app-sidebar-group-chevron {
  width: auto !important;
  font-size: 0.75rem !important;
  transition: transform 0.2s ease;
}
.app-sidebar-group-toggle[aria-expanded="true"] .app-sidebar-group-chevron {
  transform: rotate(90deg);
}

/* Sub-list container. Indentation puts sub-items below the
   parent's icon column so they read as nested. */
.app-sidebar-sublist {
  padding: 0.25rem 0 0.25rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.app-sidebar-sublist-scroll {
  max-height: 14rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  /* Subtle scrollbar — Firefox + WebKit. Colour follows the
     sidebar palette so it's visible in both light + dark. */
  scrollbar-width: thin;
  scrollbar-color: var(--sidebar-scrollbar) transparent;
}
.app-sidebar-sublist-scroll::-webkit-scrollbar {
  width: 6px;
}
.app-sidebar-sublist-scroll::-webkit-scrollbar-thumb {
  background-color: var(--sidebar-scrollbar);
  border-radius: 3px;
}
.app-sidebar-sublist-scroll::-webkit-scrollbar-track {
  background: transparent;
}

/* Sub-link styling — slightly smaller font + dimmer text than
   top-level links, so the visual hierarchy is unmistakable. */
.app-sidebar-sub-link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.625rem;
  border-radius: 0.25rem;
  color: var(--sidebar-text-muted);
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 500;
  transition: background-color 0.15s ease, color 0.15s ease;
}
.app-sidebar-sub-link i.bi {
  width: 1rem;
  font-size: 0.875rem;
  color: var(--sidebar-text-muted);
  flex-shrink: 0;
}
.app-sidebar-sub-link:hover {
  background-color: var(--sidebar-hover-bg);
  color: var(--sidebar-text-strong);
}
.app-sidebar-sub-link:hover i.bi { color: var(--sidebar-text); }
.app-sidebar-sub-link.active {
  background-color: var(--color-primary-600);
  color: #fff;
}
.app-sidebar-sub-link.active i.bi { color: #fff; }
.app-sidebar-sub-link .app-sidebar-link-label {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Filter input that appears when the user has > 5 properties.
   Follows the sidebar palette — light input on light sidebar,
   dark input on dark sidebar. */
.app-sidebar-search-wrap {
  padding: 0.25rem 0;
}
.app-sidebar-search {
  width: 100%;
  padding: 0.25rem 0.5rem;
  background-color: var(--sidebar-search-bg);
  border: 1px solid var(--sidebar-border);
  color: var(--sidebar-text);
  border-radius: 0.25rem;
  font-size: 0.8125rem;
}
.app-sidebar-search:focus {
  outline: none;
  border-color: var(--color-primary-500);
  background-color: var(--sidebar-search-focus-bg);
}
.app-sidebar-search::placeholder {
  color: var(--sidebar-text-muted);
}

/* Empty-state line shown when the user has no properties
   assigned yet. Italic + muted so it doesn't read as a real
   nav item. */
.app-sidebar-empty-state {
  padding: 0.5rem 0.625rem;
  font-size: 0.75rem;
  color: var(--sidebar-text-muted);
  font-style: italic;
}

/* =============================================================
   PHASE 2 — FORM + SECTION PATTERNS (issue #72 design pass 2)
   Codified from the 2026-05-12 MagicPatterns reference. Adds:
     .lp-page-title / .lp-page-subtitle   public-page header treatment
     .lp-section-card                     bordered card with icon+title header
     .lp-segmented[--vertical]            radio group as a button bar
     .lp-form-actions                     submit/cancel strip below sections
     .lp-form-label / .lp-required / .lp-optional
   All tokens resolve through the existing --color-* and --bs-* layer
   so dark mode and theming come for free. Pattern mirrors Bootstrap's
   .btn-check approach (absolutely-positioned hidden input + adjacent
   label) so no JS is needed for selected-state.
   ============================================================= */

/* Global .btn sizing — match the tighter typography we use
   everywhere else (form labels at 14px, table cells at 14px,
   tightened heading scale). Bootstrap default .btn sits at 16px
   text + 0.375rem×0.75rem padding which read as oversized once
   the rest of the type system tightened. The --bs-btn-* vars on
   .btn are the same per-class hooks Bootstrap uses internally for
   btn-sm / btn-lg, so size variants still apply on top. */
.btn {
  --bs-btn-padding-y:  0.4375rem;
  --bs-btn-padding-x:  1rem;
  --bs-btn-font-size:  0.875rem;
  --bs-btn-font-weight: 500;
  --bs-btn-line-height: 1.4;
}
.btn-sm {
  --bs-btn-padding-y:  0.25rem;
  --bs-btn-padding-x:  0.625rem;
  --bs-btn-font-size:  0.8125rem;
}
.btn-lg {
  --bs-btn-padding-y:  0.5rem;
  --bs-btn-padding-x:  1.25rem;
  --bs-btn-font-size:  1rem;
}

/* Icon-only buttons (single .bi child, no text label). Bootstrap's
   padding-x sits at ~10px on .btn-sm, which renders too rectangular
   when there's no label to fill the width — the box ends up clearly
   wider than tall. The icon glyph itself takes ~1em of horizontal
   space, so equalising padding-x to padding-y under-shoots and the
   box turns portrait; bump padding-x by 2px over padding-y so the
   box reads as a true square once the glyph is inside. Applies
   wherever an icon button appears (table action cells, toolbars,
   etc.) without any markup changes. Adjacent icon buttons also
   get a small breathing-room gap on top of the natural inline
   whitespace — the tightened sizing made the default gap feel
   pinched. */
.btn:has(> .bi:only-child) {
  --bs-btn-padding-x: calc(var(--bs-btn-padding-y) + 0.125rem);
  /* Rest-state outline drops to a neutral border instead of the
     variant colour — keeps action cells quiet by default while the
     icon glyph still carries the variant colour (red for destructive,
     muted grey for edit). Hover state restores --bs-btn-border-color
     to the variant colour via Bootstrap's hover hooks, so the
     affordance is unmistakable on interaction. */
  --bs-btn-border-color: var(--bs-border-color);
}
.btn:has(> .bi:only-child) + .btn:has(> .bi:only-child) {
  margin-left: 0.25rem;
}

/* Bootstrap button overrides. Bootstrap 5.3 baked the default blue
   into .btn-primary via per-class CSS vars (--bs-btn-bg,
   --bs-btn-hover-bg, etc.) at compile time — overriding --bs-primary
   alone is NOT enough to change buttons. Restating those vars per
   button class maps them to the teal palette without rebuilding
   Bootstrap from source (i.e. without adding a SCSS toolchain we
   don't want). Applies site-wide. */
.btn-primary {
  --bs-btn-bg:                    var(--color-primary-600);
  --bs-btn-border-color:          var(--color-primary-600);
  --bs-btn-hover-bg:              var(--color-primary-700);
  --bs-btn-hover-border-color:    var(--color-primary-700);
  --bs-btn-active-bg:             var(--color-primary-800);
  --bs-btn-active-border-color:   var(--color-primary-800);
  --bs-btn-disabled-bg:           var(--color-primary-600);
  --bs-btn-disabled-border-color: var(--color-primary-600);
  --bs-btn-focus-shadow-rgb:      13, 148, 136;
}
.btn-outline-primary {
  --bs-btn-color:                 var(--color-primary-700);
  --bs-btn-border-color:          var(--color-primary-600);
  --bs-btn-hover-bg:              var(--color-primary-600);
  --bs-btn-hover-border-color:    var(--color-primary-600);
  --bs-btn-active-bg:             var(--color-primary-700);
  --bs-btn-active-border-color:   var(--color-primary-700);
  --bs-btn-disabled-color:        var(--color-primary-600);
  --bs-btn-disabled-border-color: var(--color-primary-600);
  --bs-btn-focus-shadow-rgb:      13, 148, 136;
}
[data-bs-theme="dark"] .btn-primary {
  --bs-btn-bg:                    var(--color-primary-500);
  --bs-btn-border-color:          var(--color-primary-500);
  --bs-btn-hover-bg:              var(--color-primary-400);
  --bs-btn-hover-border-color:    var(--color-primary-400);
  --bs-btn-active-bg:             var(--color-primary-300);
  --bs-btn-active-border-color:   var(--color-primary-300);
  --bs-btn-disabled-bg:           var(--color-primary-500);
  --bs-btn-disabled-border-color: var(--color-primary-500);
}
[data-bs-theme="dark"] .btn-outline-primary {
  --bs-btn-color:                 var(--color-primary-300);
  --bs-btn-border-color:          var(--color-primary-500);
  --bs-btn-hover-bg:              var(--color-primary-500);
  --bs-btn-hover-border-color:    var(--color-primary-500);
  --bs-btn-active-bg:             var(--color-primary-400);
  --bs-btn-active-border-color:   var(--color-primary-400);
}
/* Dark-theme solid danger: brighten the base + lighten on hover, mirroring
   .btn-primary above so destructive solid buttons (e.g. Void) read the same
   way as the primary actions next to them rather than darkening on hover. */
[data-bs-theme="dark"] .btn-danger {
  --bs-btn-bg:                    var(--color-red-500);
  --bs-btn-border-color:          var(--color-red-500);
  --bs-btn-hover-bg:              var(--color-red-400);
  --bs-btn-hover-border-color:    var(--color-red-400);
  --bs-btn-active-bg:             var(--color-red-300);
  --bs-btn-active-border-color:   var(--color-red-300);
  --bs-btn-disabled-bg:           var(--color-red-500);
  --bs-btn-disabled-border-color: var(--color-red-500);
}

/* Bootstrap status-button family — same compile-time problem as
   btn-primary (Bootstrap baked default bright colours into the
   per-class --bs-btn-* vars). Restate each to the deepened -700
   shade + white text, matching the rest of the palette and giving
   readable contrast against the corresponding alert-* background. */
.btn-warning {
  --bs-btn-bg:                    var(--color-amber-700);
  --bs-btn-border-color:          var(--color-amber-700);
  --bs-btn-color:                 #fff;
  --bs-btn-hover-bg:              var(--color-amber-800);
  --bs-btn-hover-border-color:    var(--color-amber-800);
  --bs-btn-hover-color:           #fff;
  --bs-btn-active-bg:             var(--color-amber-800);
  --bs-btn-active-border-color:   var(--color-amber-800);
  --bs-btn-active-color:          #fff;
  --bs-btn-disabled-bg:           var(--color-amber-700);
  --bs-btn-disabled-border-color: var(--color-amber-700);
  --bs-btn-disabled-color:        #fff;
  --bs-btn-focus-shadow-rgb:      180, 83, 9;
}
.btn-danger {
  --bs-btn-bg:                    var(--color-red-700);
  --bs-btn-border-color:          var(--color-red-700);
  --bs-btn-color:                 #fff;
  --bs-btn-hover-bg:              var(--color-red-800);
  --bs-btn-hover-border-color:    var(--color-red-800);
  --bs-btn-hover-color:           #fff;
  --bs-btn-active-bg:             var(--color-red-800);
  --bs-btn-active-border-color:   var(--color-red-800);
  --bs-btn-active-color:          #fff;
  --bs-btn-disabled-bg:           var(--color-red-700);
  --bs-btn-disabled-border-color: var(--color-red-700);
  --bs-btn-disabled-color:        #fff;
  --bs-btn-focus-shadow-rgb:      185, 28, 28;
}
.btn-success {
  --bs-btn-bg:                    var(--color-emerald-700);
  --bs-btn-border-color:          var(--color-emerald-700);
  --bs-btn-color:                 #fff;
  --bs-btn-hover-bg:              var(--color-emerald-800);
  --bs-btn-hover-border-color:    var(--color-emerald-800);
  --bs-btn-hover-color:           #fff;
  --bs-btn-active-bg:             var(--color-emerald-800);
  --bs-btn-active-border-color:   var(--color-emerald-800);
  --bs-btn-active-color:          #fff;
  --bs-btn-disabled-bg:           var(--color-emerald-700);
  --bs-btn-disabled-border-color: var(--color-emerald-700);
  --bs-btn-disabled-color:        #fff;
  --bs-btn-focus-shadow-rgb:      4, 120, 87;
}
.btn-info {
  --bs-btn-bg:                    var(--color-indigo-700);
  --bs-btn-border-color:          var(--color-indigo-700);
  --bs-btn-color:                 #fff;
  --bs-btn-hover-bg:              var(--color-indigo-800);
  --bs-btn-hover-border-color:    var(--color-indigo-800);
  --bs-btn-hover-color:           #fff;
  --bs-btn-active-bg:             var(--color-indigo-800);
  --bs-btn-active-border-color:   var(--color-indigo-800);
  --bs-btn-active-color:          #fff;
  --bs-btn-disabled-bg:           var(--color-indigo-700);
  --bs-btn-disabled-border-color: var(--color-indigo-700);
  --bs-btn-disabled-color:        #fff;
  --bs-btn-focus-shadow-rgb:      67, 56, 202;
}

/* Bootstrap nav (tabs + pills) palette overrides. Same compile-time
   issue as buttons — Bootstrap baked the default blue into
   --bs-nav-pills-link-active-bg and --bs-nav-tabs-link-active-color
   at the class level, so overriding --bs-primary doesn't reach them.
   Status filters across the app should use nav-tabs (see the
   filter-switcher-uses-tabs feedback memory); pills get the teal
   treatment too so any remaining or future pill renders correctly.

   Active-tab styling is subtle on purpose: the active label gets
   primary-700 text + a 2px teal underline. Non-active labels stay
   slate-500 with primary-700 on hover. */
.nav-tabs {
  --bs-nav-tabs-border-color:           var(--bs-border-color);
  --bs-nav-tabs-link-hover-border-color: transparent transparent var(--color-primary-400);
  --bs-nav-tabs-link-active-color:      var(--color-primary-700);
  --bs-nav-tabs-link-active-bg:         transparent;
  --bs-nav-tabs-link-active-border-color: transparent transparent var(--color-primary-600);
}
.nav-tabs .nav-link {
  color: var(--bs-secondary-color);
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.625rem 1rem;
  transition: color 120ms, border-color 120ms;
}
.nav-tabs .nav-link:hover,
.nav-tabs .nav-link:focus-visible {
  color: var(--color-primary-700);
  border-color: transparent transparent var(--color-primary-400);
  isolation: isolate;
}
.nav-tabs .nav-link.active {
  color: var(--color-primary-700);
  background-color: transparent;
  border-color: transparent transparent var(--color-primary-600);
}
[data-bs-theme="dark"] .nav-tabs .nav-link.active {
  color: var(--color-primary-300);
  border-bottom-color: var(--color-primary-400);
}
[data-bs-theme="dark"] .nav-tabs .nav-link:hover {
  color: var(--color-primary-300);
}

/* Scrollable tabs — make the tab nav horizontally scrollable on mobile
   instead of wrapping to multiple lines. Used for property header with 9 tabs. */
.nav-tabs-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  -ms-overflow-style: none;
  background-image:
    linear-gradient(to right, var(--bs-body-bg), rgba(255,255,255,0) 40px),
    linear-gradient(to left, var(--bs-body-bg), rgba(255,255,255,0) 40px);
  background-position: left, right;
  background-repeat: no-repeat;
  background-size: 40px 100%, 40px 100%;
  background-attachment: local;
}
.nav-tabs-scroll::-webkit-scrollbar {
  display: none;
}
.nav-tabs-scroll .nav-tabs {
  flex-wrap: nowrap;
}
.nav-tabs-scroll .nav-link {
  white-space: nowrap;
}

/* Pills — used as inner status filters when nested inside an outer
   nav-tabs (per feedback_status_filters_use_tabs). Active state is
   subdued tinted teal — mirrors .badge-tint-primary so the active
   pill reads as a "selected chip" rather than competing visually
   with primary CTA buttons. Typography matches nav-tabs (uppercase
   tracking-wider) for a unified navigation vocabulary. */
.nav-pills {
  --bs-nav-pills-link-active-bg:    var(--color-primary-100);
  --bs-nav-pills-link-active-color: var(--color-primary-700);
}
.nav-pills .nav-link {
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.5rem 0.875rem;
  transition: color 120ms, background-color 120ms;
}
.nav-pills .nav-link:hover,
.nav-pills .nav-link:focus-visible {
  color: var(--color-primary-700);
  background-color: color-mix(in srgb, var(--color-primary-50) 70%, transparent);
}
.nav-pills .nav-link.active {
  border: 1px solid var(--color-primary-200);
}
[data-bs-theme="dark"] .nav-pills {
  --bs-nav-pills-link-active-bg:    rgba(20, 184, 166, 0.18);
  --bs-nav-pills-link-active-color: var(--color-primary-300);
}
[data-bs-theme="dark"] .nav-pills .nav-link.active {
  border-color: rgba(20, 184, 166, 0.3);
}

/* Page-level heading scale — matches the MP reference, which sits a
   notch tighter than Bootstrap defaults (Bootstrap h1 = 2.5rem feels
   shouty for a data-app). h1 matches .lp-page-title verbatim so the
   two are interchangeable. h2/h3 scale proportionally. */
h1 {
  font-size: 1.875rem;
  font-weight: 700;
  letter-spacing: -0.025em;
}
h2 {
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}
h3 {
  font-size: 1.25rem;
  font-weight: 600;
}
h4 {
  font-size: 1.125rem;
  font-weight: 600;
}
h5 {
  font-size: 1rem;
  font-weight: 600;
}
h6 {
  font-size: 0.875rem;
  font-weight: 600;
}

/* Bootstrap .card-header — match the .lp-section-card__header band
   so plain Bootstrap cards (used widely on dashboards and detail
   pages) share the same visual vocabulary as our named section
   cards. Smaller text, semibold, slate-50/70 band, --lp-divider
   border. Nested headings inherit so we don't end up with a
   0.875rem header containing a 1.25rem <h5>. */
.card-header {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bs-body-color);
  background-color: color-mix(in srgb, var(--color-slate-50) 70%, transparent);
  border-bottom: 1px solid var(--lp-divider);
  padding: 0.75rem 1.25rem;
}
.card-header h1, .card-header h2, .card-header h3,
.card-header h4, .card-header h5, .card-header h6 {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: normal;
  margin: 0;
}
[data-bs-theme="dark"] .card-header {
  background-color: color-mix(in srgb, var(--color-slate-800) 60%, transparent);
}

.lp-page-title {
  font-size: 1.875rem;
  font-weight: 700;
  letter-spacing: -0.025em;
  color: var(--bs-body-color);
  margin: 0;
}
.lp-page-subtitle {
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--bs-secondary-color);
  margin: 0.5rem 0 0;
  max-width: 36rem;
}

.lp-section-card {
  background-color: var(--bs-card-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  overflow: hidden;
}
.lp-section-card + .lp-section-card,
.lp-section-card + .lp-form-actions {
  margin-top: 1rem;
}
.lp-section-card__header {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  padding: 0.875rem 1.25rem;
  border-bottom: 1px solid var(--lp-divider);
  background-color: color-mix(in srgb, var(--color-slate-50) 70%, transparent);
}
[data-bs-theme="dark"] .lp-section-card__header {
  background-color: color-mix(in srgb, var(--color-slate-800) 60%, transparent);
}
.lp-section-card__header-icon {
  flex: none;
  font-size: 1rem;
  line-height: 1;
  color: var(--bs-secondary-color);
  margin-top: 0.125rem;
}
.lp-section-card__title {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bs-body-color);
  margin: 0;
  line-height: 1.4;
}
.lp-section-card__desc {
  font-size: 0.75rem;
  color: var(--bs-secondary-color);
  margin: 0.25rem 0 0;
  line-height: 1.5;
}
.lp-section-card__body {
  padding: 1.25rem;
}
.lp-section-card__body > * + * {
  margin-top: 1.25rem;
}

.lp-form-actions {
  background-color: var(--bs-card-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* Bootstrap .form-label default is 1rem regular — too heavy in the
   tighter design language we've established on lp-* surfaces. Bring
   it in line with .lp-form-label so every form across the app gets
   the more deliberate label treatment without per-page edits.
   See #91 for the broader Create/Edit form-alignment sweep that
   restructures section grouping + button strips. */
.form-label,
.lp-form-label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--bs-body-color);
  margin-bottom: 0.375rem;
}
.lp-required {
  color: var(--color-red-500);
  margin-left: 0.125rem;
}
.lp-optional {
  margin-left: 0.375rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--bs-secondary-color);
}

/* Segmented control: radio-group rendered as a button bar. Mirrors
   Bootstrap's .btn-check pattern. Inputs are absolutely-positioned
   so they don't claim grid cells — the labels fill the grid evenly.
   Default is horizontal equal-width; --vertical stacks full-width
   (use it for long labels like "Property manager"). */
.lp-segmented {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.5rem;
}
.lp-segmented--vertical {
  grid-auto-flow: row;
  grid-auto-columns: unset;
}
.lp-segmented__input {
  position: absolute;
  width: 1px;
  height: 1px;
  clip: rect(0, 0, 0, 0);
  pointer-events: none;
  opacity: 0;
}
.lp-segmented__option {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.5rem;
  padding: 0.5rem 0.875rem;
  margin-bottom: 0;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background-color: var(--bs-card-bg);
  color: var(--bs-body-color);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.3;
  cursor: pointer;
  user-select: none;
  transition: background-color 120ms, border-color 120ms, color 120ms;
}
.lp-segmented--vertical .lp-segmented__option {
  justify-content: flex-start;
  text-align: left;
}
.lp-segmented__option:hover {
  background-color: var(--color-slate-50);
  border-color: var(--color-slate-300);
}
[data-bs-theme="dark"] .lp-segmented__option:hover {
  background-color: var(--color-slate-800);
  border-color: var(--color-slate-600);
}
.lp-segmented__input:checked + .lp-segmented__option {
  background-color: var(--color-primary-50);
  border-color: var(--color-primary-600);
  color: var(--color-primary-700);
}
[data-bs-theme="dark"] .lp-segmented__input:checked + .lp-segmented__option {
  background-color: rgba(20, 184, 166, 0.15);
  border-color: var(--color-primary-500);
  color: var(--color-primary-300);
}
.lp-segmented__input:focus-visible + .lp-segmented__option {
  outline: 2px solid var(--color-primary-500);
  outline-offset: 2px;
}

/* Checklist: sequential step rows inside an .lp-section-card. Used
   by the /Onboarding wizard (#69). Each row is one of three states:
     --done     muted text, emerald check
     --current  subtle teal wash + 3px left rule + primary arrow icon
     --pending  default text, slate circle icon (no left rule)
   Row layout: icon, title+description, action (button or badge). */
.lp-checklist {
  list-style: none;
  margin: 0;
  padding: 0;
}
.lp-checklist__item {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--lp-divider);
  position: relative;
}
.lp-checklist__item:last-child {
  border-bottom: 0;
}
.lp-checklist__item--current {
  background-color: color-mix(in srgb, var(--color-primary-50) 60%, transparent);
}
[data-bs-theme="dark"] .lp-checklist__item--current {
  background-color: rgba(20, 184, 166, 0.08);
}
.lp-checklist__item--current::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background-color: var(--color-primary-600);
}
.lp-checklist__icon {
  flex: none;
  font-size: 1.25rem;
  line-height: 1;
  margin-top: 0.125rem;
}
.lp-checklist__icon--done    { color: var(--color-emerald-600); }
.lp-checklist__icon--current { color: var(--color-primary-600); }
.lp-checklist__icon--pending { color: var(--bs-secondary-color); }
[data-bs-theme="dark"] .lp-checklist__icon--done    { color: var(--color-emerald-300, #6ee7b7); }
[data-bs-theme="dark"] .lp-checklist__icon--current { color: var(--color-primary-400); }
.lp-checklist__body {
  flex: 1 1 auto;
  min-width: 0;
}
.lp-checklist__title {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--bs-body-color);
  margin: 0;
  line-height: 1.4;
}
.lp-checklist__item--done .lp-checklist__title {
  font-weight: 500;
  color: var(--bs-secondary-color);
}
.lp-checklist__desc {
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
  margin: 0.25rem 0 0;
  line-height: 1.5;
}
/* Action slot for a checklist row. Fixed width so every row's button
   shares the same column width regardless of label length — see the
   "aligned action buttons" feedback memory. Child buttons fill the
   slot and centre their text. */
.lp-checklist__action {
  flex: none;
  align-self: center;
  width: 11rem;
}
.lp-checklist__action .btn,
.lp-checklist__action .badge-tint {
  width: 100%;
  justify-content: center;
  text-align: center;
}
@media (max-width: 575.98px) {
  /* On very narrow screens the 11rem slot eats too much horizontal
     space. Let actions size to content again; rows already stack
     visually clean at small widths. */
  .lp-checklist__action { width: auto; min-width: 8rem; }
}

/* Generic aligned-button stack utility. Apply to a wrapping element
   when you have multiple buttons that should line up as a column
   (sidebar of CTAs, stacked primary+secondary pair). Children fill
   the container width and centre their labels. */
.lp-action-stack {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.lp-action-stack > .btn,
.lp-action-stack > .badge-tint {
  width: 100%;
  justify-content: center;
  text-align: center;
}

/* Muted text link with a real hover state. Use anywhere a "Skip",
   "Cancel", "Back to ..." or other secondary text-link is needed —
   replaces the .btn.btn-link.text-secondary pattern which has no
   visible hover (text-secondary's !important kills .btn-link's
   hover colour). Works on both <a> and <button type="submit"> so
   form-handler links can use it too. */
.lp-link-muted {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.375rem 0;
  background: none;
  border: 0;
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
  text-decoration: none;
  transition: color 120ms;
  cursor: pointer;
}
.lp-link-muted:hover,
.lp-link-muted:focus-visible {
  color: var(--bs-link-hover-color);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lp-link-muted:focus-visible {
  outline: 2px solid var(--color-primary-500);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Server-side validation summaries (asp-validation-summary) ship from
   the scaffold as a bare text-danger bullet list. Render every one as
   a Bootstrap danger alert so form errors read prominently and look
   consistent app-wide — including the framework-rendered Identity
   pages (Login etc.) whose markup we can't edit directly. */
.validation-summary-errors {
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
  border: 1px solid var(--bs-danger-border-subtle);
  border-radius: var(--bs-border-radius);
  background-color: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
}
.validation-summary-errors ul {
  margin-bottom: 0;
}

/* Forms that already wrap the summary in an explicit .alert
   (RequestAccount, Feedback) keep that wrapper as the box — strip the
   styling here so they don't render an alert inside an alert. */
.alert .validation-summary-errors {
  padding: 0;
  margin-bottom: 0;
  border: 0;
  background-color: transparent;
}

/* Brand icon — currently unused; the header logo <img> tags are
   commented out pending a professional logo (#108). Uncomment this
   rule and the <img> tags together to restore.
.app-brand-icon {
  height: 28px;
  width: auto;
  flex-shrink: 0;
}
*/