/* ── Design tokens ──────────────────────────────────────────────────── */
:root {
  --font-ui: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --theme-shadow-soft:   0 16px 36px #0f172a29;
  --theme-shadow-strong: 0 24px 60px #02061747;
  --theme-ring:          #38bdf847;
  --theme-transition:    160ms ease;
  --navbar-height:       57px;
  /* .subnav-bar is designed to stay a single row always -- items that don't
     fit move into an overflow "more" menu instead of wrapping (see
     .subnav-more-menu below), so this height is stable enough to treat as a
     constant the same way as --navbar-height. */
  --subnav-height:       35px;
}

:root,
html[data-theme="dark"] {
  color-scheme: dark;
  --app-bg:          #08121f;
  --app-bg-accent:   radial-gradient(circle at top right, #4dabf72e, transparent 40%),
                     linear-gradient(180deg, #08121f 0%, #0d1728 100%);
  --surface-1:       #07111fe6;
  --card-surface:    #101e36;
  --surface-2:       #0f172a9e;
  --surface-3:       #94a3b81a;
  --surface-overlay: #020617f5;
  --border-strong:   #94a3b847;
  --border-subtle:   #94a3b81f;
  --text-primary:    #e2e8f0;
  --text-secondary:  #cbd5e1;
  --text-muted:      #94a3b8;
  --text-inverse:    #06111b;
  --accent:          #38bdf8;
  --accent-strong:   #22c55e;
  --accent-warm:     #f59e0b;
  --danger:          #f87171;
  --success:         #86efac;
  --badge-muted-bg:  #94a3b829;
  --badge-live-bg:   #22c55e2e;
  --badge-warn-bg:   #f59e0b2e;
  --badge-error-bg:  #f8717129;
  --badge-dark-bg:   #1e293bcc;
  --ai:              #c4b5fd;
  --ai-strong:       #7c3aed;
  --badge-ai-bg:     color-mix(in srgb, #7c3aed 12%, var(--surface-2));
  --border-ai:       color-mix(in srgb, #7c3aed 35%, var(--border-subtle));
  --accent-gradient: linear-gradient(135deg, #0ea5e9, #22c55e);
  --header-gradient: linear-gradient(135deg, #0369a1, #15803d);
  --title-gradient:  linear-gradient(135deg, #60a5fa, #34d399);
}

html[data-theme="light"] {
  color-scheme: light;
  --app-bg:          #f2f7fb;
  --app-bg-accent:   radial-gradient(circle at top left, #0ea5e924, transparent 36%),
                     linear-gradient(180deg, #f9fcff 0%, #edf4fb 100%);
  --surface-1:       #fffffff0;
  --card-surface:    #fffffff0;
  --surface-2:       #f1f5f9eb;
  --surface-3:       #e2e8f0d9;
  --surface-overlay: #fffffffa;
  --border-strong:   #94a3b880;
  --border-subtle:   #94a3b838;
  --text-primary:    #102235;
  --text-secondary:  #294157;
  --text-muted:      #5f768c;
  --text-inverse:    #ffffff;
  --accent:          #0284c7;
  --accent-strong:   #15803d;
  --accent-warm:     #b45309;
  --danger:          #dc2626;
  --success:         #166534;
  --badge-muted-bg:  #94a3b82e;
  --badge-live-bg:   #22c55e24;
  --badge-warn-bg:   #f59e0b24;
  --badge-error-bg:  #f8717124;
  --badge-dark-bg:   #64748b29;
  --ai:              #7c3aed;
  --ai-strong:       #7c3aed;
  --badge-ai-bg:     #f3f0ff;
  --border-ai:       #c4b5fd;
  --accent-gradient: linear-gradient(135deg, #0284c7, #16a34a);
  --header-gradient: linear-gradient(135deg, #0284c7, #16a34a);
  --title-gradient:  linear-gradient(135deg, #0f172a, #0284c7);
  --theme-shadow-soft:   0 18px 36px #94a3b829;
  --theme-shadow-strong: 0 26px 54px #94a3b82e;
  --theme-ring:          #0284c738;
}

/* ── Bootstrap global token mapping ─────────────────────────────────── */
/* Set Bootstrap's CSS variables so its components render with our tokens */
html[data-theme="dark"],
html[data-theme="light"] {
  --bs-body-bg:            var(--app-bg);
  --bs-body-color:         var(--text-primary);
  --bs-body-font-family:   var(--font-ui);
  --bs-heading-color:      var(--text-primary);
  --bs-secondary-color:    var(--text-muted);
  --bs-emphasis-color:     var(--text-primary);
  --bs-secondary-bg:       var(--surface-2);
  --bs-tertiary-bg:        var(--surface-3);
  --bs-border-color:       var(--border-subtle);
  --bs-link-color:         var(--accent);
  --bs-link-hover-color:   var(--accent);
}

/* ── Base ────────────────────────────────────────────────────────────── */
html, body {
  background: var(--app-bg-accent);
  transition: background var(--theme-transition), color var(--theme-transition);
}

html {
  min-height: 100%;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

body > main {
  flex: 1 0 auto;
  width: 100%;
}
:focus-visible {
  outline: 2px solid var(--theme-ring);
  outline-offset: 2px;
}

/* ── Navbar ──────────────────────────────────────────────────────────── */
/* Site-wide default: navbar hides on scroll down, reveals on scroll up
   (any amount, from any depth in the page) — see the scroll-direction
   listener in base.html that toggles .navbar-hidden on <html>. */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* Higher than .toast-container (1080): .navbar's own z-index caps the
     stacking context for everything inside it (including open dropdown
     menus), so bumping a descendant's z-index alone can't outrank a
     sibling of the navbar itself — e.g. a persistent toast — if the
     navbar's own value is lower. */
  z-index: 1085;
  background: var(--surface-1);
  border-bottom: 1px solid var(--border-strong);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  transform: translateY(0);
  transition: transform 200ms ease;
  --bs-navbar-color:             var(--text-secondary);
  --bs-navbar-hover-color:       var(--text-primary);
  --bs-navbar-active-color:      var(--accent);
  --bs-navbar-brand-color:       var(--text-primary);
  --bs-navbar-brand-hover-color: var(--text-primary);
  --bs-navbar-toggler-border-color: var(--border-strong);
  --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28226, 232, 240, 0.92%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
html.navbar-hidden .navbar {
  transform: translateY(-100%);
}
/* .navbar is position:fixed, so it doesn't participate in the browser's
   elastic "rubber-band" bounce on overscroll — during the bounce the page
   stretches down past its real top edge, briefly exposing whatever is
   above the navbar. Extending the navbar's own background upward past its
   top edge means anything the bounce exposes there is still the matching
   surface color rather than the page background gradient behind it. */
.navbar::before {
  content: "";
  position: absolute;
  top: -100px;
  left: 0;
  right: 0;
  height: 100px;
  background: inherit;
}
/* .navbar leaving normal document flow (fixed) means page content would
   otherwise jump up underneath it — reserve its height at the top of
   every page. */
main.container {
  margin-top: var(--navbar-height);
}

.navbar-toggler {
  padding: 0.45rem 0.65rem;
  border-color: var(--border-strong);
  background: var(--surface-2);
}

.navbar-toggler:hover {
  background: var(--surface-3);
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border-strong));
}

.navbar-toggler:focus {
  box-shadow: 0 0 0 3px var(--theme-ring);
}

.navbar-toggler-icon {
  width: 1.3em;
  height: 1.3em;
}

html[data-theme="light"] .navbar {
  --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2841, 65, 87, 0.80%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

.navbar-brand {
  font-weight: 700;
  background: var(--title-gradient);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.navbar-nav {
  gap: 4px;
  align-items: center;
}

.navbar .nav-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  border-radius: 10px;
  padding: 0 14px;
  transition: background var(--theme-transition), color var(--theme-transition);
}

.navbar .nav-link:hover       { background: var(--surface-3); }
.navbar .nav-link.active      { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.navbar .nav-link.dropdown-toggle::after { opacity: 0.6; }
.navbar .nav-link.dropdown-toggle:hover::after { opacity: 1; }

.navbar .form-control { border-radius: 12px 0 0 12px; }

.navbar-search-form {
  max-width: 500px;
  width: 100%;
}

/* Icon-only input-group button (e.g. navbar search, "add value" +
   buttons). border-radius on the end cap is already handled by the
   generic .input-group .btn:last-child rule further down this file —
   this only owns color. Labeled/text action buttons (Buy Seats, Join
   Company, Send) are a different intent and stay on btn-primary,
   not this class. */
.input-group .btn-outline-light {
  border-color: var(--border-strong);
  color: var(--text-secondary);
  background: var(--surface-3);
}

.input-group .btn-outline-light:hover {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 34%, var(--border-strong));
}

@media (max-width: 991.98px) {
  .navbar .container-fluid {
    position: relative;
    z-index: 1;
  }

  .mobile-nav-panel {
    z-index: 1000;
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: max-content;
    min-width: 140px;
    max-width: min(320px, calc(100vw - 1rem));
    padding: 12px;
    border: 1px solid var(--border-strong);
    border-radius: 18px;
    background: var(--surface-overlay);
    box-shadow: var(--theme-shadow-strong);
  }

  .mobile-nav-panel.collapsing {
    right: 0;
  }

  .mobile-nav-search {
    max-width: none !important;
    width: min(100%, 280px);
    margin: 0 0 10px 0 !important;
  }

  .mobile-nav-list {
    width: max-content;
    min-width: 100%;
    align-items: stretch;
    gap: 4px;
  }

  .mobile-nav-list > .nav-item {
    width: 100%;
  }

  .mobile-nav-list .nav-link {
    justify-content: flex-start;
    width: 100%;
    min-height: 40px;
    padding: 0 14px;
    border: 1px solid transparent;
    background: transparent;
    border-radius: 12px;
    text-align: left;
    white-space: nowrap;
    gap: 10px;
  }

  .mobile-nav-list .nav-link i,
  .mobile-nav-list .dropdown-item i {
    width: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
  }

  .mobile-nav-list .nav-link:hover,
  .mobile-nav-list .nav-link:focus-visible,
  .mobile-nav-list .nav-link.active {
    background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
    border-color: color-mix(in srgb, var(--accent) 26%, var(--border-strong));
  }

  .mobile-nav-list .dropdown-toggle::after {
    margin-left: auto;
  }

  .mobile-nav-list .dropdown-menu {
    position: static;
    inset: auto;
    transform: none !important;
    width: 100%;
    margin-top: 4px;
    padding: 4px 0 0 0;
    border: none;
    background: transparent;
    box-shadow: none;
  }

  .mobile-nav-list .dropdown-item {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    border-radius: 10px;
    padding: 8px 14px;
    white-space: nowrap;
  }

  .mobile-nav-list .dropdown-divider {
    margin: 4px 0;
  }
}
/* ── Dropdown ────────────────────────────────────────────────────────── */
.dropdown-menu {
  --bs-dropdown-bg:                var(--surface-overlay);
  --bs-dropdown-border-color:      var(--border-strong);
  --bs-dropdown-border-radius:     14px;
  --bs-dropdown-box-shadow:        var(--theme-shadow-strong);
  --bs-dropdown-color:             var(--text-secondary);
  --bs-dropdown-link-color:        var(--text-secondary);
  --bs-dropdown-link-hover-color:  var(--text-primary);
  --bs-dropdown-link-hover-bg:     var(--surface-3);
  --bs-dropdown-link-active-color: var(--text-primary);
  --bs-dropdown-link-active-bg:    var(--surface-3);
  --bs-dropdown-divider-bg:        var(--border-subtle);
  --bs-dropdown-item-padding-x:    12px;
  --bs-dropdown-item-padding-y:    8px;
  --bs-dropdown-inner-border-radius: 9px;
  padding: 6px;
}

/* ── Cards ───────────────────────────────────────────────────────────── */
.card {
  --bs-card-bg:                  var(--card-surface);
  --bs-card-border-color:        var(--border-strong);
  --bs-card-border-radius:       18px;
  --bs-card-inner-border-radius: 17px;
  --bs-card-color:               var(--text-primary);
  --bs-card-cap-bg:              var(--surface-2);
  --bs-card-cap-color:           var(--text-primary);
  --bs-card-title-color:         var(--text-primary);
  --bs-card-subtitle-color:      var(--text-secondary);
  box-shadow: var(--theme-shadow-soft);
  transition: box-shadow var(--theme-transition);
}

.card:hover { box-shadow: var(--theme-shadow-strong); }

.card-header {
  border-bottom: 1px solid var(--border-subtle);
  padding: 14px 20px;
}

.card-header > i,
.card-header .card-title i,
.card-header h5 > i,
.card-header h6 > i {
  color: var(--accent);
}

.card-header.bg-light,
.card-header.bg-white {
  background: var(--surface-2) !important;
  color: var(--text-primary) !important;
  border-bottom-color: var(--border-subtle) !important;
}

.card-header.bg-primary {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 10%, var(--card-surface)), var(--card-surface)) !important;
  color: var(--text-primary) !important;
  border-bottom-color: color-mix(in srgb, var(--accent) 24%, var(--border-subtle)) !important;
}

.card-header.bg-success {
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-live-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  color: var(--text-primary) !important;
  border-bottom-color: color-mix(in srgb, var(--accent-strong) 26%, var(--border-subtle)) !important;
}

.card-header.bg-warning {
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-warn-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  color: var(--text-primary) !important;
  border-bottom-color: color-mix(in srgb, var(--accent-warm) 26%, var(--border-subtle)) !important;
}
.card-header.bg-warning > i,
.card-header.bg-warning .card-title i,
.card-header.bg-warning h5 > i,
.card-header.bg-warning h6 > i {
  color: var(--accent-warm);
}

.card-header.bg-danger {
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-error-bg) 90%, var(--card-surface) 10%), var(--card-surface)) !important;
  color: var(--text-primary) !important;
  border-bottom-color: color-mix(in srgb, var(--danger) 26%, var(--border-subtle)) !important;
}
.card-header.bg-danger > i,
.card-header.bg-danger .card-title i,
.card-header.bg-danger h5 > i,
.card-header.bg-danger h6 > i {
  color: var(--danger);
}

.card-header.bg-secondary {
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-muted-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  color: var(--text-primary) !important;
  border-bottom-color: color-mix(in srgb, var(--text-muted) 18%, var(--border-subtle)) !important;
}

.card.bg-light,
.card.bg-white {
  background: var(--card-surface) !important;
  color: var(--text-primary) !important;
  border-color: var(--border-strong) !important;
}

.bg-surface-2 {
  background-color: var(--surface-2) !important;
  color: var(--text-primary) !important;
}

/* ── Collapsible card ────────────────────────────────────────────────── */
/* Card with a chevron button that collapses the body. Usage:
   <div class="card">
     <div class="card-header collapsible-card-header">
       <h5 class="mb-0 text-accent"><i class="bi bi-card-text"></i> Title</h5>
       <button type="button" class="collapsible-card-trigger"
               data-bs-toggle="collapse" data-bs-target="#myCollapse"
               aria-expanded="true" aria-controls="myCollapse">
         <i class="bi bi-chevron-down collapsible-card-chevron"></i>
       </button>
     </div>
     <div id="myCollapse" class="collapse show">
       <div class="card-body">…</div>
     </div>
   </div>
*/
.collapsible-card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  user-select: none;
  transition: background var(--theme-transition);
}
.collapsible-card-trigger {
  appearance: none;
  -webkit-appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  box-shadow: none;
  line-height: 1;
  cursor: pointer;
}
.collapsible-card-trigger:hover .collapsible-card-chevron,
.collapsible-card-trigger:focus-visible .collapsible-card-chevron {
  color: var(--text-secondary) !important;
}
.collapsible-card-trigger:focus-visible {
  outline: 2px solid var(--theme-ring);
  outline-offset: 2px;
}
.collapsible-card-chevron {
  color: var(--text-muted) !important;
  flex-shrink: 0;
  transition: transform var(--theme-transition), color var(--theme-transition);
}
.collapsible-card-trigger[aria-expanded="true"] .collapsible-card-chevron {
  transform: rotate(180deg);
  color: var(--text-secondary) !important;
}
.collapsible-card-header:has(+ .collapse:not(.show)) {
  border-bottom-left-radius:  var(--bs-card-inner-border-radius);
  border-bottom-right-radius: var(--bs-card-inner-border-radius);
  border-bottom-color: transparent;
}
html[data-theme="dark"] .collapsible-card-header .btn-close { filter: invert(1); }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  font-family: var(--font-ui);
  font-weight: 700;
  border-radius: 12px;
  min-height: 40px;
  transition: opacity var(--theme-transition), transform 100ms ease, box-shadow var(--theme-transition);
}

.btn:active  { transform: scale(0.97); }
.btn:disabled, .btn.disabled { opacity: 0.48; filter: saturate(0.7); }

.btn i { color: inherit; line-height: 1; flex-shrink: 0; }

/* Reset Bootstrap Icons' baseline offset inside all buttons */
.btn i::before { vertical-align: 0; }

/* btn-primary: gradient bg can't go through --bs-btn-bg (background-color only),
   so we set background directly. Both rules needed to survive Bootstrap's :hover. */
.btn-primary,
.btn-primary:hover {
  --bs-btn-color:               white;
  --bs-btn-hover-color:         white;
  --bs-btn-border-color:        transparent;
  --bs-btn-hover-border-color:  transparent;
  background: var(--accent-gradient);
  box-shadow: var(--theme-shadow-soft);
}

.btn-primary:hover { opacity: 0.9; }

.btn-outline-secondary {
  --bs-btn-color:               var(--text-secondary);
  --bs-btn-bg:                  transparent;
  --bs-btn-border-color:        var(--border-strong);
  --bs-btn-hover-color:         var(--text-primary);
  --bs-btn-hover-bg:            var(--surface-3);
  --bs-btn-hover-border-color:  var(--border-strong);
  --bs-btn-active-color:        var(--text-primary);
  --bs-btn-active-bg:           var(--surface-3);
}

.btn-outline-primary {
  --bs-btn-color:               var(--accent);
  --bs-btn-border-color:        var(--accent);
  --bs-btn-hover-color:         var(--accent);
  --bs-btn-hover-bg:            color-mix(in srgb, var(--accent) 16%, transparent);
  --bs-btn-hover-border-color:  var(--accent);
  --bs-btn-active-color:        var(--accent);
  --bs-btn-active-bg:           color-mix(in srgb, var(--accent) 16%, transparent);
  --bs-btn-active-border-color: var(--accent);
  font-weight:                  600;
}


.btn-outline-warning {
  --bs-btn-color:               var(--accent-warm);
  --bs-btn-border-color:        var(--border-strong);
  --bs-btn-hover-color:         var(--accent-warm);
  --bs-btn-hover-bg:            var(--badge-warn-bg);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--accent-warm) 40%, var(--border-strong));
  --bs-btn-active-color:        var(--accent-warm);
  --bs-btn-active-bg:           var(--badge-warn-bg);
  background: var(--surface-3);
}

.text-warning { color: var(--accent-warm) !important; }
.text-danger  { color: var(--danger)      !important; }
.text-success { color: var(--success)     !important; }

.border-primary { border-color: color-mix(in srgb, var(--accent) 40%, var(--border-strong))       !important; }
.border-danger  { border-color: color-mix(in srgb, var(--danger) 40%, var(--border-strong))       !important; }
.border-success { border-color: color-mix(in srgb, var(--accent-strong) 40%, var(--border-strong)) !important; }
.border-warning { border-color: color-mix(in srgb, var(--accent-warm) 40%, var(--border-strong))   !important; }

.btn-warning {
  --bs-btn-color:               var(--accent-warm);
  --bs-btn-bg:                  var(--badge-warn-bg);
  --bs-btn-border-color:        color-mix(in srgb, var(--accent-warm) 40%, var(--border-strong));
  --bs-btn-hover-color:         var(--accent-warm);
  --bs-btn-hover-bg:            var(--badge-warn-bg);
}

.btn-success {
  --bs-btn-color:               var(--success);
  --bs-btn-bg:                  var(--badge-live-bg);
  --bs-btn-border-color:        color-mix(in srgb, var(--accent-strong) 40%, var(--border-strong));
  --bs-btn-hover-color:         var(--success);
  --bs-btn-hover-bg:            var(--badge-live-bg);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--accent-strong) 48%, var(--border-strong));
}

.btn-danger {
  --bs-btn-color:               var(--danger);
  --bs-btn-bg:                  var(--badge-error-bg);
  --bs-btn-border-color:        color-mix(in srgb, var(--danger) 40%, var(--border-strong));
  --bs-btn-hover-color:         var(--danger);
  --bs-btn-hover-bg:            var(--badge-error-bg);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--danger) 48%, var(--border-strong));
}

.btn-info {
  --bs-btn-color:               var(--accent);
  --bs-btn-bg:                  color-mix(in srgb, var(--accent) 16%, transparent);
  --bs-btn-border-color:        color-mix(in srgb, var(--accent) 34%, var(--border-strong));
  --bs-btn-hover-color:         var(--accent);
  --bs-btn-hover-bg:            color-mix(in srgb, var(--accent) 20%, transparent);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--accent) 42%, var(--border-strong));
}

.btn-outline-danger {
  --bs-btn-color:               var(--danger);
  --bs-btn-border-color:        color-mix(in srgb, var(--danger) 40%, var(--border-strong));
  --bs-btn-hover-color:         var(--danger);
  --bs-btn-hover-bg:            var(--badge-error-bg);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--danger) 48%, var(--border-strong));
  --bs-btn-active-color:        var(--danger);
  --bs-btn-active-bg:           var(--badge-error-bg);
}

.btn-outline-success {
  --bs-btn-color:               var(--success);
  --bs-btn-border-color:        color-mix(in srgb, var(--accent-strong) 40%, var(--border-strong));
  --bs-btn-hover-color:         var(--success);
  --bs-btn-hover-bg:            var(--badge-live-bg);
  --bs-btn-hover-border-color:  color-mix(in srgb, var(--accent-strong) 48%, var(--border-strong));
  --bs-btn-active-color:        var(--success);
  --bs-btn-active-bg:           var(--badge-live-bg);
  font-weight:                  600;
}

.btn-outline-ai {
  --bs-btn-color:               var(--ai);
  --bs-btn-border-color:        var(--border-ai);
  --bs-btn-bg:                  var(--badge-ai-bg);
  --bs-btn-hover-color:         #fff;
  --bs-btn-hover-bg:            var(--ai-strong);
  --bs-btn-hover-border-color:  var(--ai-strong);
  --bs-btn-active-color:        #fff;
  --bs-btn-active-bg:           var(--ai-strong);
}

.btn-sm { border-radius: 10px; min-height: 32px; padding: 5px 12px; display: inline-flex; align-items: center; gap: 4px; }
.btn-lg { border-radius: 14px; min-height: 48px; }

/* Square icon-only button — combine with btn-sm + a colour variant.
   Convention: bi-x-lg = remove from selection (reversible);
               bi-trash = permanent delete (destructive). */
.btn-icon-sm {
  width: 2rem;
  height: 2rem;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.btn-icon-sm > i { line-height: 1; }

.btn-outline-danger.btn-icon-sm,
.btn-danger.btn-icon-sm {
  --bs-btn-border-color:       var(--danger);
  --bs-btn-hover-border-color: var(--danger);
}

.btn-outline-secondary.btn-icon-sm {
  --bs-btn-border-color:       var(--text-muted);
  --bs-btn-hover-border-color: var(--text-muted);
}

/* ── Form controls ───────────────────────────────────────────────────── */
/* Bootstrap 5.3 exposes limited CSS vars for inputs; use direct props */
.form-control,
.form-select {
  background-color: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text-primary);
  border-radius: 12px;
  font-family: var(--font-ui);
}

.form-control::placeholder { color: var(--text-muted); }

.form-control:focus,
.form-select:focus {
  background-color: var(--surface-2);
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border-strong));
  box-shadow: 0 0 0 3px var(--theme-ring);
  color: var(--text-primary);
}

.form-label {
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 13px;
}

/* All-caps, muted variant — use alongside .form-label, not instead of it:
   class="form-label form-label-caps". */
.form-label-caps {
  color: var(--text-muted);
  text-transform: uppercase;
}

/* Label row: flex container pairing a label with a badge/status element.
   Restores the bottom gap that mb-0 on the label would otherwise remove. */
.form-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 0.5rem;
}

/* Close variant — badge/status sits immediately after the label instead
   of pushed to the far edge (space-between). For full-width fields where
   the label and badge read as one unit (e.g. "Business Type  PICK ONE"),
   not the half-column "label ... status" layout .form-label-row's
   default was built for. Use alongside .form-label-row, not instead of
   it: class="form-label-row form-label-row-close". */
.form-label-row-close {
  justify-content: flex-start;
}

.form-check-input {
  background-color: var(--surface-2);
  border-color: var(--border-strong);
  box-shadow: none;
}

html[data-theme="light"] .form-check-input {
  background-color: white;
  border-color: #64748b99;
}

.form-check-input[type="checkbox"] {
  border-radius: 6px;
}

.form-check-input[type="radio"] {
  border-radius: 50%;
}

.form-check-input:hover {
  border-color: color-mix(in srgb, var(--accent) 34%, var(--border-strong));
}

.form-check-input:focus {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--border-strong));
  box-shadow: 0 0 0 3px var(--theme-ring);
}

.form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}

.form-check-input[type="checkbox"]:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='rgba%2807,17,31,0.96%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.2' d='M4.5 10.5l3.2 3.2L15.5 6.5'/%3e%3c/svg%3e");
}

.form-check-input[type="radio"]:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='rgba%2807,17,31,0.96%29'/%3e%3c/svg%3e");
}

/* NEW THEME.CSS ADDITION -- FLAGGING FOR SIGN-OFF (2026-07-24):
   .form-check-input[type="checkbox"]'s 6px border-radius (above, needed for
   plain checkboxes) and checkmark :checked background-image were also
   applying to .form-switch .form-check-input at equal CSS specificity,
   loaded after Bootstrap -- so every .form-switch toggle in the app (5
   templates: new_project, block_editor, process_proposal,
   process_questions, collections/edit_collection) rendered as a small
   rounded square with a static checkmark instead of a sliding pill.
   Promoted from generate_presentation.html's page-local .tight-switch
   (compact 32x18 pill, plain white dot, no border) -- chosen as the
   app-wide standard over the initial 48x28 attempt (matched to
   .theme-switch) as too large/heavy for inline use next to labels and
   other controls. Same dot color/size in both themes since tight-switch
   never needed a per-theme thumb variant either. Wrapper is flexed
   (instead of Bootstrap's padding-left/negative-margin-left technique) so
   the switch reliably vertical-centers against its label regardless of
   the 18px height differing from a plain checkbox's -- fixed 2026-07-24
   after the first version centered against the top of the line box
   instead of the label's middle. */
.form-check.form-switch {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding-left: 0;
}

.form-switch .form-check-input {
  width: 32px;
  height: 18px;
  margin-top: 0;
  margin-left: 0;
  flex-shrink: 0;
  border: none;
  border-radius: 999px;
  background-color: var(--border-strong);
  box-shadow: none;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3e%3ccircle cx='7' cy='7' r='7' fill='%23ffffff'/%3e%3c/svg%3e");
  background-size: 14px 14px;
  background-repeat: no-repeat;
  background-position: 2px center;
  transition: background-position .15s ease, background-color .15s ease;
}

/* html[data-theme="light"] .form-check-input (above) sets a plain white
   background-color at higher specificity (extra html element selector) --
   without this override it silently wins over .form-switch's track color
   in light theme, leaving a white dot invisible on a white track (the
   exact bug this component was built to fix, just relocated). */
html[data-theme="light"] .form-switch .form-check-input {
  background-color: var(--border-strong);
}

.form-switch .form-check-input:checked {
  background-color: var(--accent);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3e%3ccircle cx='7' cy='7' r='7' fill='%23ffffff'/%3e%3cpath fill='none' stroke='rgba%287,17,31,0.85%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.8' d='M3.2 7.4l2.2 2.2L10.8 4.6'/%3e%3c/svg%3e");
  background-position: calc(100% - 2px) center;
}

html[data-theme="light"] .form-switch .form-check-input:checked {
  background-color: var(--accent);
}

.form-check-label {
  color: var(--text-secondary);
}

.form-control.bg-light,
.form-select.bg-light {
  background-color: var(--surface-2) !important;
  color: var(--text-primary) !important;
  border-color: var(--border-strong) !important;
}

.input-group .form-control:not(:last-child) { border-radius: 12px 0 0 12px; }
.input-group .btn:last-child                { border-radius: 0  12px 12px 0; }

/* Narrow input-group widths — promoted out of repeated inline
   style="max-width:…" on the "add a custom X" chip-entry rows (Business
   Type, Capabilities, Engagement Type, Set-Asides, Required Review Areas)
   and short-code entry rows (NAICS) in wizard/company_setup*.html. */
.input-group-narrow { max-width: 420px; }
.input-group-code   { max-width: 300px; }

/* ── Legacy Bootstrap compatibility ─────────────────────────────────── */
.list-group {
  --bs-list-group-bg: transparent;
  --bs-list-group-border-color: var(--border-strong);
  --bs-list-group-color: var(--text-primary);
  --bs-list-group-action-color: var(--text-primary);
  --bs-list-group-action-hover-color: var(--text-primary);
  --bs-list-group-action-active-color: var(--text-primary);
  --bs-list-group-action-hover-bg: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
  --bs-list-group-action-active-bg: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
  --bs-list-group-border-radius: 14px;
}

.list-group-item {
  background: color-mix(in srgb, var(--surface-2) 92%, transparent);
  color: var(--text-primary);
  border-color: var(--border-subtle);
}

.list-group-item-action:hover,
.list-group-item-action:focus {
  color: var(--text-primary);
}

.list-group-flush > .list-group-item {
  background: transparent;
  border-left: 0;
  border-right: 0;
}

.list-group-flush > .list-group-item:first-child {
  border-top: 0;
}

.list-group-flush > .list-group-item:last-child {
  border-bottom: 0;
}

/* Clip list-group-flush content to the card's inner border-radius */
.card .list-group-flush {
  overflow: hidden;
  border-bottom-left-radius: var(--bs-card-inner-border-radius);
  border-bottom-right-radius: var(--bs-card-inner-border-radius);
}

/* Clip flush table-responsive edges to the card's inner border-radius.
   Applies only when card-body.p-0 is the first/last child (no card-header/footer
   between the card edge and the table), so no internal gap is introduced.
   Works because table-responsive already has overflow-x: auto (non-visible),
   which activates border-radius clipping on its content. */
.card > .card-body.p-0:first-child > .table-responsive:first-child {
  border-top-left-radius:  var(--bs-card-inner-border-radius);
  border-top-right-radius: var(--bs-card-inner-border-radius);
}
.card > .card-body.p-0:last-child > .table-responsive:last-child {
  border-bottom-left-radius: var(--bs-card-inner-border-radius);
  border-bottom-right-radius: var(--bs-card-inner-border-radius);
}

/* Card body containing scrollable rich text (instructions, notes, etc.) */
.rich-text-display {
  white-space: normal;
  line-height: 1.6;
  min-height: 180px;
  max-height: 22.4em;
  overflow-y: auto;
  padding-right: 1.0rem;
  word-break: normal;
  overflow-wrap: break-word;
  hyphens: none;
}

/* ── Table sort button ───────────────────────────────────────────────── */
/* Invisible <button> inside a <th> that inherits the heading appearance
   exactly. Only the sort icon gets its own colour treatment.
   JS contract: add .is-active to the active button and set
   data-direction="asc"|"desc" to rotate the icon. */
.table-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  line-height: inherit;
  cursor: pointer;
}
.table-sort-btn:hover,
.table-sort-btn.is-active { color: inherit; }

.table-sort-icon {
  color: var(--text-muted);
  transition: color var(--theme-transition), transform var(--theme-transition);
}
.table-sort-btn:hover .table-sort-icon                       { color: var(--text-secondary); }
.table-sort-btn.is-active .table-sort-icon                   { color: var(--accent); }
.table-sort-btn[data-direction="asc"] .table-sort-icon       { transform: rotate(180deg); }

/* ── Accordion ──────────────────────────────────────────────────────── */
.accordion {
  --bs-accordion-bg:                    transparent;
  --bs-accordion-border-color:          var(--border-subtle);
  --bs-accordion-btn-color:             var(--text-primary);
  --bs-accordion-btn-bg:                transparent;
  --bs-accordion-btn-padding-x:         1rem;
  --bs-accordion-btn-padding-y:         0.85rem;
  --bs-accordion-btn-focus-box-shadow:  0 0 0 3px var(--theme-ring);
  --bs-accordion-active-color:          var(--accent);
  --bs-accordion-active-bg:             color-mix(in srgb, var(--accent) 10%, transparent);
  --bs-accordion-body-padding-x:        1rem;
  --bs-accordion-body-padding-y:        0.75rem;
  --bs-accordion-border-radius:         18px;
  --bs-accordion-inner-border-radius:   17px;
}

.accordion-item { background: transparent; }

.accordion-button {
  font-weight: 600;
  font-size: 0.95rem;
  transition: background var(--theme-transition), color var(--theme-transition);
}

.accordion-button:hover {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.accordion-button:not(.collapsed) {
  box-shadow: 0 1px 0 var(--border-subtle);
}

/* Restore bottom border on standalone accordions; remove only when inside a card */
.accordion-item:last-child {
  border-bottom: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color) !important;
}
.card .accordion-item:last-child {
  border-bottom: 0 !important;
}

/* Dark mode: Bootstrap's caret SVG is hardcoded dark — override with light glyphs */
html[data-theme="dark"] .accordion-button {
  --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23e2e8f0'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
  --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2338bdf8'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

/* ── Tips ────────────────────────────────────────────────────────────── */
/* All tooltips: trigger:'click' (Bootstrap handles toggle), no arrow.
   --bs-tooltip-zindex is raised above the fixed navbar's z-index (1085,
   see .navbar above) -- Bootstrap's default (1080) is lower, so any
   tooltip whose Popper-computed position lands near the top of the
   viewport (e.g. .page-info-btn next to a page heading, right under the
   navbar) used to render partly hidden behind it. Same bug class as the
   modal/navbar issue elsewhere in this file, but tooltips are positioned
   dynamically relative to their trigger (not a static margin), so the fix
   here is just letting the tooltip render on top of the navbar rather than
   repositioning it -- standard for a transient, click-triggered popup. */
.tooltip {
  --bs-tooltip-bg: var(--surface-overlay);
  --bs-tooltip-color: var(--text-secondary);
  --bs-tooltip-border-radius: 12px;
  --bs-tooltip-opacity: 1;
  --bs-tooltip-max-width: min(600px, calc(100vw - 32px));
  --bs-tooltip-zindex: 1090;
}

.tooltip .tooltip-inner {
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: var(--surface-overlay);
  box-shadow: var(--theme-shadow-soft);
  color: var(--text-secondary);
  text-align: left;
  line-height: 1.5;
}

.tooltip .tooltip-arrow { display: none; }

/* ── Info button — page / heading level ─────────────────────────────── */
/* Just the .page-info-btn markup + title attribute -- init, positioning,
   and dismissal are all handled globally by ui/static/js/page-info-tip.js
   (loaded from base.html). Ref: collections page. */
.page-info-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  margin-left: 0.375rem;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font-size: 1em;
  line-height: 1;
  vertical-align: middle;
  cursor: pointer;
  transition: color var(--theme-transition);
}
.page-info-btn:hover,
.page-info-btn[aria-expanded="true"] { color: var(--accent); }
.page-info-btn:focus-visible { outline: 2px solid var(--theme-ring); outline-offset: 2px; }

/* ── Info popup — inline / label level ──────────────────────────────── */
/* Click-toggle popup for use next to form labels, checkboxes, etc.      */
/* Same approach as wizard tooltips. JS lives in base.html (global).     */
.info-popup-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.info-popup-wrap::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  width: min(320px, calc(100vw - 48px));
  height: 18px;
}
.info-popup-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s ease, transform 0.1s ease;
}
.info-popup-btn:hover { color: var(--accent); }
.info-popup-btn:focus-visible { outline: 2px solid var(--theme-ring); outline-offset: 2px; }
.info-popup-btn:active { transform: scale(0.97); }

.info-popup {
  --info-popup-shift: 0px;
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 1090;
  width: min(320px, calc(100vw - 48px));
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: var(--surface-overlay);
  box-shadow: var(--theme-shadow-soft);
  color: var(--text-secondary);
  font-size: 0.8125rem;
  font-weight: normal;
  line-height: 1.5;
  white-space: normal;
  opacity: 0;
  visibility: hidden;
  transform: translate(var(--info-popup-shift), -4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.15s;
  pointer-events: none;
}
.info-popup-wrap.is-open .info-popup {
  opacity: 1;
  visibility: visible;
  transform: translate(var(--info-popup-shift), 0);
  transition-delay: 0s;
  pointer-events: auto;
}

/* Tip box — inline one-liner hint.
   <div class="tip-box">
     <i class="bi bi-lightbulb tip-box-icon"></i>
     <span><strong>Tip:</strong> Your hint text here.</span>
   </div>
*/
.tip-box {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent);
  border-radius: 10px;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--text-secondary);
  text-align: left;
}
.tip-box-icon {
  color: var(--accent);
  flex-shrink: 0;
  margin-top: 2px;
}
.tip-box strong { color: var(--accent); }

/* Tip card — card with lightbulb header and body containing text + list.
   <div class="card tip-card">
     <div class="card-header">
       <h6 class="mb-0 text-accent"><i class="bi bi-lightbulb"></i> Title</h6>
     </div>
     <div class="card-body">
       <p>Explanation text.</p>
       <ul>
         <li>Item one</li>
         <li>Item two</li>
       </ul>
     </div>
   </div>
*/
.tip-card .card-body {
  font-size: 0.875rem;
  color: var(--text-secondary);
}
.tip-card .card-body ul,
.tip-card .card-body ol {
  padding-left: 1.25rem;
  margin-bottom: 0;
}
.tip-card .card-body li + li { margin-top: 0.35rem; }
.tip-card .card-body > :last-child { margin-bottom: 0; }

/* ── Badges ──────────────────────────────────────────────────────────── */
/* Bootstrap's .bg-* utilities use !important internally, so badge color
   overrides require it too. All other badge properties are clean. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  font-family: var(--font-ui);
  font-weight: 600;
  padding: 0.35em 0.75em;
}

.badge.bg-primary   { background: color-mix(in srgb, var(--accent) 20%, transparent) !important; color: var(--accent) !important; }
.badge.bg-success   { background: var(--badge-live-bg)  !important; color: var(--success)      !important; }
.badge.bg-warning   { background: var(--badge-warn-bg)  !important; color: var(--accent-warm)  !important; }
.badge.bg-danger    { background: var(--badge-error-bg) !important; color: var(--danger)       !important; }
.badge.bg-secondary { background: var(--badge-muted-bg) !important; color: var(--text-secondary) !important; }
.badge.bg-dark      { background: var(--badge-dark-bg)  !important; color: var(--text-muted)   !important; }

/* Compact variant — dense inline lists (e.g. per-row confidence/status chips) */
.badge-sm { font-size: 0.65rem; padding: 0.25em 0.6em; }
/* Uppercase variant — e.g. cardinality hints ("PICK ONE" / "PICK ANY") paired with a .form-label-row */
.badge-caps { text-transform: uppercase; }
/* "Feeds: ..." variant — a readable phrase (not a short instruction), so no uppercase transform;
   distinct, lower-key tone so it never competes visually with a pick-one/pick-any badge already
   sitting in the same .form-label-row. */
.badge-feeds { text-transform: none; background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); }
.badge.bg-white,
.badge.bg-light     { background: var(--surface-3)      !important; color: var(--text-secondary) !important; }

.badge.bg-warning.text-dark,
.badge.bg-light.text-dark,
.badge.bg-white.text-dark {
  color: inherit !important;
}

/* ── Chip radio (single-select chips) ───────────────────────────────── */
/* Modifier for a page-local .chip (see wizard/company_setup.html's own
   <style> block — .chip itself was never promoted, this only promotes
   the single-select layer built on top of it). Chips stay independent,
   ordinary flex-wrap pills — identical wrapping behavior to multi-select
   chip fields — with a leading radio-dot icon (hollow ring off, filled
   dot on) carrying the single-select meaning. Active state is a border +
   light background tint (not the plain .chip.active solid fill, and not
   fully transparent either — a bare accent-colored icon on a transparent
   chip read as too subtle / the icon looked "missing"). Selector is
   .chip.chip-radio.active (not just .chip-radio.active) specifically for
   specificity, not style: page-local .chip.active has the same 2-class
   specificity, and since .chip lives page-local (loads after theme.css)
   it would otherwise win by source order and silently override this with
   solid fill, hiding the accent-on-accent icon. Markup: <span
   class="chip chip-radio ..."><i class="bi bi-circle radio-off"></i><i
   class="bi bi-record-circle radio-on"></i> Label</span>. History: an
   earlier "joined segmented bar" version broke once a custom-entry chip
   needed to grow — see wizard/chip-style-options-mockup.html for the
   full comparison. */
.chip.chip-radio.active {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-color: var(--accent); color: var(--accent); font-weight: 600;
}
.chip-radio .radio-on { display: none; color: var(--accent); }
.chip-radio.active .radio-on { display: inline; }
.chip-radio.active .radio-off { display: none; }

/* .chip-radio-custom: a .chip-radio that also accepts a typed value (an
   "Other" option) — combine both classes: class="chip chip-radio
   chip-radio-custom". Inactive, it reads as a normal chip label; active,
   the label swaps for a real text input embedded in the same chip (not a
   separate box below the group), so a committed value sits visibly
   inside the chip the same way any other selected chip communicates
   "this is the current value." Needs this markup alongside it:
     <span class="chip-radio-custom-label">Other</span>
     <input type="text" class="chip-radio-custom-input">
     <button class="chip-radio-custom-icon-btn chip-radio-custom-clear">×</button>
     <button class="chip-radio-custom-icon-btn chip-radio-custom-confirm">
       <i class="... confirm-icon"></i><i class="... edit-icon"></i>
     </button>
   Add .is-invalid to flag an empty committed value (e.g. selected but
   never typed, or cleared) — pairs with a validation check in the
   consuming page's JS; theme.css only owns the visual state. */
.chip-radio-custom .chip-radio-custom-input { display: none; }
.chip-radio-custom.active .chip-radio-custom-label { display: none; }
.chip-radio-custom.active .chip-radio-custom-input {
  display: inline-block; background: transparent; border: none; border-bottom: 1px solid transparent;
  outline: none; color: inherit; font: inherit; width: 150px; padding: 0 0 1px;
}
/* Underline only on :focus — otherwise a value you've clicked away from
   still looks like a live, uncommitted text field instead of a settled
   selection, unlike every other chip's plain, confident-looking label. */
.chip-radio-custom.active .chip-radio-custom-input:focus { border-bottom-color: currentColor; }
.chip-radio-custom.active .chip-radio-custom-input::placeholder { color: inherit; opacity: .65; }

/* Two buttons, both visible whenever there's text, whatever the focus
   state: × clears the field; ✓ commits while typing (same as pressing
   Enter) or jumps back into editing once already committed — the
   consuming page's JS picks the right action based on current focus. */
.chip-radio-custom .chip-radio-custom-icon-btn {
  display: none; background: none; border: none; padding: 0; margin-left: 5px;
  color: inherit; opacity: .85; line-height: 1; vertical-align: middle;
}
.chip-radio-custom .chip-radio-custom-icon-btn:hover { opacity: 1; }
.chip-radio-custom.active .chip-radio-custom-input:focus:not(:placeholder-shown) ~ .chip-radio-custom-clear,
.chip-radio-custom.active .chip-radio-custom-input:not(:placeholder-shown) ~ .chip-radio-custom-confirm {
  display: inline-block;
}
/* Within the confirm/edit button: checkmark while typing (nothing to
   confirm yet is a different state), pencil once committed — a
   checkmark with nothing left to confirm reads as confusing; a pencil
   clearly says "click to edit this." */
.chip-radio-custom-confirm .edit-icon { display: none; }
.chip-radio-custom-input:not(:focus) ~ .chip-radio-custom-confirm .confirm-icon { display: none; }
.chip-radio-custom-input:not(:focus) ~ .chip-radio-custom-confirm .edit-icon { display: inline; }
/* Selected but left empty (or cleared with ×) — flag the chip itself so
   the error reads as belonging to that segment, not just a stray
   message elsewhere on the page. */
.chip-radio-custom.is-invalid { border-color: var(--danger) !important; }
.chip-radio-custom.is-invalid.active { color: var(--danger); }
.chip-radio-custom.is-invalid.active .radio-on { color: var(--danger); }

/* ── Nav tabs ────────────────────────────────────────────────────────── */
/* Underline style — same pattern account/profile.html used locally as
   .profile-settings-tabs, promoted here so every .nav-tabs gets it for free. */
.nav-tabs {
  border-bottom: 1px solid var(--border-subtle);
}
.nav-tabs .nav-link {
  color: var(--text-muted);
  font-weight: 500;
  border: none;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  background: transparent;
}
.nav-tabs .nav-link:hover {
  color: var(--text-secondary);
  border-bottom-color: var(--border-strong);
}
.nav-tabs .nav-link.active {
  color: var(--accent);
  background-color: transparent;
  border-bottom-color: var(--accent);
}

/* ── Tables ──────────────────────────────────────────────────────────── */
.table {
  --bs-table-bg:             transparent;
  --bs-table-color:          var(--text-primary);
  --bs-table-border-color:   var(--border-subtle);
  --bs-table-hover-bg:       color-mix(in srgb, var(--accent) 6%, transparent);
  --bs-table-hover-color:    var(--text-primary);
  --bs-table-striped-bg:     var(--surface-2);
  --bs-table-striped-color:  var(--text-primary);
  --bs-table-active-bg:      color-mix(in srgb, var(--accent) 10%, transparent);
}

.table thead th {
  background: var(--surface-2);
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-subtle);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}

.table > tbody > tr > td,
.table > tbody > tr > th {
  background-color: transparent;
  color: var(--text-primary);
}

/* ── Modals ──────────────────────────────────────────────────────────── */
/* Canonical modal position: every modal in this app centers vertically and
   clears the fixed navbar, with no per-template opt-in required.
   `modal-dialog-centered` is now a no-op (kept in older templates only for
   readability) -- see bugs/2026-07-23-modal-header-hidden-behind-navbar.md
   for why this used to be inconsistent (some modals centered, some
   top-aligned at a margin smaller than the navbar's own height) and
   CLAUDE.md's "Modal Markup" convention for the rule going forward. */
.modal {
  /* Backstop for modals whose content is taller than the viewport, where
     centering has no room to apply and the dialog falls back to starting
     at this margin -- must clear --navbar-height regardless of centering. */
  --bs-modal-margin: calc(var(--navbar-height) + 0.75rem);
}

/* Pages with a sticky subnav bar (e.g. view_project.html's Source
   Documents/Collections/... row) stack it directly below the navbar, so the
   backstop above needs to clear both, not just the navbar -- otherwise a
   short-viewport / tall-content modal can still open overlapping the
   subnav. Uses the same body:has(.subnav-outer) selector as the navbar's
   own subnav-aware rule elsewhere in this file. */
body:has(.subnav-outer) .modal {
  --bs-modal-margin: calc(var(--navbar-height) + var(--subnav-height) + 0.75rem);
}

/* NEW THEME.CSS ADDITION -- FLAGGING FOR SIGN-OFF (2026-07-23):
   ── Form actions ────────────────────────────────────────────────────── */
/* Canonical alignment for a page-level form's action buttons (Save/Cancel,
   Create/Back, etc.): right-aligned, matching Bootstrap's own .modal-footer
   default (justify-content: flex-end) that this app already inherits
   silently across every modal -- this just extends the same convention to
   forms outside modals instead of leaving it undecided per-template.
   Promoted from company_setup.html's page-local .panel-footer, which
   already implemented this -- .panel-footer is kept here as an alias so
   company_setup.html's existing markup and JS selectors (which query
   `.panel-footer` extensively for its per-section save/cancel state) don't
   need to change; its own local re-declaration of this rule was removed.
   Use `form-actions` on any new form's action-button wrapper:
   <div class="form-actions"><button>Cancel</button>
   <button type="submit">Save</button></div>

   NOT in scope: a full-width `btn ... w-100` submit button on a short,
   single-purpose form with no Cancel/secondary action (e.g.
   create_collection.html, edit_collection.html) is a deliberately different,
   valid pattern -- the full-width button *is* the card's primary visual
   weight on an otherwise mostly-empty form. Don't fold these into
   `.form-actions`; that's a considered decision (2026-07-23), not
   inconsistency left to clean up. */
.form-actions,
.panel-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: .6rem;
}

.modal-dialog {
  display: flex;
  align-items: center;
  min-height: calc(100% - var(--bs-modal-margin) * 2);
}

/* NEW THEME.CSS ADDITION -- FLAGGING FOR SIGN-OFF (2026-07-23):
   ── Page header ─────────────────────────────────────────────────────── */
/* Canonical page header: title centered on the page, with an optional Back
   button on the same row, left-justified -- baselined on
   tools/generate_presentation.html, intended to become the standard across
   all pages once confirmed (see AGENTS.md).

   .page-header-title must be the ONLY flow content in .page-header-row --
   an optional subtitle <p> goes outside the row, centered separately below
   it. This is deliberate, not an oversight: .page-header-back is centered
   at 50% of .page-header-row's own height, so if a subtitle shared the row
   (inside .page-header-title), the row's height would be title+subtitle
   combined and the back button would center against *that*, landing lower
   than the title's own vertical center. Keeping the row exactly as tall as
   the title is what keeps the back button aligned with it regardless of
   subtitle length or presence.
   An optional right-side action button (e.g. dashboard.html's "New
   Project") uses .page-header-action, mirroring .page-header-back on the
   opposite side -- same reasoning applies (absolutely positioned, centered
   at 50% of the row's height, independent of title/subtitle length).
   Migrated to every applicable page 2026-07-23 (see
   dev-todos/2026-07-23-page-header-standard-audit.md for the full list and
   the pages intentionally left out of scope).

   If a page has NO subtitle, add `mb-5` directly to that page's own
   .page-header-row (not baked in here) -- .page-header-subtitle carries
   the header-to-content gap via its own margin-bottom when present, but
   with no subtitle nothing does, so the row needs its own fallback margin
   or content starts flush against the title with zero gap.
   Markup:
   <div class="page-header-row">
     <a href="..." class="btn btn-outline-secondary page-header-back">
       <i class="bi bi-arrow-left"></i> Back
     </a>
     <h2 class="page-header-title text-accent">...</h2>
     <a href="..." class="btn btn-primary page-header-action">
       <i class="bi bi-folder-plus"></i> New Project
     </a>
   </div>
   <p class="page-header-subtitle text-muted">...</p> */
.page-header-row {
  position: relative;
  display: flex;
  align-items: center;
  /* Baked in here (rather than left to a per-page `mt-*` utility class or a
     wrapper div's own padding) so every page using .page-header-row lands
     its title at the same height below the navbar/subnav automatically --
     confirmed 2026-07-23 against search_results.html's h2, which the user
     preferred, landing at the same effective offset via this value. */
  margin-top: 3rem;
}

.page-header-title {
  width: 100%;
  text-align: center;
  margin: 0;
}

/* Standardized 2026-07-23: the gap between a page's header and its first
   content card/row should be 3rem everywhere -- confirmed against
   tools/document_summarizer.html, which happened to already land on 3rem
   via Bootstrap's .mb-5 on its header wrapper (an untracked coincidence,
   not a deliberate standard until now). Baked into this class so any page
   using .page-header-row gets it automatically instead of remembering to
   add `mb-5`/`mb-4`/`py-5` per page.

   margin-top is the h2-to-subtitle gap (also confirmed against
   document_summarizer.html, which lands on this via Bootstrap's .mb-2 on
   its h2). Deliberately placed here as margin-TOP on the subtitle rather
   than margin-bottom on .page-header-title: the title lives inside
   .page-header-row, whose height .page-header-back centers against --
   adding margin there would grow the row and shift the back button's
   centering off the title again (the exact bug fixed earlier the same
   day). The subtitle lives outside the row, so its own margin-top can't
   affect that math. */
.page-header-subtitle {
  text-align: center;
  margin: 0.5rem 0 3rem;
}

/* NEW THEME.CSS ADDITION -- FLAGGING FOR SIGN-OFF (2026-07-23):
   ── Joined radio-toggle button group ───────────────────────────────────── */
/* Promoted from tools/generate_presentation.html's page-local "Output
   Format" control (2026-07-23) -- real Tabler/Bootstrap radio-toggle group
   (.btn-check input + sibling <label>), not a hand-rolled div lookalike.
   Native :checked/:disabled state drives the CSS directly; clicking a
   label natively checks its radio. Outlined rather than filled to match
   Content Type / Context Source's active-state treatment: colored border +
   tint instead of a solid background. A single joined strip (shared
   borders, only the outer two corners rounded) rather than gapped boxes.

   Rounding is class-based (.group-first / .group-last), not
   :first-child/:last-child -- for a STATIC group (fixed number of options,
   never conditionally hidden), just put those two classes directly on the
   first/last <label> in the markup. For a DYNAMIC group where segments can
   be hidden per some other selection (generate_presentation.html's Output
   Format varies by Content Type), recompute which *visible* label is
   first/last in JS and toggle the classes there instead --
   display:none doesn't change structural position, so plain
   :first-child/:last-child would round the wrong corners once the actual
   first/last child isn't the first/last visible one. See
   syncOutputFormatToggle() in generate_presentation.html for the dynamic
   pattern to copy if a new dynamic use case comes up.
   Markup (static case):
   <div class="format-btn-group">
     <input type="radio" class="btn-check" name="..." id="optA" checked autocomplete="off">
     <label class="format-btn group-first" for="optA">Option A</label>
     <input type="radio" class="btn-check" name="..." id="optB" autocomplete="off">
     <label class="format-btn group-last" for="optB">Option B</label>
   </div> */
.format-btn-group {
  display: flex;
  height: 38px;
}
/* Compact variant for inline/secondary contexts (e.g. view_project.html's
   Retrieval scope row, sitting next to small helper text) -- matches
   Bootstrap's own .btn-group-sm sizing rather than the full-size default
   above, meant for a primary-decision control like Output Format. */
.format-btn-group.format-btn-group-sm {
  height: 30px;
}
.format-btn-group.format-btn-group-sm .format-btn {
  font-size: 0.8rem;
  padding: 0 10px;
  flex: 0 0 auto;
  white-space: nowrap;
}
.format-btn-group .btn-check {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.format-btn-group .format-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35em;
  border: 1px solid var(--border-strong);
  border-radius: 0;
  margin-left: -1px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-secondary, #495057);
  cursor: pointer;
  user-select: none;
  position: relative;
  transition: all 0.15s ease;
}
.format-btn-group .format-btn.group-first {
  border-radius: 8px 0 0 8px;
  margin-left: 0;
}
.format-btn-group .format-btn.group-last {
  border-radius: 0 8px 8px 0;
}
.format-btn-group .btn-check:checked + .format-btn {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  z-index: 1;
}
.format-btn-group .btn-check:disabled + .format-btn {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.page-header-back {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
}

.page-header-action {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

.btn.page-header-action:active {
  transform: translateY(-50%) scale(0.97);
}

/* .btn:active { transform: scale(0.97); } (this file, "Buttons" section)
   also targets `transform` on this same element -- only one wins, so
   without this rule, pressing the button replaces the centering translate
   with just the scale, snapping it out of vertical alignment for the
   duration of the click (and back once released). Compose both instead.
   Specificity (.btn.page-header-back:active, 3 classes) intentionally
   beats .btn:active (2 classes) regardless of source order. */
.btn.page-header-back:active {
  transform: translateY(-50%) scale(0.97);
}

.modal-backdrop {
  --bs-backdrop-bg: #020617;
  --bs-backdrop-opacity: 0.72;
}

.modal-content {
  --bs-modal-bg:                  var(--card-surface);
  --bs-modal-border-color:        var(--border-strong);
  --bs-modal-border-radius:       18px;
  --bs-modal-inner-border-radius: 17px;
  --bs-modal-box-shadow:          var(--theme-shadow-soft);
  --bs-modal-color:               var(--text-primary);
  --bs-modal-header-border-color: transparent;
  --bs-modal-footer-border-color: transparent;
  box-shadow: var(--theme-shadow-soft);
}

.modal-header {
  --modal-header-accent: var(--accent);
  padding: 20px 20px 12px;
  background: transparent !important;
  color: var(--text-primary) !important;
  border-bottom: 0;
}

.theme-modal-header {
  background: transparent;
  color: var(--text-primary);
}

.modal-title {
  color: var(--modal-header-accent);
  font-weight: 600;
}

.modal-header.bg-primary {
  --modal-header-accent: var(--accent);
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 10%, var(--card-surface)), var(--card-surface)) !important;
  border-bottom-color: color-mix(in srgb, var(--accent) 24%, var(--border-subtle));
}

.modal-header.bg-success {
  --modal-header-accent: var(--success);
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-live-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  border-bottom-color: color-mix(in srgb, var(--accent-strong) 26%, var(--border-subtle));
}

.modal-header.bg-warning {
  --modal-header-accent: var(--accent-warm);
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-warn-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  border-bottom-color: color-mix(in srgb, var(--accent-warm) 26%, var(--border-subtle));
}

.modal-header.bg-danger {
  --modal-header-accent: var(--danger);
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-error-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  border-bottom-color: color-mix(in srgb, var(--danger) 26%, var(--border-subtle));
}

.modal-header.bg-secondary {
  --modal-header-accent: var(--text-secondary);
  background: linear-gradient(180deg, color-mix(in srgb, var(--badge-muted-bg) 88%, var(--card-surface) 12%), var(--card-surface)) !important;
  border-bottom-color: color-mix(in srgb, var(--text-muted) 18%, var(--border-subtle));
}

/* Bold AI header — full purple gradient, white text */
.modal-header.bg-ai {
  background: linear-gradient(135deg, var(--ai-strong), var(--ai)) !important;
  border-bottom-color: var(--border-ai);
}

.modal-header.bg-ai .modal-title,
.modal-header.bg-ai .modal-title i,
.modal-header.bg-ai .btn-close {
  color: #fff !important;
}

.modal-header.bg-primary .modal-title,
.modal-header.bg-primary .modal-title i,
.modal-header.bg-success .modal-title,
.modal-header.bg-success .modal-title i,
.modal-header.bg-warning .modal-title,
.modal-header.bg-warning .modal-title i,
.modal-header.bg-danger .modal-title,
.modal-header.bg-danger .modal-title i,
.modal-header.bg-secondary .modal-title,
.modal-header.bg-secondary .modal-title i {
  color: var(--modal-header-accent);
}

.modal-body {
  padding: 12px 20px 20px;
}

.modal-footer {
  padding: 0 20px 20px;
  background: transparent;
  border-top: 0;
  align-items: stretch;
}
.modal-footer .btn {
  display: inline-flex;
  align-items: center;
}

.modal-header .btn-close,
.theme-modal-header .btn-close {
  filter: invert(0.7);
}


/* ── Alerts ──────────────────────────────────────────────────────────── */
.alert { --bs-alert-border-radius: 14px; }
html[data-theme="dark"] .alert .btn-close { filter: invert(1); }

/* Bootstrap's default .alert-dismissible .btn-close is top:0 with 1.25rem/1rem padding,
   sized for the default alert's padding. Our compact (py-2) alerts are shorter than that,
   so the button's padded box overflows past the bottom edge, pushing the icon off-center.
   Centering it vertically works regardless of the alert's actual height. */
.alert-dismissible .btn-close {
  top: 50%;
  transform: translateY(-50%);
}

.alert-info {
  --bs-alert-color:        var(--text-secondary);
  --bs-alert-bg:           color-mix(in srgb, var(--accent) 10%, transparent);
  --bs-alert-border-color: color-mix(in srgb, var(--accent) 24%, transparent);
}

.alert-warning {
  --bs-alert-color:        var(--accent-warm);
  --bs-alert-bg:           var(--badge-warn-bg);
  --bs-alert-border-color: color-mix(in srgb, var(--accent-warm) 24%, transparent);
}

.alert-danger {
  --bs-alert-color:        var(--danger);
  --bs-alert-bg:           var(--badge-error-bg);
  --bs-alert-border-color: color-mix(in srgb, var(--danger) 24%, transparent);
}

.alert-success {
  --bs-alert-color:        var(--success);
  --bs-alert-bg:           var(--badge-live-bg);
  --bs-alert-border-color: color-mix(in srgb, var(--accent-strong) 24%, transparent);
}

.alert-light {
  --bs-alert-color:        var(--text-secondary);
  --bs-alert-bg:           var(--surface-2);
  --bs-alert-border-color: var(--border-subtle);
}

.alert-secondary {
  --bs-alert-color:        var(--text-secondary);
  --bs-alert-bg:           var(--badge-muted-bg);
  --bs-alert-border-color: color-mix(in srgb, var(--text-muted) 24%, transparent);
}

/* ── Project tier display ────────────────────────────────────────────── */
.project-tier-display { background-color: var(--surface-2); }


/* ── Create Collection page ──────────────────────────────────────────── */
.create-collection-hero-icon {
  font-size: 4rem;
  color: var(--accent);
}

.collection-next-step-card {
  background: var(--surface-2) !important;
  border-color: var(--border-subtle) !important;
}

.collection-next-step-icon {
  font-size: 2rem;
  color: var(--accent);
}


/* ── Footer ──────────────────────────────────────────────────────────── */
.footer {
  background: var(--surface-1);
  border-top: 1px solid var(--border-subtle);
  color: var(--text-muted);
  margin-top: auto;
  flex-shrink: 0;
  padding: 1rem 0;
}

.footer-links {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  column-gap: 0.9rem;
  row-gap: 0.75rem;
}

a.footer-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  min-height: 34px;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--border-subtle);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--theme-transition), background var(--theme-transition), border-color var(--theme-transition);
}

a.footer-link:hover {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border-subtle));
}

/* ── Gradient card header ────────────────────────────────────────────── */
/* Use on .card-header when background is accent-gradient. Locks text and
   icons to white so they stay visible in both themes. */
.card-header-gradient {
  background: var(--header-gradient) !important;
  color: white !important;
  --bs-card-cap-color: white;
  --bs-heading-color: white;
}
.card-header-gradient i,
.card-header-gradient .card-title i,
.card-header-gradient h5 > i,
.card-header-gradient h6 > i,
.card-header-gradient .modal-title,
.card-header-gradient .modal-title i {
  color: white !important;
}

/* ── Surface box ─────────────────────────────────────────────────────── */
/* Subtle inset card: surface-2 background, soft border, standard radius */
.surface-box {
  background: var(--surface-2);
  border: 1px solid var(--border-subtle);
  border-radius: 12px;
  padding: 12px;
}

/* ── Stat cards (KPI) ────────────────────────────────────────────────── */
/* No border on 3 sides; thick token-colored left accent bar */
.card-stat-accent  { border: 0; border-left: 4px solid var(--accent)        !important; }
.card-stat-warm    { border: 0; border-left: 4px solid var(--accent-warm)   !important; }
.card-stat-success { border: 0; border-left: 4px solid var(--accent-strong) !important; }

/* ── Typography utilities ────────────────────────────────────────────── */
.eyebrow {
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.title-gradient {
  background: var(--title-gradient);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.text-accent {
  color: var(--accent) !important;
}

.min-vh-70 {
  min-height: 70vh;
}

.icon-display-lg {
  font-size: 4rem;
  line-height: 1;
}

.text-measure-md {
  max-width: 28rem;
}

/* ── Theme switch pill ───────────────────────────────────────────────── */
.theme-switch-frame {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 36px;
  padding: 0 6px;
  border: none;
  border-radius: 10px;
  background: transparent;
  transition: background var(--theme-transition);
}

.theme-switch-frame:hover { background: var(--surface-3); }

.theme-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: 48px;
  height: 28px;
  padding: 0;
  min-height: unset;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: color-mix(in srgb, var(--surface-3) 92%, transparent);
  box-shadow: inset 0 1px 2px #0f172a1f;
  cursor: pointer;
  flex-shrink: 0;
  transition: background var(--theme-transition), border-color var(--theme-transition);
}

.theme-switch:hover {
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border-strong));
}

.theme-switch-track {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  display: block;
}

.theme-switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 22px;
  height: 22px;
  z-index: 1;
  border-radius: 50%;
  background: var(--surface-1);
  box-shadow: 0 1px 4px #0f172a3d;
  transition: transform var(--theme-transition), background var(--theme-transition);
  pointer-events: none;
}

.theme-switch[data-theme="light"] .theme-switch-thumb { transform: translateX(20px); }

.theme-switch-icon {
  position: absolute;
  top: 50%;
  z-index: 2;
  transform: translateY(-50%);
  color: var(--text-secondary);
  font-size: 11px;
  line-height: 1;
  opacity: 0.62;
  transition: opacity var(--theme-transition), color var(--theme-transition);
  pointer-events: none;
}

.theme-switch-icon-moon { left: 7px; }
.theme-switch-icon-sun  { right: 7px; }

.theme-switch[data-theme="dark"]  .theme-switch-icon-moon,
.theme-switch[data-theme="light"] .theme-switch-icon-sun  { opacity: 1; }

.theme-switch[data-theme="dark"]  .theme-switch-icon-moon { color: #dbeafe; }
.theme-switch[data-theme="light"] .theme-switch-icon-sun  { color: color-mix(in srgb, var(--accent-warm) 76%, #ffffff); }

.theme-switch[data-theme="dark"]  .theme-switch-icon-sun,
.theme-switch[data-theme="light"] .theme-switch-icon-moon { opacity: 0.34; }

/* ── Collection Picker ───────────────────────────────────────────────── */
.collection-picker-wrap { max-width: 520px; }

.collection-picker {
  max-height: 400px;
  overflow-y: auto;
  background: color-mix(in srgb, var(--surface-2) 92%, transparent);
  border-color: var(--border-subtle) !important;
  border-radius: 8px !important;
  padding: 4px !important;
}

.wizard-collection-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.wizard-collection-sort {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.wizard-collection-sort .form-select { min-width: 170px; }

.collection-picker-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 4px;
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: 8px;
  transition: background var(--theme-transition), border-color var(--theme-transition);
}

.collection-picker-option:last-child { margin-bottom: 0; }

.collection-picker-option .form-check-input {
  flex: 0 0 auto;
  margin-top: 4px;
  margin-left: 0;
}

.collection-picker-option:hover {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface-2));
  border-color: color-mix(in srgb, var(--accent) 22%, var(--border-subtle));
}

.collection-picker-label {
  flex: 1;
  min-width: 0;
  cursor: pointer;
}

.collection-picker-heading {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 2px;
}

.collection-picker-title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}

.collection-picker-description {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.45;
}

@media (max-width: 575.98px) {
  .wizard-collection-toolbar { flex-direction: column; align-items: stretch; }
  .wizard-collection-sort { justify-content: space-between; }
}

/* ── Loading Overlay ─────────────────────────────────────────────────── */
/* Full-screen backdrop with blur. JS: add .active to show, remove to hide. */
.loading-overlay {
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background: var(--surface-overlay);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: none;
  justify-content: center;
  align-items: center;
  z-index: 9999;
  animation: loading-fade-in 0.3s ease-in-out;
}

.loading-overlay.active { display: flex; }

.loading-overlay .spinner-container {
  text-align: center;
  animation: loading-slide-up 0.4s ease-out;
}

.loading-overlay .spinner-border { border-width: 0.35em; }

.loading-overlay .loading-text {
  animation: loading-pulse 1.5s ease-in-out infinite;
}

@keyframes loading-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes loading-slide-up {
  from { transform: translateY(30px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

@keyframes loading-pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.6; }
}

/* Button loading state — add .btn-loading to show pulse + spinning icon */
.btn-loading {
  animation: btn-pulse 1.5s ease-in-out infinite;
  position: relative;
}

.btn-loading .bi {
  animation: loading-spin 1s linear infinite;
  display: inline-block;
}

@keyframes btn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--theme-ring); }
  50%       { box-shadow: 0 0 0 10px transparent; }
}

@keyframes loading-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Reusable rotation for any Bootstrap Icon glyph — same keyframe as .btn-loading above */
.spin-icon { animation: loading-spin 1s linear infinite; display: inline-block; }

/* ── Scrollbars ──────────────────────────────────────────────────────── */
::-webkit-scrollbar       { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; }

/* ── Toasts ──────────────────────────────────────────────────────────── */
/* Note: Bootstrap ships its own .toast-container (position:absolute, pointer-events:none)
   for its Toast component. Our class name collides, so pointer-events must be reasserted
   here or every click on our toasts silently falls through to whatever is underneath. */
.toast-container {
  position: fixed;
  top: 80px;
  right: 20px;
  z-index: 1080;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
/* Pages with the sticky sub-nav bar (.subnav-outer, e.g. view_project.html
   and the tools/ pages) have more vertical chrome at the top than plain
   pages — toasts need to start further down so they don't collide with
   it, the same way the navbar border-bottom is conditioned on this
   selector elsewhere in this file. */
body:has(.subnav-outer) .toast-container {
  top: 112px;
}

.toast-message {
  background: var(--card-surface);
  border-radius: 8px;
  box-shadow: var(--theme-shadow-strong);
  padding: 16px;
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 300px;
  max-width: 380px;
  animation: toast-slide-in 0.3s ease-out;
  /* THEME.CSS ADDITION: default word-wrap only breaks at spaces/hyphens -- a long unbroken
     segment (e.g. a filename with underscores instead of spaces, "SCC_Cottonwood_CMAR_SOQ.pdf")
     has no valid break point otherwise and would overflow this fixed-width box instead of
     wrapping. */
  overflow-wrap: break-word;
}

.toast-message i    { font-size: 20px; flex-shrink: 0; }
/* min-width: 0 overrides the flex-item default (auto), which otherwise locks this item's
   minimum width to its unbroken content's full intrinsic size -- a well-known flexbox
   behavior that silently defeats overflow-wrap/word-break on any descendant text no matter
   what those properties are set to, unless this is set. */
.toast-message > div { flex: 1; min-width: 0; }

.toast-message.success { border-left: 4px solid var(--accent-strong); }
.toast-message.error   { border-left: 4px solid var(--danger); }
.toast-message.info    { border-left: 4px solid var(--accent); }
.toast-message.warning { border-left: 4px solid var(--accent-warm); }

/* Persistent variant — stays until the user dismisses it, no auto-timeout */
.toast-message--persistent {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

html[data-theme="dark"] .toast-message .btn-close { filter: invert(1); }
/* Bootstrap's default X glyph reads bolder/larger than our thin bi-dash-lg
   minimize icon even at a matched box size — its stroke weight is heavier,
   so the glyph itself needs to be noticeably smaller, not just same-boxed. */
.toast-message .btn-close {
  --bs-btn-close-width: 0.5em;
  --bs-btn-close-height: 0.5em;
  width: 16px;
  height: 16px;
  padding: 0;
  background-position: center;
}
.toast-message-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
/* THEME.CSS ADDITION: Bootstrap's .spinner-border default (.75s) reads as jittery at this
   avatar size / this close to the eye -- slows the same built-in keyframe down rather than
   defining a new animation. */
.toast-message-avatar .spinner-border {
  animation-duration: 2.2s;
}

.toast-message-divider {
  border-top: 1px solid var(--border-subtle);
  margin: 0;
}

.toast-status-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.toast-status-list li {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
/* Same flex min-width:auto issue as .toast-message > div above, one level deeper -- this li's
   single child (the wrapping span around the action/filename pair) is itself a flex item and
   needs the same override for overflow-wrap to actually take effect on the filename inside it. */
.toast-status-list li > span {
  min-width: 0;
}

/* THEME.CSS ADDITION: without this, the browser can wrap at any space in the row's combined
   text run, including inside the bold action phrase itself ("Extracting" / "text..." on
   separate lines) whenever the phrase + start of a long filename doesn't fit on one line --
   which action/filename combination happens to trigger it depends on live content length, not
   anything scoped to one page, so this belongs in the shared component. Forces the whole
   bold phrase to move to the next line as one unit instead of splitting mid-phrase. */
.toast-status-list li strong {
  white-space: nowrap;
}

.toast-status-list i     { font-size: 16px; }
.toast-status-list .done { color: var(--success); }
.toast-status-list .skipped { color: var(--text-muted); }
/* THEME.CSS ADDITION (Azure #199): reuses .toast-pill-icon's dot token in this list context --
   that class's box-model rule is scoped to ".toast-pill .toast-pill-icon" (the minimized pill),
   so it draws nothing when reused bare in a status-list row without this. */
.toast-status-list .toast-pill-icon {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  margin-top: 6px; /* aligns with the bold stage text's cap-height, not the row's raw top edge */
}

/* THEME.CSS ADDITION (Azure #199): animates a trailing "..." on an in-progress stage label
   instead of it sitting static -- three dots fading in sequence, classic typing-indicator
   timing. Scoped to .stage-ellipsis span rather than a generic name to avoid colliding with
   any other ellipsis usage elsewhere in this file. */
.stage-ellipsis span {
  opacity: 0;
  animation: stage-ellipsis-dot 1.4s infinite;
}
.stage-ellipsis span:nth-child(1) { animation-delay: 0s; }
.stage-ellipsis span:nth-child(2) { animation-delay: 0.2s; }
.stage-ellipsis span:nth-child(3) { animation-delay: 0.4s; }
@keyframes stage-ellipsis-dot {
  0%, 20%  { opacity: 0; }
  50%      { opacity: 1; }
  100%     { opacity: 0; }
}

.toast-status-list a {
  color: var(--text-muted);
  text-decoration: none;
  flex-shrink: 0;
}
.toast-status-list a:hover { color: var(--accent); text-decoration: underline; }

@keyframes toast-slide-in {
  from { transform: translateX(400px); opacity: 0; }
  to   { transform: translateX(0);     opacity: 1; }
}

/* Processing-toast additions: minimize + conditional (complete-only) auto-dismiss,
   layered onto .toast-message--persistent above. See showProcessingToast() /
   completeProcessingToast() in toast-utils.js. */
.toast-message.info .toast-message-avatar    { color: var(--accent); }
.toast-message.success .toast-message-avatar { color: var(--accent-strong); }

.btn-minimize {
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: color 160ms ease, background 160ms ease;
}
.btn-minimize i { font-size: 14px; }
.btn-minimize:hover { color: var(--text-primary); }
.btn-minimize:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.toast-message .toast-timeout-rail {
  /* Specificity must beat ".toast-message > div { flex: 1 }" above, or its
     flex-basis:0 collapses this to 0 height regardless of the height set here. */
  flex: none;
  height: 3px;
  border-radius: 999px;
  background: var(--accent-strong);
  animation: toast-countdown 4s linear forwards;
  transform-origin: left;
}

@keyframes toast-countdown {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

/* Minimized pill — same materials as .toast-message, collapsed. No spinner here:
   a spinning icon at this size reads as distracting rather than informative. */
.toast-pill {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 9px;
  width: 240px;
  background: var(--card-surface);
  border: 1px solid var(--border-subtle);
  box-shadow: var(--theme-shadow-strong);
  border-radius: 999px;
  padding: 9px 14px 9px 12px;
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--text-primary);
  animation: toast-slide-in 0.24s ease-out;
}
.toast-pill:hover { border-color: var(--accent); }
.toast-pill span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.toast-pill .toast-pill-icon {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.toast-pill.is-complete i { color: var(--accent-strong); font-size: 14px; }
.toast-pill .dismiss {
  background: none;
  border: none;
  color: var(--text-muted);
  padding: 2px;
  margin-left: 2px;
  cursor: pointer;
  display: flex;
}

/* ── Sticky sub-nav bar (site-wide component) ─────────────────────────
   A horizontal, sticky, full-page-width bar docked directly below the
   navbar, for pages that want a secondary row of nav items (in-page
   section jumps, cross-page tool switching, etc.) — first used by
   view_project.html's section nav and the tools/ pages' tool switcher.
   Any page can use it by rendering the markup below (see either of those
   for a working example) with its own set of .subnav-item entries.

   Split into two layers: .subnav-outer is the full-bleed sticky
   background (standard negative-margin breakout: width:100vw plus
   margins pulling it back to the viewport edges regardless of how deep
   it's nested inside centered containers); the inner .subnav-bar is a
   plain Bootstrap .container (same class the page body uses), so items
   center to match the page's own centered content instead of hugging the
   left edge of the full-width bar.

   No border-bottom — a soft shadow into the body content instead, less
   harsh than a hard ruled line, and reads as one merged band with the
   navbar above. Any page rendering .subnav-outer automatically strips the
   navbar's own border-bottom too (see the `body:has(.subnav-outer)` rule
   below) — a page-level design decision made once, site-wide, rather than
   a local override every template with a sub-bar has to remember to add.

   `top` defaults to --navbar-height so the bar sits right below the
   navbar without overlapping it while both are shown; when the navbar
   hides (scrolling down), `top` drops to 0 to reclaim that space, keyed
   off the same .navbar-hidden class the navbar itself uses. ── */
.subnav-outer {
  position: sticky;
  top: var(--navbar-height);
  transition: top 200ms ease;
  z-index: 10;
  width: 100vw;
  /* base.html's <main class="container py-4"> puts 1.5rem of padding
     above this element even though it's the first thing in the content
     block — cancel it so the bar docks flush against the navbar. */
  margin-top: -1.5rem;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  background: var(--surface-1);
  box-shadow: 0 4px 10px -6px rgba(0,0,0,0.18);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
html.navbar-hidden .subnav-outer {
  top: 0;
}
body:has(.subnav-outer) .navbar {
  border-bottom: none;
}

.subnav-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  padding: 0.2rem 0.75rem;
  /* No horizontal scrollbar — items that don't fit move into the "more"
     overflow menu instead (see subnavOverflowMenu() in base.html). */
  overflow: hidden;
}

.subnav-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  color: var(--text-secondary);
  text-decoration: none;
  flex-shrink: 0;
  transition: background var(--theme-transition), color var(--theme-transition);
}
.subnav-back:hover {
  background: var(--surface-3);
  color: var(--text-primary);
}

.subnav-item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 28px;
  padding: 0 10px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
  font-size: 0.85rem;
  line-height: 1;
  transition: background var(--theme-transition), color var(--theme-transition);
}
.subnav-item:hover {
  background: var(--surface-3);
  color: var(--text-primary);
}
.subnav-item i {
  font-size: 1rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
}
.subnav-item.disabled {
  cursor: default;
  opacity: .5;
  pointer-events: none;
}
.subnav-item.active {
  color: var(--accent);
}

/* Overflow "more" menu — items that don't fit in the bar are moved here
   by JS rather than allowing horizontal scroll. Reuses .subnav-item for
   each entry, stacked full-width instead of inline.
   Positioned manually via JS (see subnavOverflowMenu() in base.html)
   rather than Bootstrap's Dropdown/Popper — .subnav-bar's backdrop-filter
   makes it a containing block for `position: fixed` descendants, which
   broke Popper's fixed-strategy math; a plain JS-computed position
   sidesteps that entirely. */
.subnav-more {
  position: relative;
}
.subnav-more-menu {
  position: fixed;
  list-style: none;
  margin: 0;
  min-width: 200px;
  padding: 6px;
  background: var(--surface-overlay);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: var(--theme-shadow-strong);
  z-index: 1050;
  display: none;
}
.subnav-more-menu.show {
  display: block;
}
.subnav-more-menu .subnav-item {
  display: flex;
  justify-content: flex-start;
  width: 100%;
  height: 36px;
}

