/* input(2512,1): run-time error CSS1019: Unexpected token, found '}' */
/* ── Skip Link (A11y) ── */
.skip-link {
    position: absolute;
    top: -40px;
    left: 0;
    padding: 8px 16px;
    background: #112549;
    color: #fff;
    z-index: 1100;
    text-decoration: none;
    font-weight: 600;
}
.skip-link:focus {
    top: 0;
}

/* Offset anchor jumps (e.g. the post-upload redirect to #documents / #images)
   so the attachment card clears the 56px fixed topbar instead of hiding under it. */
.attachment-anchor {
    scroll-margin-top: 72px;
}

/* ── Sidebar Navigation ── */
.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 240px;
    background: var(--ccmf-sidebar-bg, #112549);
    color: #c5cdd8;
    display: flex;
    flex-direction: column;
    z-index: 1050;
    overflow: hidden;
    overscroll-behavior: contain;
    transform: translateX(-100%);
    visibility: hidden;
}
@media (min-width: 992px) {
    .sidebar {
        transform: translateX(0);
        visibility: visible;
    }
}

/* ── July 4th GA launch celebration — patriotic sidebar background ──
   Gated on `body.launch-celebration` (admin toggle). navbanner01.svg is a bold, opaque
   distressed-flag texture (navy star field on the left, a bright white right third, red
   drips) — NOT a subtle transparent motif. To fill the whole 240px column (behind the 64px
   rail + 176px panel, full viewport height, and into the mobile drawer) we cover-fill it and
   lay a navy scrim over it: a top→bottom gradient of the sidebar navy (rgb(17,37,73)) at
   0.72→0.86 opacity. The scrim keeps the light rail/panel text (#c5cdd8) above AA contrast
   over the busy and near-white areas of the art. Higher specificity than the base .sidebar
   rule so only the image layer is overridden; the base --ccmf-sidebar-bg still backs it. */
body.launch-celebration .sidebar {
    background-image:
        linear-gradient(rgba(17, 37, 73, 0.72), rgba(17, 37, 73, 0.86)),
        url('/images/navbanner01.svg?v=3wlREdHp0_fHvjwEvCuSQhX8Urs');
    background-repeat: no-repeat;
    background-position: top center;
    background-size: cover;
}

/* ── July 4th GA launch banner (Dashboard, dismissible) ──
   Full-width image banner (topbanner01, 1600×300 16:3) shown at the top of the Dashboard
   while the launch celebration is active. Served WebP-first with a PNG fallback via <picture>.
   The circular dismiss button is overlaid top-right; on close the banner is removed and the
   dismissal is remembered in localStorage. */
.launch-banner {
    position: relative;
}
.launch-banner-img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0.6rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.launch-banner-dismiss {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease;
}
.launch-banner-dismiss:hover,
.launch-banner-dismiss:focus-visible {
    background: rgba(0, 0, 0, 0.65);
}
.launch-banner-dismiss:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}
.sidebar.sidebar-open {
    transform: translateX(0);
    visibility: visible;
}
@media (prefers-reduced-motion: no-preference) {
    .sidebar {
        transition: transform 0.25s ease, visibility 0.25s ease;
    }
}
.sidebar-header {
    display: flex;
    align-items: center;
    padding: 0.75rem;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}
.sidebar-brand {
    display: flex;
    align-items: center;
    width: 100%;
    text-decoration: none;
}
.sidebar-logo {
    width: 100%;
    height: auto;
}
.sidebar-body {
    flex: 1;
    padding: 0.75rem 0;
    overflow-y: auto;
    /* Hide scrollbar cosmetically; nav remains scrollable via wheel/touch. */
    scrollbar-width: none;
}
.sidebar-body::-webkit-scrollbar {
    display: none;
}
.sidebar-section-label {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: rgba(255,255,255,0.4);
    padding: 1rem 1rem 0.25rem 1rem;
}
.sidebar-link {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.5rem 1rem;
    color: #c5cdd8;
    text-decoration: none;
    font-size: 0.875rem;
    border-left: 3px solid transparent;
    min-height: 40px;
}
button.sidebar-link {
    background: none;
    border: none;
    border-left: 3px solid transparent;
    cursor: pointer;
    width: 100%;
    text-align: left;
}
.sidebar-link:hover,
.sidebar-link:focus-visible {
    background: rgba(255,255,255,0.08);
    color: #fff;
    text-decoration: none;
}
.sidebar-link:focus-visible {
    outline: 2px solid var(--ccmf-sidebar-accent, #5b9bd5);
    outline-offset: -2px;
}
.sidebar-link.active {
    background: rgba(255,255,255,0.12);
    color: #fff;
    border-left-color: var(--ccmf-sidebar-accent, #5b9bd5);
    font-weight: 600;
}
.sidebar-link i {
    font-size: 1.1rem;
    width: 1.25rem;
    text-align: center;
    flex-shrink: 0;
}
.sidebar-badge {
    margin-left: auto;
    font-size: 0.7rem;
}
.sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1040;
}
.sidebar-backdrop.show {
    display: block;
}

/* ── Sidebar rail ──
   Two-tier "workspace rail" (#1266, PR-B). The 240px .sidebar column is split under the
   full-width logo header into a slim 64px icon rail + a 176px contextual panel column
   (64 + 176 = 240, so the .topbar/.main-content 240px offsets are unchanged). Each rail icon
   is an area; clicking one swaps the visible panel client-side (site.js) without navigating.
   Legacy .sidebar-body / .sidebar-section-label rules above are now unused but harmless. */
.sidebar-split {
    display: flex;
    flex: 1;
    min-height: 0;
}
.sidebar-rail {
    width: 64px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid rgba(255,255,255,0.1);
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: none;
}
.sidebar-rail::-webkit-scrollbar {
    display: none;
}
.sidebar-rail-group {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    padding: 0.5rem 0;
}
.sidebar-rail-footer {
    margin-top: auto;
}
.sidebar-rail-form {
    width: 100%;
    display: flex;
    justify-content: center;
    margin: 0;
}
.sidebar-rail-btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    color: #c5cdd8;
    text-decoration: none;
    background: none;
    border: none;
    border-left: 3px solid transparent;
    cursor: pointer;
}
.sidebar-rail-btn i {
    font-size: 1.35rem;
    line-height: 1;
}
.sidebar-rail-btn:hover,
.sidebar-rail-btn:focus-visible {
    background: rgba(255,255,255,0.08);
    color: #fff;
    text-decoration: none;
}
.sidebar-rail-btn:focus-visible {
    outline: 2px solid var(--ccmf-sidebar-accent, #5b9bd5);
    outline-offset: -2px;
}
.sidebar-rail-btn.active {
    background: rgba(255,255,255,0.12);
    color: #fff;
    border-left-color: var(--ccmf-sidebar-accent, #5b9bd5);
}
/* Direct-link rail icons (Admin / Notifications / Help) mark the current page with a subtle
   dot rather than the open-panel accent, so at most one open-panel highlight shows at a time. */
.sidebar-rail-btn[aria-current="page"]::after {
    content: "";
    position: absolute;
    bottom: 5px;
    left: 50%;
    transform: translateX(-50%);
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--ccmf-sidebar-accent, #5b9bd5);
}
.sidebar-rail-btn .sidebar-badge {
    position: absolute;
    top: 3px;
    right: 3px;
    margin-left: 0;
    font-size: 0.6rem;
    padding: 0.15em 0.35em;
}
/* Emergency Spouse Mode "tell": a small, deliberately low-key dot in the sidebar footer, shown
   only to the entitled user while the mode is active. No label or tooltip — it is meant to be
   easily overlooked by anyone but the user. Colour is intentionally muted; adjust as needed. */
.spouse-mode-tell {
    display: block;
    width: 8px;
    height: 8px;
    margin: 4px auto;
    border-radius: 50%;
    background: var(--ccmf-spouse-tell, #b8860b);
    opacity: 0.75;
}
.sidebar-panels {
    flex: 1;
    min-width: 0;
    padding: 0.5rem 0;
    overflow-y: auto;
    scrollbar-width: none;
}
.sidebar-panels::-webkit-scrollbar {
    display: none;
}
.sidebar-panel {
    display: flex;
    flex-direction: column;
}
/* Guard: .sidebar-panel's display:flex would otherwise defeat the [hidden] attribute. */
.sidebar-panel[hidden] {
    display: none;
}
.sidebar-panel-title {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: rgba(255,255,255,0.4);
    padding: 0.5rem 1rem 0.35rem 1rem;
}
[data-bs-theme="dark"] .sidebar-rail {
    border-right-color: rgba(255,255,255,0.06);
}
[data-bs-theme="dark"] .sidebar-rail-btn:hover,
[data-bs-theme="dark"] .sidebar-rail-btn:focus-visible {
    background: rgba(255,255,255,0.06);
}
[data-bs-theme="dark"] .sidebar-rail-btn.active {
    background: rgba(255,255,255,0.1);
}

/* ── Top Bar ── */
.topbar {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    height: 56px;
    background: var(--bs-body-bg);
    border-bottom: 1px solid var(--bs-border-color);
    z-index: 1030;
    display: flex;
    align-items: center;
}
@media (min-width: 992px) {
    .topbar {
        left: 240px;
    }
}
.topbar-inner {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0 1rem;
    gap: 0.5rem;
}
.topbar-toggle {
    color: var(--bs-body-color);
}
.topbar-brand {
    display: flex;
    align-items: center;
}
.topbar-logo {
    height: 32px;
    width: auto;
}
.topbar-actions {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}
.topbar-action-btn {
    min-width: 36px;
    min-height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* ── Topbar Community / Follow Links ── */
/* Class hooks are named "topbar-community" (not "social") on purpose — see the note in
   _TopBar.cshtml. Keep these names neutral so "social widget" blocker filter lists don't
   cosmetically hide the nav. */
.topbar-community {
    gap: 0.1rem;
}
.topbar-community-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    color: var(--bs-secondary-color);
    font-size: 1.15rem;
    text-decoration: none;
    transition: color 0.15s ease, background-color 0.15s ease;
}
.topbar-community-link:hover,
.topbar-community-link:focus-visible {
    background-color: var(--bs-tertiary-bg);
    text-decoration: none;
}
.topbar-community-link:focus-visible {
    outline: 2px solid #005fcc;
    outline-offset: 2px;
}
.topbar-community-link--youtube:hover,
.topbar-community-link--youtube:focus-visible {
    color: #ff0000;
}
.topbar-community-link--instagram:hover,
.topbar-community-link--instagram:focus-visible {
    color: #e4405f;
}
.topbar-community-link--tiktok:hover,
.topbar-community-link--tiktok:focus-visible {
    color: #111;
}
[data-bs-theme="dark"] .topbar-community-link--tiktok:hover,
[data-bs-theme="dark"] .topbar-community-link--tiktok:focus-visible {
    color: #fff;
}
.topbar-community-link--discord:hover,
.topbar-community-link--discord:focus-visible {
    color: #5865f2;
}
.topbar-community-label {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

/* ── Main Content ──
   Simple natural flow: topbar is fixed (56px), so main-content has 56px
   of top padding to clear it. Content and footer render in normal block
   flow inside main-content, no flex/min-height gymnastics. */
.main-content {
    padding-top: 56px;
}
@media (min-width: 992px) {
    .main-content {
        margin-left: 240px;
    }
}
.main-content > main {
    padding: 1.5rem 1.5rem 4rem 1.5rem;
    max-width: 1400px;
    width: 100%;
}

/* Footer — plain block element at the end of .main-content. */
.footer {
    background-color: var(--bs-body-bg);
}

/* ── Dark mode sidebar overrides ── */
[data-bs-theme="dark"] .sidebar {
    background: var(--ccmf-sidebar-bg-dark, #0c1a32);
}
[data-bs-theme="dark"] .sidebar-link:hover,
[data-bs-theme="dark"] .sidebar-link:focus-visible {
    background: rgba(255,255,255,0.06);
}
[data-bs-theme="dark"] .sidebar-link.active {
    background: rgba(255,255,255,0.1);
}

/* Navbar logo */
.navbar-logo {
  height: 40px;
  width: auto;
}

html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #112549;
}

/* Prevent horizontal scroll from any stray overflowing element,
   applied to body (not html) so it never interferes with vertical
   scrolling / document height calculations. */
body {
  overflow-x: clip;
}

.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;
}

/* Hide spin buttons on number inputs */
.hide-spin-buttons::-webkit-outer-spin-button,
.hide-spin-buttons::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.hide-spin-buttons {
  -moz-appearance: textfield;
}

/* File Preview Modal */
.file-preview-image {
  max-width: 100%;
  max-height: 80vh;
  object-fit: contain;
}

.file-preview-iframe {
  width: 100%;
  height: 80vh;
  border: none;
}

.file-preview-link:hover img {
  opacity: 0.85;
  transition: opacity 0.15s ease-in-out;
}

/* Preserve line breaks in description / notes fields */
.preserve-whitespace {
  white-space: pre-line;
}

/* ── Admin Page ── */

:root {
  --ccmf-blue: #112549;
  --ccmf-red: #DF2023;
  /* Brand + dashboard chart palette (shared globally so the icon-pack theme block and the
     Chart.js getComputedStyle reader resolve the same tokens; previously these lived in the
     Dashboard view's inline <style>, which rendered at end-of-body and overrode the theme). */
  --ccmf-navy: #112549;
  --ccmf-light: #F7F7F7;
  --ccmf-navy-mid: #1e3d6f;
  --ccmf-navy-light: #2a5a99;
  --ccmf-red-dark: #b51a1d;
  --ccmf-red-light: #e8546b;
  /* A3.2 (#1125): darkened from #6c757d. As muted/secondary TEXT the old value only
     reached 4.45:1 on tinted card surfaces (--bs-tertiary-bg #f8f9fa / --ccmf-light
     #F7F7F7) — just under WCAG AA's 4.5:1. #565d64 clears AA with headroom on every
     light surface (~6.6:1 on white, ~6.3:1 on tinted). Only consumed as text on the
     Dashboard summary line and as a Chart.js axis/series colour, both of which read
     fine slightly darker. Dark-theme --ccmf-steel (#b0b8c4, 7.7:1) is already AA. */
  --ccmf-steel: #565d64;
  --ccmf-dark: #212529;
  --ccmf-navy-alpha: rgba(17,37,73,0.35);
  --ccmf-red-alpha: rgba(223,32,35,0.35);
  --ccmf-navy-bright: #3a70b2;
  --ccmf-red-mid: #f0888a;
  --ccmf-navy-bright-mid: #4a88c9;
  --ccmf-grid-color: rgba(0,0,0,.05);
  --ccmf-tooltip-bg: #112549;
  --ccmf-pie-border: #fff;
  /* Sidebar chrome */
  --ccmf-sidebar-bg: #112549;
  --ccmf-sidebar-bg-dark: #0c1a32;
  --ccmf-sidebar-accent: #5b9bd5;
}

.text-ccmf-blue { color: var(--ccmf-blue); }
.text-ccmf-red { color: var(--ccmf-red); }

.admin-page-title {
  font-weight: 700;
  margin-bottom: 1rem;
}

.admin-divider {
  border-color: #dee2e6;
  margin: 2rem 0;
}

.admin-section-header {
  font-size: 1.25rem;
  font-weight: 600;
  border-left: 4px solid var(--ccmf-blue);
  padding-left: 0.75rem;
  margin-bottom: 1rem;
}

/* Admin accordion */
#adminAccordion .accordion-button {
  font-size: 1.1rem;
  font-weight: 600;
  padding: 0.875rem 1.25rem;
  border-left: 4px solid var(--ccmf-blue);
}

#adminAccordion .accordion-button:not(.collapsed) {
  background-color: #f0f6ff;
  color: var(--ccmf-blue);
  box-shadow: none;
}

#adminAccordion .accordion-button:focus {
  box-shadow: 0 0 0 0.2rem rgba(0, 86, 179, 0.15);
}

#adminAccordion .accordion-item {
  border-color: #dee2e6;
}

#adminAccordion .accordion-body {
  padding: 1.25rem;
}

/* Stat cards */
.admin-stat-card {
  background: #fff;
  border: 1px solid #e9ecef;
  border-radius: 0.5rem;
  padding: 1rem;
  text-align: center;
  height: 100%;
  min-height: 7.5rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  transition: box-shadow 0.15s, border-color 0.15s;
}

.admin-stat-card--clickable {
  cursor: pointer;
}

.admin-stat-card--clickable:hover,
.admin-stat-card--clickable:focus-visible {
  border-color: var(--ccmf-blue);
  box-shadow: 0 2px 8px rgba(0, 86, 179, 0.12);
}

.admin-stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
}

.admin-stat-label {
  font-size: 0.8rem;
  color: #6c757d;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-top: 0.25rem;
}

/* ── Dashboard Overview stat cards ───────────────────────────────── */
/* Two variants: --global (community / platform-wide) and --personal (user). */
.dashboard-stat-card {
  position: relative;
  background: #fff;
  border: 1px solid #e9ecef;
  border-left: 4px solid transparent;
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  text-align: left;
  height: 100%;
  min-height: 7rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  transition: box-shadow 0.15s, border-color 0.15s, transform 0.15s;
}

.dashboard-stat-card--clickable {
  cursor: pointer;
}

.dashboard-stat-card--clickable:hover,
.dashboard-stat-card--clickable:focus-visible,
a:focus-visible .dashboard-stat-card--clickable {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
}

.dashboard-stat-label-top {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.25rem;
}

.dashboard-stat-card .dashboard-stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
}

.dashboard-stat-card .dashboard-stat-label {
  font-size: 0.85rem;
  color: #495057;
  margin-top: 0.15rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

/* Global (Community) variant — navy accent */
.dashboard-stat-card--global {
  background: #f4f7fb;
  border-left-color: var(--ccmf-navy, #1b3a6b);
}
.dashboard-stat-card--global .dashboard-stat-label-top {
  color: var(--ccmf-navy, #1b3a6b);
}
.dashboard-stat-card--global .dashboard-stat-value {
  color: var(--ccmf-navy, #1b3a6b);
}

/* Personal (Your Collection) variant — red accent */
.dashboard-stat-card--personal {
  background: #fdf5f5;
  border-left-color: var(--ccmf-red, #c8102e);
}
.dashboard-stat-card--personal .dashboard-stat-label-top {
  color: var(--ccmf-red, #c8102e);
}
.dashboard-stat-card--personal .dashboard-stat-value {
  color: var(--ccmf-red, #c8102e);
}

/* Overview section collapse animation */
.overview-body {
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s ease;
}
.overview-body.is-collapsed {
  max-height: 0 !important;
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .overview-body { transition: none; }
}

/* Featured Model image — larger centered display on the dashboard card */
.featured-model-img {
  max-width: 100%;
  width: 260px;
  height: auto;
  max-height: 260px;
  object-fit: contain;
  background: #fff;
  padding: 0.5rem;
  border: 1px solid #e9ecef;
}
[data-bs-theme="dark"] .featured-model-img {
  background: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

/* Tables */
.admin-table {
  font-size: 0.875rem;
}

.admin-table th {
  font-weight: 600;
  white-space: nowrap;
  border-bottom-width: 2px;
}

.admin-table td {
  vertical-align: middle;
}

.admin-actions {
  white-space: nowrap;
}

.admin-actions .btn {
  padding: 0.15rem 0.4rem;
  font-size: 0.75rem;
}

/* Sortable column headers */
.sortable-header {
  text-decoration: none;
  color: inherit;
  white-space: nowrap;
  cursor: pointer;
}
.sortable-header:hover {
  color: var(--ccmf-blue);
}
.sort-icon {
  font-size: 0.7rem;
  opacity: 0.6;
  margin-left: 0.2rem;
}

/* Nuclear button */
.btn-nuclear {
  background-color: var(--ccmf-red);
  color: #fff;
  border: none;
  font-weight: 600;
}

.btn-nuclear:hover {
  background-color: #a3172f;
  color: #fff;
}

.btn-nuclear:disabled {
  background-color: #6c757d;
  cursor: not-allowed;
}

/* Batch toolbar */
.admin-batch-toolbar {
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 0.375rem;
  padding: 0.5rem 1rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

/* Search dropdown */
.admin-search-dropdown {
  position: absolute;
  z-index: 1000;
  background: #fff;
  border: 1px solid #dee2e6;
  border-radius: 0.375rem;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  max-height: 200px;
  overflow-y: auto;
  width: 100%;
}

.admin-search-item {
  margin: 0;
}

.admin-search-item .btn {
  padding: 0.4rem 0.75rem;
  text-decoration: none;
  color: #212529;
}

.admin-search-item .btn:hover {
  background: #f0f6ff;
}

/* Usage detail inline panel */
.usage-detail-row td {
  background: #f8f9fa;
  border-left: 3px solid var(--ccmf-blue);
}

.usage-detail-panel h6 {
  color: var(--ccmf-blue);
}

.usage-detail-panel .table {
  font-size: 0.875rem;
}

/* Step indicator */
.admin-step-indicator {
  display: flex;
  gap: 1rem;
}

.admin-step-indicator .step {
  padding: 0.25rem 0.75rem;
  border-radius: 1rem;
  font-size: 0.85rem;
  background: #e9ecef;
  color: #6c757d;
}

.admin-step-indicator .step.active {
  background: var(--ccmf-blue);
  color: #fff;
  font-weight: 600;
}

.admin-step-indicator .step.completed {
  background: #198754;
  color: #fff;
}

/* ── Dark Mode Toggle ── */

/* Icon visibility: show moon in light mode, sun in dark mode */
.dark-mode-icon-sun { display: none; }
.dark-mode-icon-moon { display: inline-block; }

[data-bs-theme="dark"] .dark-mode-icon-sun { display: inline-block; }
[data-bs-theme="dark"] .dark-mode-icon-moon { display: none; }

/* ── Dark Mode Overrides ── */

[data-bs-theme="dark"] {
  --ccmf-blue: #6db3f8;
  --ccmf-red: #ff6b7a;
  /* Dashboard chart palette (dark). Relocated from the Dashboard view's inline <style> so the
     defaults sit after the light :root block (equal specificity → source order wins) and the
     icon-pack theme block can still override them. */
  --ccmf-navy: #6db3f8;
  --ccmf-navy-mid: #5a9de6;
  --ccmf-navy-light: #82c4ff;
  --ccmf-light: rgba(255,255,255,0.07);
  --ccmf-red-dark: #ff8f99;
  --ccmf-red-light: #ffb0b8;
  --ccmf-steel: #b0b8c4;
  --ccmf-navy-alpha: rgba(109,179,248,0.4);
  --ccmf-red-alpha: rgba(255,107,122,0.4);
  --ccmf-navy-bright: #82c4ff;
  --ccmf-red-mid: #ffb0b8;
  --ccmf-navy-bright-mid: #6db3f8;
  --ccmf-grid-color: rgba(255,255,255,.08);
  --ccmf-tooltip-bg: rgba(20,22,35,0.92);
  --ccmf-pie-border: rgba(255,255,255,.12);
  color-scheme: dark;
}

/* Navbar */
[data-bs-theme="dark"] .navbar {
  background-color: var(--bs-body-bg) !important;
  border-bottom-color: var(--bs-border-color) !important;
}

/* Focus ring */
[data-bs-theme="dark"] .btn:focus,
[data-bs-theme="dark"] .btn:active:focus,
[data-bs-theme="dark"] .btn-link.nav-link:focus,
[data-bs-theme="dark"] .form-control:focus,
[data-bs-theme="dark"] .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--bs-body-bg), 0 0 0 0.25rem var(--ccmf-blue);
}

/* Admin stat cards */
[data-bs-theme="dark"] .admin-stat-card {
  background: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .admin-stat-card--clickable:hover,
[data-bs-theme="dark"] .admin-stat-card--clickable:focus-visible {
  border-color: var(--ccmf-blue);
  box-shadow: 0 2px 8px rgba(100, 160, 255, 0.18);
}

[data-bs-theme="dark"] .admin-stat-label {
  color: var(--bs-secondary-color);
}

/* Dashboard Overview stat cards — dark */
[data-bs-theme="dark"] .dashboard-stat-card {
  background: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}
[data-bs-theme="dark"] .dashboard-stat-card .dashboard-stat-label {
  color: var(--bs-body-color);
}
[data-bs-theme="dark"] .dashboard-stat-card--global {
  background: rgba(27, 58, 107, 0.18);
}
[data-bs-theme="dark"] .dashboard-stat-card--global .dashboard-stat-label-top,
[data-bs-theme="dark"] .dashboard-stat-card--global .dashboard-stat-value {
  color: #8ab4f8;
}
[data-bs-theme="dark"] .dashboard-stat-card--personal {
  background: rgba(200, 16, 46, 0.12);
}
[data-bs-theme="dark"] .dashboard-stat-card--personal .dashboard-stat-label-top,
[data-bs-theme="dark"] .dashboard-stat-card--personal .dashboard-stat-value {
  color: #ff8896;
}

/* Achievements Earned badges — dark. Bootstrap's bg-light/text-dark utilities are fixed light
   values that don't adapt to the dark theme, so the pills render near-white on the dark card.
   Override to the same surface tokens the other dark cards use. */
[data-bs-theme="dark"] .achievement-badge {
  background-color: var(--bs-tertiary-bg) !important;
  color: var(--bs-body-color) !important;
  border-color: var(--bs-border-color) !important;
}

/* btn-outline-dark — dark. Bootstrap's outline-dark is a near-black outline that
   disappears against the dark body (e.g. ScaleModels "Convert to Set"). Remap its
   button tokens so it reads as a light outline, mirroring btn-outline-light. */
[data-bs-theme="dark"] .btn-outline-dark {
  --bs-btn-color: var(--bs-body-color);
  --bs-btn-border-color: var(--bs-border-color);
  --bs-btn-hover-bg: var(--bs-body-color);
  --bs-btn-hover-color: var(--bs-body-bg);
  --bs-btn-hover-border-color: var(--bs-body-color);
  --bs-btn-active-bg: var(--bs-body-color);
  --bs-btn-active-color: var(--bs-body-bg);
  --bs-btn-active-border-color: var(--bs-body-color);
}

/* ── $1M+ Milestone card (Community → Total Inventory Cost) ──
   Gold gradient background, pulsing glow, and corner ribbon to mark the
   community crossing $1,000,000 in total inventory cost. */
.milestone-card {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, #fff8dc 0%, #fff3b0 50%, #ffe98a 100%) !important;
  border-left-color: #b8860b !important;
  box-shadow: 0 0 0 0 rgba(184, 134, 11, 0.55);
  animation: milestone-glow 3s ease-in-out infinite;
}
.milestone-card .dashboard-stat-label-top,
.milestone-card .dashboard-stat-value,
.milestone-card .dashboard-stat-label {
  color: #5c4400 !important;
}
.milestone-ribbon {
  position: absolute;
  /* Generously oversized so any milestone label fits — from "$1M Club" up through
     four-digit-million labels like "$10.75M Club" — without truncation. */
  top: 14px;
  right: -56px;
  transform: rotate(35deg);
  background: linear-gradient(135deg, #b8860b 0%, #d4a017 50%, #f3c623 100%);
  /* Dark text on the gold gradient — WCAG AA contrast (~12:1 vs the lightest stop). */
  color: #2f2200;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  /* border-box so padding doesn't push the rendered width past min-width on narrow cards. */
  box-sizing: border-box;
  padding: 4px 64px;
  min-width: 180px;
  text-align: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  z-index: 2;
  pointer-events: none;
  white-space: nowrap;
}
.milestone-ribbon .bi {
  margin-right: 2px;
}
/* On narrow viewports each card is ~half the screen (col-6), so the ribbon needs to
   shrink to avoid clipping the stat value or overflowing the .milestone-card overflow:
   hidden frame in a way that hides the whole label. */
@media (max-width: 575.98px) {
  .milestone-ribbon {
    top: 10px;
    right: -42px;
    padding: 3px 44px;
    min-width: 130px;
    font-size: 0.65rem;
    letter-spacing: 0.02em;
  }
}
@keyframes milestone-glow {
  0%, 100% { box-shadow: 0 0 0 0 rgba(184, 134, 11, 0.45); }
  50%      { box-shadow: 0 0 18px 4px rgba(184, 134, 11, 0.55); }
}
@media (prefers-reduced-motion: reduce) {
  .milestone-card { animation: none; }
}
/* Keyboard focus indicator: stop the pulsing glow and apply a high-contrast outline
   so the milestone card still has a visible focus ring (WCAG 2.4.7). The animated
   box-shadow would otherwise mask the shared :focus-visible state defined for all
   .dashboard-stat-card--clickable elements. */
a:focus-visible .milestone-card,
.milestone-card:focus-visible {
  animation: none;
  outline: 3px solid #0d6efd;
  outline-offset: 2px;
  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.35);
}
[data-bs-theme="dark"] .milestone-card {
  background: linear-gradient(135deg, #3a2e0a 0%, #4a3a0e 50%, #5a4612 100%) !important;
  border-left-color: #f3c623 !important;
}
[data-bs-theme="dark"] .milestone-card .dashboard-stat-label-top,
[data-bs-theme="dark"] .milestone-card .dashboard-stat-value,
[data-bs-theme="dark"] .milestone-card .dashboard-stat-label {
  color: #ffe98a !important;
}
[data-bs-theme="dark"] .milestone-ribbon {
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}

/* Admin batch toolbar */
[data-bs-theme="dark"] .admin-batch-toolbar {
  background: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

/* Admin search dropdown */
[data-bs-theme="dark"] .admin-search-dropdown {
  background: var(--bs-body-bg);
  border-color: var(--bs-border-color);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

[data-bs-theme="dark"] .admin-search-item .btn {
  color: var(--bs-body-color);
}

[data-bs-theme="dark"] .admin-search-item .btn:hover {
  background: var(--bs-tertiary-bg);
}

/* Usage detail panel */
[data-bs-theme="dark"] .usage-detail-row td {
  background: var(--bs-tertiary-bg);
}

/* Admin step indicator */
[data-bs-theme="dark"] .admin-step-indicator .step {
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
}

/* Admin divider */
[data-bs-theme="dark"] .admin-divider {
  border-color: var(--bs-border-color);
}

/* Footer */
[data-bs-theme="dark"] .footer {
  border-top-color: var(--bs-border-color) !important;
}

/* File preview modal */
[data-bs-theme="dark"] .file-preview-image {
  background: var(--bs-tertiary-bg);
}

/* Nuclear button stays red in both modes */
[data-bs-theme="dark"] .btn-nuclear {
  background-color: var(--ccmf-red);
}

[data-bs-theme="dark"] .btn-nuclear:hover {
  background-color: #c9354b;
}

/* Template builder column items */
[data-bs-theme="dark"] #available-columns .border-bottom {
  border-color: var(--bs-border-color) !important;
}

[data-bs-theme="dark"] #selected-columns .list-group-item {
  background-color: var(--bs-body-bg);
  border-color: var(--bs-border-color);
  color: var(--bs-body-color);
}

/* Import review table: warning rows readable in dark mode */
[data-bs-theme="dark"] .table-warning {
  --bs-table-bg: #332701;
  --bs-table-border-color: #664e03;
  color: var(--bs-body-color);
}

/* ── Account / Profile ── */

.profile-avatar {
  border-radius: 50%;
  object-fit: cover;
}

.profile-avatar-sm {
  width: 24px;
  height: 24px;
}

.profile-avatar-lg {
  width: 100px;
  height: 100px;
}

/* ── Notification Bell ── */

.notification-badge {
  position: absolute;
  top: -2px;
  right: -4px;
  font-size: 0.65rem;
  min-width: 1.1rem;
  padding: 0.15rem 0.35rem;
  line-height: 1;
}

.notification-dropdown {
  width: 340px;
  max-width: 90vw;
}

.notification-dropdown-body {
  max-height: 320px;
  overflow-y: auto;
  /* overflow-y:auto would otherwise compute overflow-x to auto, letting a long
     thread title/snippet produce a horizontal scrollbar. Clip the x-axis so rows
     truncate cleanly within the fixed dropdown width instead of scrolling sideways. */
  overflow-x: hidden;
}

/* Lets a flex child (e.g. the message-dropdown text column) shrink below its
   content width so Bootstrap's .text-truncate can ellipsize. Mirrors the same
   utility in mobile.css, which is not loaded on the desktop topbar. */
.min-width-0 {
  min-width: 0;
}

.notification-dropdown-item {
  display: flex;
  align-items: flex-start;
  padding: 0.5rem 0.75rem;
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid var(--bs-border-color-translucent);
  gap: 0.5rem;
}

.notification-dropdown-item:last-child {
  border-bottom: none;
}

.notification-dropdown-item:hover {
  background-color: var(--bs-tertiary-bg);
}

.notification-unread {
  background-color: rgba(13, 110, 253, 0.04);
  border-left: 3px solid var(--bs-primary);
}

.notification-unread-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: var(--bs-primary);
  flex-shrink: 0;
  margin-top: 0.35rem;
}

/* Dark mode notification overrides */
[data-bs-theme="dark"] .notification-dropdown {
  background-color: var(--bs-body-bg);
  border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .notification-dropdown-item:hover {
  background-color: var(--bs-tertiary-bg);
}

[data-bs-theme="dark"] .notification-unread {
  background-color: rgba(100, 160, 255, 0.08);
}

/* ── Inquiries panel (#302) ── */
.inquiry-row.inquiry-unread {
  background-color: rgba(13, 110, 253, 0.04);
  border-left: 3px solid var(--bs-primary);
}

.inquiry-unread-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: var(--bs-primary);
  flex-shrink: 0;
  display: inline-block;
}

[data-bs-theme="dark"] .inquiry-row.inquiry-unread {
  background-color: rgba(100, 160, 255, 0.08);
}

.account-card .card-header {
  background: #f8f9fa;
  font-weight: 600;
}

[data-bs-theme="dark"] .account-card .card-header {
  background: var(--bs-tertiary-bg);
}

[data-bs-theme="dark"] .account-card {
  border-color: var(--bs-border-color);
}

/* Footer legal links */
.footer-legal-links a {
  color: #6c757d;
  text-decoration: none;
  margin-left: 1rem;
  font-size: 0.85rem;
}

.footer-legal-links a:hover {
  color: var(--ccmf-blue);
  text-decoration: underline;
}

/* ── Card Grid View ── */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 1rem;
}

.card-grid-item {
  transition: box-shadow 0.15s ease-in-out;
}

.card-grid-item:hover {
  box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.12);
}

.card-grid-item:focus-within {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

.card-grid-item .card-title a {
  text-decoration: none;
  color: inherit;
}

.card-grid-item .card-title a:hover {
  text-decoration: underline;
}

[data-bs-theme="dark"] .card-grid-item {
  border-color: var(--bs-border-color);
}

/* ── Piece Card Image ── */
.piece-card-img {
  width: 150px;
  height: 100%;
  min-height: 150px;
  object-fit: cover;
  border-radius: var(--bs-card-inner-border-radius) 0 0 var(--bs-card-inner-border-radius);
  flex-shrink: 0;
  align-self: stretch;
  background-color: var(--bs-tertiary-bg, #f8f9fa);
}

.card-grid-item-horizontal {
  /* Was a hard 150px (the image height) with overflow:hidden, which clipped the
     user-tag row (#555) whenever the title wrapped to two or three lines. Let the
     card grow to fit its content, keeping the image height as a floor. */
  min-height: 150px;
  overflow: hidden;
}

/* Make the inner flex row fill the now content-driven card height so the image
   column stretches to match instead of leaving a gap below a fixed-height image. */
.card-grid-item-horizontal > .d-flex {
  flex: 1 1 auto;
  min-height: 0;
}

.card-grid-item-horizontal > .d-flex > a:first-child {
  display: flex;
}

.piece-card-name {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
  min-height: calc(1.3em * 3);
  margin-bottom: 0.15rem;
}

@media (max-width: 575.98px) {
  .card-grid {
    grid-template-columns: 1fr;
  }
}

/* ── Card Grid Controls ── */
.card-grid-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

/* ── View Mode Toggle ── */
.view-mode-toggle .btn {
  padding: 0.25rem 0.5rem;
}

/* ── Search Input with Clear Button (#357) ── */
.ccmf-search-wrap {
  position: relative;
}

.ccmf-search-wrap .ccmf-search-input {
  /* Reserve space on the right so typed text never sits underneath the
     clear button (44px = button width incl. padding). */
  padding-right: 2.75rem;
}

.ccmf-search-clear {
  position: absolute;
  top: 50%;
  right: 0.25rem;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* WCAG 2.5.8: target size at least 24x24 CSS px. We use 32x32. */
  width: 2rem;
  height: 2rem;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--bs-secondary-color, #6c757d);
  cursor: pointer;
  line-height: 1;
}

.ccmf-search-clear:hover,
.ccmf-search-clear:focus-visible {
  color: var(--bs-body-color);
  background-color: var(--bs-secondary-bg, rgba(0, 0, 0, 0.06));
}

.ccmf-search-clear:focus-visible {
  outline: 2px solid var(--bs-primary, #0d6efd);
  outline-offset: 2px;
}

.ccmf-search-clear[hidden] {
  display: none;
}

/* ── Admin hub ──
   Admin user-picker combobox (WAI-ARIA APG combobox pattern; see _AdminUserPicker.cshtml +
   wwwroot/js/admin-user-picker.js). The listbox is absolutely positioned inside the
   position:relative .ccmf-search-wrap; z-index 1056 keeps it above a Bootstrap modal (1055)
   because the link-invite picker renders inside #linkInviteModal. */
.admin-user-picker-listbox {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1056;
  max-height: 15rem;
  overflow-y: auto;
  margin-top: 0.125rem;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.admin-user-picker-listbox[hidden] {
  display: none;
}

.admin-user-picker-option {
  cursor: pointer;
}

.admin-user-picker-option[aria-selected="true"],
.admin-user-picker-option:hover {
  background-color: var(--bs-primary-bg-subtle, #cfe2ff);
}

/* ── Sortable column headers (#1176) ──
   Clickable, accessible table column headers on /ScaleModels and /Sets. The header is a
   real <a> (server GET navigation) so it works without JS and is keyboard operable. The
   caret shape — not colour alone — conveys sort direction; a visually-hidden suffix
   announces the next action and the <th> carries aria-sort. */
.sortable-col-header {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  /* WCAG 2.5.8: keep the clickable target at least 24x24 CSS px. */
  min-height: 1.5rem;
  padding: 0.1rem 0.25rem;
  margin: -0.1rem -0.25rem;
  color: inherit;
  text-decoration: none;
  border-radius: 0.25rem;
  white-space: nowrap;
}

.sortable-col-header-end {
  justify-content: flex-end;
}

.sortable-col-header:hover,
.sortable-col-header:focus-visible {
  color: var(--bs-primary, #0d6efd);
  background-color: var(--bs-secondary-bg, rgba(0, 0, 0, 0.06));
  text-decoration: none;
}

.sortable-col-header:focus-visible {
  outline: 2px solid var(--bs-primary, #0d6efd);
  outline-offset: 2px;
}

.sortable-col-header.active {
  font-weight: 600;
}

/* Inactive caret stays faint until the column is active or hovered, so the active
   sort reads clearly while still hinting that every header is sortable. */
.sortable-col-header .sortable-col-caret.text-muted {
  opacity: 0.5;
}

.sortable-col-header:hover .sortable-col-caret,
.sortable-col-header:focus-visible .sortable-col-caret {
  opacity: 1;
}

/* ── Filter Panel ── */
.ccmf-filter-panel {
  background-color: var(--bs-light, #f8f9fa);
  border: 1px solid var(--bs-border-color);
  border-radius: 0.375rem;
  padding: 1rem;
}

[data-bs-theme="dark"] .ccmf-filter-panel {
  background-color: var(--bs-tertiary-bg);
}

/* ── Remote card-grid loading overlay (#737) ──
   Parity with Tabulator's built-in ajaxLoader for the card-grid view: a dim + spinner
   shown over the grid while a remote fetch is in flight. Toggled by card-grid.js setBusy(). */
.ccmf-grid-busy-host {
  position: relative;
}

.ccmf-grid-busy-overlay {
  position: absolute;
  inset: 0;
  min-height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: rgba(var(--bs-body-bg-rgb, 255, 255, 255), 0.6);
  border-radius: 0.375rem;
  z-index: 5;
}

/* Static busy indicator when motion is reduced (no spin). */
@media (prefers-reduced-motion: reduce) {
  .ccmf-grid-busy-overlay .spinner-border {
    animation: none;
  }
}

/* ── Active Filter Badges ── */
.ccmf-filter-badge {
  font-size: 0.8rem;
  font-weight: 500;
  padding: 0.3em 0.55em;
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
}

.ccmf-badge-close {
  font-size: 0.55em;
  padding: 0;
  opacity: 0.8;
}

.ccmf-badge-close:hover {
  opacity: 1;
}

/* ── Tabulator Cell Overflow ── */
.tabulator .tabulator-cell {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Tabulator keyboard-sortable header focus (a11y #1132) ──
   tabulator-init.js makes sortable headers focusable (tabindex=0) so they can be
   sorted from the keyboard. The header's overflow:hidden clips an outset outline, so
   draw the focus ring with an inset box-shadow (kept inside the cell) plus an inset
   outline fallback, and cover both :focus and :focus-visible (WCAG 2.4.7). */
.tabulator .tabulator-header .tabulator-col.tabulator-sortable:focus,
.tabulator .tabulator-header .tabulator-col.tabulator-sortable:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: -2px;
  box-shadow: inset 0 0 0 2px var(--bs-primary);
}

/* ── Tabulator Dark Mode Tweaks ── */
[data-bs-theme="dark"] .tabulator {
  background-color: var(--bs-body-bg);
  border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .tabulator .tabulator-header {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
  color: var(--bs-body-color);
}

[data-bs-theme="dark"] .tabulator .tabulator-header .tabulator-col {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
}

[data-bs-theme="dark"] .tabulator .tabulator-tableholder .tabulator-table .tabulator-row {
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
}

[data-bs-theme="dark"] .tabulator .tabulator-tableholder .tabulator-table .tabulator-row:nth-child(even) {
  background-color: var(--bs-tertiary-bg);
}

[data-bs-theme="dark"] .tabulator .tabulator-tableholder .tabulator-table .tabulator-row:hover {
  background-color: var(--bs-secondary-bg);
}

[data-bs-theme="dark"] .tabulator .tabulator-footer {
  background-color: var(--bs-tertiary-bg);
  border-color: var(--bs-border-color);
  color: var(--bs-body-color);
}

/* ── "Go to page" jump control (#1182) ──
   Shared compact form injected next to the pager on Tabulator footers
   (tabulator-init.js), card grids (card-grid.js) and the server-rendered
   _Pager.cshtml. Native <form>/<input type=number>/<button> so it works
   without inline JS (CSP-safe) and inherits Bootstrap theming + dark mode. */
.ccmf-goto {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin: 0;
}
.ccmf-goto[hidden] {
  display: none;
}
.ccmf-goto .ccmf-goto-label {
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
  white-space: nowrap;
}
.ccmf-goto .ccmf-goto-input {
  width: 4.75rem;
  text-align: center;
}
.ccmf-goto .ccmf-goto-btn {
  white-space: nowrap;
}

/* Inside the Tabulator footer the paginator is centered; give the jump control
   room from the First/Prev/Next/Last buttons. */
.tabulator .tabulator-paginator .ccmf-goto {
  margin-left: 0.75rem;
}

/* Card-grid pager: the jump control sits centered on its own line below the
   page buttons. */
.ccmf-goto.ccmf-goto-cards {
  display: flex;
  justify-content: center;
  margin-top: 0.5rem;
}

/* ── Wantlist ── */

/* Distinctive, clearly-clickable search affordance: a small primary pill
   button rather than a faint inline glyph, so users notice it and understand
   it triggers an external-search action (#119 feedback). */
.wantlist-search-icon {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  margin-left: 0.4rem;
  font-size: 0.9rem;
  line-height: 1;
  color: var(--bs-primary);
  background-color: var(--bs-primary-bg-subtle);
  border: 1px solid var(--bs-primary-border-subtle);
  border-radius: 50%;
  vertical-align: middle;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.wantlist-search-icon:hover,
.wantlist-search-icon:focus-visible {
  color: #fff;
  background-color: var(--bs-primary);
  border-color: var(--bs-primary);
  outline: none;
}
@media (prefers-reduced-motion: reduce) {
  .wantlist-search-icon { transition: none; }
}

/* ── External search badges (#119) ──
   Centralized look for the marketplace / catalog / auction / association
   "search this item elsewhere" badges. Markup is built by
   wwwroot/js/external-search-links.js. */
.ccmf-search-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
}
.ccmf-search-badges-label {
  color: var(--bs-secondary-color);
  margin-right: 0.25rem;
}
.ccmf-search-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  /* 24px min target (WCAG 2.5.8); roomy enough to tap on mobile. */
  min-height: 1.6rem;
  padding: 0.1rem 0.5rem;
  font-size: 0.78rem;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.85rem;
  color: var(--bs-body-color);
  background-color: var(--bs-body-bg);
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.ccmf-search-badge:hover,
.ccmf-search-badge:focus-visible {
  border-color: var(--bs-primary);
  color: var(--bs-primary);
  background-color: var(--bs-tertiary-bg);
}
.ccmf-search-badge .bi {
  font-size: 0.78rem;
}
/* Group accents so manufacturer / association badges read as distinct. */
.ccmf-search-badge-manufacturer {
  border-color: var(--bs-primary-border-subtle);
}
.ccmf-search-badge-manufacturer:hover,
.ccmf-search-badge-manufacturer:focus-visible {
  border-color: var(--bs-primary);
}
.ccmf-search-badge-association {
  border-color: var(--bs-warning-border-subtle);
}
.ccmf-search-badge-association:hover,
.ccmf-search-badge-association:focus-visible {
  border-color: var(--bs-warning);
  color: var(--bs-warning-text-emphasis);
}
.ccmf-search-badge-lock {
  font-size: 0.65rem;
  opacity: 0.7;
}
@media (prefers-reduced-motion: reduce) {
  .ccmf-search-badge {
    transition: none;
  }
}

/* ── Sticky Form Actions ──
   Action bar that sticks just below the fixed 56px topbar so Save/Cancel are
   always reachable on long edit forms without scrolling to the bottom. */
.form-actions-sticky {
  position: sticky;
  top: 56px;
  z-index: 1020;
  margin: -0.5rem -0.5rem 1rem;
  padding: 0.5rem;
  background: var(--bs-body-bg);
  border-bottom: 1px solid var(--bs-border-color);
  backdrop-filter: saturate(140%) blur(2px);
}
.form-actions-sticky-inner {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.form-actions-sticky-title {
  font-weight: 600;
  color: var(--bs-secondary-color);
  max-width: 50%;
}
.form-actions-sticky-buttons {
  display: flex;
  gap: 0.5rem;
}

/* Hide the sticky form actions bar while EasyMDE is in fullscreen mode.
   EasyMDE's fullscreen layers (.editor-toolbar.fullscreen, .CodeMirror-fullscreen,
   .editor-preview-active-side) use position:fixed with a low z-index, which would
   otherwise sit underneath our z-index:1020 sticky bar. Hiding the bar lets the
   fullscreen editor own the viewport; users exit fullscreen to save. */
body:has(.editor-toolbar.fullscreen) .form-actions-sticky,
body:has(.CodeMirror-fullscreen) .form-actions-sticky {
  display: none;
}

/* ── Show Feature ── */

/* Show card hover effect on Index listing */
@media (prefers-reduced-motion: no-preference) {
  .show-card {
    transition: transform 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  }
  .show-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.12);
  }
}

/* Show status badge pill shape */
.show-status-badge {
  border-radius: 1rem;
  font-size: 0.75rem;
  padding: 0.3em 0.7em;
  letter-spacing: 0.02em;
}

/* Countdown card */
#countdown-card {
  border-color: var(--bs-border-color);
}

/* Logo initials circle */
.show-initials-circle {
  width: 120px;
  height: 120px;
  font-size: 2.5rem;
  font-weight: 700;
  color: #fff;
}

/* Spot buttons — ensure minimum 48x48 tap target on mobile (WCAG 2.5.8) */
@media (max-width: 575.98px) {
  .spot-btn {
    min-width: 48px;
    min-height: 48px;
    padding: 0.5rem;
  }
}

/* Note template buttons in spot modal */
.note-template-btn {
  font-size: 0.8rem;
}

/* Dark mode show overrides */
[data-bs-theme="dark"] .show-card:hover {
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.3);
}

/* ──────────────────────────────────────────────────────────────────────────
   Shared dashboard primitives
   ──────────────────────────────────────────────────────────────────────────
   These card/icon/color utilities were originally scoped to /Dashboard but
   are reused by /Community (and other pages) through shared partials like
   ~/Views/Dashboard/_CommunitySection.cshtml and _ActiveContestWidget.cshtml.
   They need to be globally available so the visual style matches across
   pages. Dashboard-only layout rules (module sidebar, customize mode,
   chart palette variables) stay in Dashboard/Index.cshtml's inline style
   block.
   ────────────────────────────────────────────────────────────────────────── */

/* CCMF palette utilities */
.bg-ccmf-navy  { background-color: var(--ccmf-navy, #112549) !important; }
.bg-ccmf-red   { background-color: var(--ccmf-red,  #DF2023) !important; }
.bg-ccmf-blue  { background-color: var(--ccmf-navy-light, #2a5a99) !important; }
.bg-ccmf-light { background-color: var(--ccmf-light, #F7F7F7) !important; }
.text-ccmf-navy { color: var(--ccmf-navy, #112549); }
.text-ccmf-red  { color: var(--ccmf-red,  #DF2023); }
.text-ccmf-blue { color: var(--ccmf-navy-light, #2a5a99); }

/* Card shell used across Dashboard, Community, and other feature pages */
.dashboard-card {
    border: none;
    border-radius: 12px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .06);
}

[data-bs-theme="dark"] .dashboard-card {
    box-shadow: 0 2px 12px rgba(0, 0, 0, .3);
}

/* Round color-filled icon badge (used next to card titles) */
.dashboard-icon-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    font-size: 1rem;
    flex-shrink: 0;
}

/* Big number callout inside cards */
.dashboard-big-number {
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.1;
}

/* Row-link hover state (used inside .dashboard-card lists) */
.dashboard-row-link {
    padding: 2px 4px;
    border-radius: 6px;
    transition: background-color 0.15s;
}

.dashboard-row-link:hover {
    background-color: var(--ccmf-light, #F7F7F7);
}

.dashboard-notice-link:hover {
    color: var(--ccmf-navy, #112549) !important;
    text-decoration: underline !important;
}

/* Manufacturer detail "most-owned" top-3 card row */
.top-owned-card {
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    overflow: hidden;
}
.top-owned-card:hover,
.top-owned-card:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .1);
}
.top-owned-thumb-col {
    min-height: 120px;
}
.top-owned-thumb {
    max-height: 110px;
    object-fit: contain;
    padding: 0.5rem;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 6px;
}
@media (prefers-reduced-motion: reduce) {
    .top-owned-card { transition: none; }
    .top-owned-card:hover,
    .top-owned-card:focus-visible { transform: none; }
}

/* ── Needs Attention dashboard card (issue #765) ──
   Action tiles: each is a full-width link to a filtered /Pieces list. Meaning is
   carried by icon + count badge + text + priority order (never color alone). */
.needs-attention-tile {
    color: inherit;
    background-color: var(--bs-body-bg, #fff);
    border-color: var(--bs-border-color, #dee2e6) !important;
    min-height: 56px; /* comfortable tap target (WCAG 2.5.8) */
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.needs-attention-tile:hover,
.needs-attention-tile:focus-visible {
    background-color: var(--ccmf-light, #F7F7F7);
    border-color: var(--ccmf-red, #DF2023) !important;
    transform: translateY(-2px);
}
.needs-attention-tile .needs-attention-text { color: var(--bs-body-color, #212529); }
@media (prefers-reduced-motion: reduce) {
    .needs-attention-tile { transition: none; }
    .needs-attention-tile:hover,
    .needs-attention-tile:focus-visible { transform: none; }
}

/* ── Get Started hero card tiles (issue #951, PR-2) ──
   First-run onboarding action tiles. Each tile is a full focusable <a>; meaning is
   carried by icon + heading + supporting text (never color alone). Visible focus ring
   per WCAG 2.4.7; comfortable target size per WCAG 2.5.8. The --ccmf-navy / --ccmf-light
   custom properties already remap for dark mode, so no theme-specific overrides needed. */
.get-started-tile {
    display: flex;
    align-items: flex-start;
    color: inherit;
    text-decoration: none;
    background-color: var(--bs-body-bg, #fff);
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 10px;
    min-height: 56px;
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.get-started-tile:hover,
.get-started-tile:focus-visible {
    background-color: var(--ccmf-light, #F7F7F7);
    border-color: var(--ccmf-navy, #112549);
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .08);
}
.get-started-tile:focus-visible {
    outline: 2px solid var(--ccmf-navy, #112549);
    outline-offset: 2px;
}
.get-started-tile .get-started-tile-title { color: var(--bs-body-color, #212529); }
.get-started-tile-body { min-width: 0; }
@media (prefers-reduced-motion: reduce) {
    .get-started-tile { transition: none; }
    .get-started-tile:hover,
    .get-started-tile:focus-visible { transform: none; }
}

/* Achievement roadmap (issue #1017 / P1.3).
   Celebrates the level the user currently holds in each category (highest tier earned) while
   showing band-relative progress toward the next tier. Maxed ladders get a success accent;
   not-yet-started categories are muted with a greyed icon. Colours come from Bootstrap CSS
   variables so dark mode adapts automatically. */
.achievement-roadmap-item {
    border: 1px solid var(--bs-border-color, #dee2e6);
    border-radius: 10px;
    background-color: var(--bs-body-bg, #fff);
}
.achievement-roadmap-item.maxed {
    /* Every tier earned — celebrate with a subtle success surface + border. */
    border-color: var(--bs-success-border-subtle, #a3cfbb);
    background-color: var(--bs-success-bg-subtle, #d1e7dd);
}
.achievement-roadmap-item.unearned {
    /* Distinguish not-yet-started visually via a muted surface + greyed icon only.
       Never apply opacity/grayscale to the whole card: it would dim the
       category name and progress label below the WCAG AA 4.5:1 contrast floor. */
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}
.achievement-roadmap-item.unearned .achievement-roadmap-icon {
    opacity: 0.55;
    filter: grayscale(1);
}
.achievement-roadmap-category {
    font-size: 0.7rem;
    line-height: 1.1;
}
.achievement-roadmap-icon {
    font-size: 1.1rem;
    line-height: 1;
}
.achievement-roadmap-progress .progress {
    height: 0.4rem;
    margin-bottom: 0.15rem;
}
@media (prefers-reduced-motion: reduce) {
    .achievement-roadmap-progress .progress-bar { transition: none; }
}

/* Commemorative "gold row" badges (July 4th GA launch). A distinct GOLD surface — deliberately
   different from the green .maxed "completed" style — that pins these honorary badges to the top
   of the achievements area. Placed after .maxed/.unearned so it wins on same-specificity ties
   regardless of the earned/unearned class the badge also carries. */
.achievement-roadmap-item.commemorative {
    border-color: var(--bs-warning-border-subtle, #e0b878);
    background-color: var(--bs-warning-bg-subtle, #fff3cd);
}
.achievement-roadmap-item.commemorative.unearned {
    /* Not-yet-earned commemoratives keep the gold identity but read as muted/locked. */
    background-color: var(--bs-tertiary-bg, #f8f9fa);
    border-style: dashed;
}
.achievement-roadmap-item.commemorative.unearned .achievement-roadmap-icon {
    /* Keep the gold icon legible (don't grayscale like standard unearned badges). */
    opacity: 0.7;
    filter: none;
}
.text-ccmf-gold {
    color: #b8860b !important;
}

/* "?" help affordance next to the hardware scanner toggle (issue #951, PR-2).
   Guarantees a >=24px square target (WCAG 2.5.8) for the icon-only button. */
.scanner-help-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 31px;
    line-height: 1;
}

/* ── Recent marketplace matches (issue #766) ──
   Wantlist-matched public listings tile. Mirrors .needs-attention-tile so the two
   discovery cards read consistently, with a fixed-size thumbnail and truncating body. */
.marketplace-match-tile {
    color: inherit;
    background-color: var(--bs-body-bg, #fff);
    border-color: var(--bs-border-color, #dee2e6) !important;
    min-height: 56px; /* comfortable tap target (WCAG 2.5.8) */
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.marketplace-match-tile:hover,
.marketplace-match-tile:focus-visible {
    background-color: var(--ccmf-light, #F7F7F7);
    border-color: var(--ccmf-red, #DF2023) !important;
    transform: translateY(-2px);
}
.marketplace-match-thumb {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 0.375rem;
    overflow: hidden;
    background-color: var(--ccmf-light, #F7F7F7);
    font-size: 1.5rem;
}
.marketplace-match-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* Allow the flex body to shrink so .text-truncate works inside the tile. */
.marketplace-match-body {
    min-width: 0;
}
@media (prefers-reduced-motion: reduce) {
    .marketplace-match-tile { transition: none; }
    .marketplace-match-tile:hover,
    .marketplace-match-tile:focus-visible { transform: none; }
}

/* ── Milestones / streaks card (Issue #767) ──
   Per-track tile with achieved badge + progress bar toward the next threshold.
   Progress is conveyed as text too (".milestone-progress" sibling), not colour
   alone, for accessibility (#751). */
.milestone-track {
    background-color: var(--bs-tertiary-bg, #f8f9fa);
}
.milestone-current {
    font-variant-numeric: tabular-nums;
}
.milestone-progress {
    height: 8px;
}

/* ── Hardware Barcode Scanner (Issue #347) ──
   Sticky listening banner + non-blocking floating result toast for the desktop
   USB / Bluetooth keyboard-emulating scanner. Uses Bootstrap CSS variables so
   the colors flip cleanly under data-bs-theme="dark". */
.hw-scan-banner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1080;
    display: none;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.4rem 0.75rem;
    font-size: 0.875rem;
    color: #fff;
    background: var(--bs-success, #198754);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.hw-scan-banner.hw-scan-banner-active { display: flex; }
.hw-scan-banner .hw-scan-banner-pulse {
    width: 0.65rem;
    height: 0.65rem;
    border-radius: 50%;
    background: #fff;
    animation: hw-scan-pulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .hw-scan-banner .hw-scan-banner-pulse { animation: none; opacity: 0.85; }
}
@keyframes hw-scan-pulse {
    0%, 100% { opacity: 0.4; transform: scale(0.85); }
    50%      { opacity: 1;   transform: scale(1.1); }
}

.hw-scan-toast {
    position: fixed;
    top: 4rem;
    right: 1rem;
    z-index: 1085;
    width: 22rem;
    max-width: calc(100vw - 2rem);
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.2);
    padding: 0.75rem;
}
.hw-scan-toast .hw-scan-toast-close {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
}
.hw-scan-toast .hw-scan-toast-body { padding-right: 1.25rem; }

/* Push page content down by the banner height when scanner mode is active.
   Banner is ~32px tall; using padding-top on body keeps fixed elements like
   the topbar in their original positions but shifts the rest. */
body:has(.hw-scan-banner.hw-scan-banner-active) { padding-top: 2rem; }

/* Shared scan-result renderer styles (mobile.css has the same rules — these
   are duplicated here so the desktop hardware-scanner toast on /ScaleModels
   and /Dashboard, which doesn't load mobile.css, renders match cards
   correctly). Without these, <img> elements render at intrinsic size and
   blow out the toast. */
.scan-match-card {
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    padding: 0.75rem;
}

.scan-match-thumb {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: 0.375rem;
    flex-shrink: 0;
}

.scan-match-thumb-sm {
    width: 48px;
    height: 48px;
    object-fit: cover;
    border-radius: 0.25rem;
    flex-shrink: 0;
}

.scan-match-placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary);
    font-size: 1.5rem;
}

.scan-match-list {
    list-style: none;
    padding: 0;
    margin: 0;
    border-top: 1px solid var(--bs-border-color);
}

.scan-match-row {
    border-bottom: 1px solid var(--bs-border-color);
}

.scan-match-pick {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: 100%;
    min-height: 56px;
    padding: 0.5rem;
    background: transparent;
    border: 0;
    color: inherit;
    text-align: left;
}

.scan-match-pick:hover {
    background: var(--bs-tertiary-bg);
}

.scan-match-pick:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

/* Desktop toast is narrow (22rem); shrink the thumb a touch and allow the
   action buttons to wrap so they're never cropped under the close button. */
.hw-scan-toast .scan-match-thumb {
    width: 64px;
    height: 64px;
}

.hw-scan-toast .scan-match-card {
    border: 0;
    padding: 0;
}

.hw-scan-toast .scan-btn {
    min-height: 38px;
}


/* -- Barcode display (read-only, on /ScaleModels/Details + /Pieces/Details) -- */
.barcode-display {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
}

.barcode-display .barcode-svg {
    /* JsBarcode renders into the SVG; we only constrain the box.
       Using currentColor for the bars lets the bars adapt to dark mode. */
    max-width: 240px;
    width: 100%;
    height: auto;
    color: var(--bs-body-color);
}

.barcode-display .barcode-digits {
    font-size: 0.875rem;
    color: var(--bs-secondary-color);
    letter-spacing: 0.05em;
    user-select: all;     /* one-click select for typing into another system */
}

@media print {
    /* On a printed inventory card, force the bars black on white so cheap
       printers + cheap scanners cooperate. Hide the rest of the chrome. */
    .barcode-display .barcode-svg {
        color: #000 !important;
        max-width: 60mm;
    }

    .barcode-display .barcode-digits {
        color: #000 !important;
    }
}

/* Inventory manifest: a clean shelf-walk printout. Scoped via :has() so hiding the
   app chrome only affects the manifest page, not every print in the app. Buttons and
   other non-essential controls opt out with Bootstrap's .d-print-none. */
@media print {
    body:has(.inventory-manifest) .sidebar,
    body:has(.inventory-manifest) .topbar,
    body:has(.inventory-manifest) .sidebar-backdrop {
        display: none !important;
    }

    body:has(.inventory-manifest) .main-content {
        margin: 0 !important;
        padding: 0 !important;
    }

    .inventory-manifest .manifest-table th,
    .inventory-manifest .manifest-table td {
        border: 1px solid #000 !important;
    }
}

/* -- Inventory count-mode camera viewfinder (Issue #847, Slice B) --
   Self-contained copy of the viewfinder rules that mobile.css defines for the
   add-to-collection scanner. The main app layout does NOT load mobile.css, so
   the inventory Workspace (a desktop/tablet audit surface) needs its own copy —
   same precedent as the duplicated .scan-match-* rules above. Prefixed .inv-* so
   they never collide with the mobile .scan-* classes. */
.inv-viewfinder {
    position: relative;
    aspect-ratio: 4 / 3;
    max-height: 320px;
    background: #000;
    border-radius: 0.5rem;
    overflow: hidden;
}

.inv-viewfinder video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.inv-viewfinder[data-state="idle"] video {
    opacity: 0.3;
}

.inv-reticle {
    position: absolute;
    inset: 15% 10%;
    border: 2px solid rgba(255, 255, 255, 0.85);
    border-radius: 0.5rem;
    pointer-events: none;
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.25);
}

/* On an accepted count the reticle border flashes green. Gated behind a colour
   change only (no motion), and the transition itself is suppressed under
   prefers-reduced-motion so the cue is instantaneous rather than animated. */
.inv-viewfinder[data-state="hit"] .inv-reticle {
    border-color: var(--bs-success, #198754);
    box-shadow: 0 0 0 9999px rgba(25, 135, 84, 0.18);
}

@media (prefers-reduced-motion: no-preference) {
    .inv-reticle {
        transition: border-color 0.15s ease, box-shadow 0.15s ease;
    }
}

.inv-overlay {
    position: absolute;
    bottom: 0.5rem;
    left: 0.5rem;
    right: 0.5rem;
    color: #fff;
    font-size: 0.875rem;
    text-align: center;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
    pointer-events: none;
}

/* ── Inventory triage: mobile parity (Issue #880) ──
   The triage / attribution surfaces (scan list, attribute, adjust-set, report,
   audit index) were laid out for desktop. The scan-row action cluster now wraps
   on every viewport, and at the xs breakpoint the .btn-sm / .form-control-sm
   controls grow to a comfortable touch target. Scoped to the inventory page
   roots so nothing else in the app is affected. */
.inventory-scans .inv-action-cluster {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    justify-content: flex-end;
}

@media (max-width: 575.98px) {
    /* Comfortable tap targets. WCAG 2.5.8 asks for 24px minimum; 44px matches the
       touch guidance the mobile scanner surfaces already follow. inline-flex keeps
       the icon + label centered once the button grows past its text height. */
    .inventory-scans .btn-sm,
    .inventory-attribute-scan .btn-sm,
    .inventory-adjust-set-scan .btn-sm,
    .inventory-report .btn-sm,
    .inventory-index .btn-sm {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* The icon-only delete button needs width as well as height to be tappable. */
    .inventory-scans .inv-action-cluster .btn {
        min-width: 44px;
    }

    /* Match the set-member "not present" stepper height to its Save button so the
       pair reads as one comfortable control. */
    .inventory-adjust-set-scan .form-control-sm {
        min-height: 44px;
    }
}

/* -- Manufacturer Lookup: highlight filled fields (Issue #464) --
   Applied by the Set form (and reusable elsewhere) to inputs the lookup just
   populated. Picks a bg/border tint that works in both light and dark themes
   via Bootstrap's --bs-info-bg-subtle / --bs-info-border-subtle CSS variables.
   The .lookup-filled-flash variant adds a brief highlight pulse and is
   removed after a short JS-driven timeout, leaving .lookup-filled in place
   as a persistent reminder while the user reviews the form. */
.lookup-filled {
    border-color: var(--bs-info-border-subtle, #9eeaf9);
    background-color: var(--bs-info-bg-subtle, #cff4fc);
}
.lookup-filled:focus {
    /* Re-use Bootstrap's focus ring color so contrast stays intact when the
       user tabs into a highlighted field. */
    box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.25);
}
@media (prefers-reduced-motion: no-preference) {
    .lookup-filled-flash {
        animation: lookup-filled-pulse 1.4s ease-out;
    }
    @keyframes lookup-filled-pulse {
        0%   { background-color: rgba(13, 202, 240, 0.55); }
        100% { background-color: var(--bs-info-bg-subtle, #cff4fc); }
    }
}

/* ── Blog post body / Docs body — YouTube embeds ──
   Chrome around the <lite-youtube> facade emitted by the :::youtube ::: shortcode.
   The vendored lite-yt-embed.css only handles the play-button + 16:9 padding; the
   rounded corners, shadow, hover lift, and the 9:16 Shorts variant are ours.
   Scoped to both .blog-post-body (Blog) and .content-body (Docs) so each surface
   gets identical chrome without the rules leaking onto, say, an admin preview
   pane on an unrelated screen. */

/* Reading-column cap. The surrounding `.main-content > main` is 1400px wide, so
   without this rule a blog article + a `.blog-img-medium` (640px) float-left image
   stretches across an uncomfortably wide column on desktop and the image visually
   "escapes" the text block. 720px matches the `sizes="(max-width: 720px) 100vw, 720px"`
   hint that ResponsiveImageRewriter emits — adjust both together if the cap moves.

   The cap is applied to the `.blog-post` <article> (not the inner `.blog-post-body`)
   so the article's <header> (h1 + byline) shares the same left edge as the body
   prose. Capping the inner div alone would center the body inside a full-width
   article and visually offset the heading from the text. We deliberately do NOT
   set `margin: auto` here — the column is left-aligned within the main content
   area so a `.blog-img-align-left` image actually appears at the LEFT edge of the
   reading column, matching the author's explicit alignment choice.

   `.markdown-editor__preview` gets the same cap so authors see the production
   layout while drafting. It has no separate header sibling, so the cap can sit
   directly on the preview pane.

   `display: flow-root` on `.blog-post-body` and `.markdown-editor__preview` is
   the float containment guarantee. Without it, the `.blog-img-align-left` /
   `-right` floats escape the article body and the page footer ends up flowing
   around them at the side of the article. The per-paragraph `clear: both` rule
   below handles intra-paragraph float ending, but the body-level flow-root is
   the safety net that keeps the float contained no matter how Markdig or
   ResponsiveImageRewriter wraps the image (bare <img>, <p><img></p>, or
   <p><picture><img></picture></p>). */
.blog-post,
.markdown-editor__preview {
    max-width: 720px;
}
.blog-post-body,
.markdown-editor__preview {
    display: flow-root;
}

.blog-post-body lite-youtube,
.content-body lite-youtube {
    border-radius: 0.5rem;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    margin: 1.5rem auto;
    max-width: 720px;
    /* Clip the poster image to the rounded corners. */
    overflow: hidden;
}

/* 9:16 vertical for YouTube Shorts (opt-in via `:::youtube ID short:::`). The
   override on ::after { padding-bottom: 0 } is required because the vendored CSS
   uses the padding-bottom hack for 16:9; aspect-ratio doesn't take effect until
   we cancel that padding. */
.blog-post-body lite-youtube.lyt-short,
.content-body lite-youtube.lyt-short {
    aspect-ratio: 9 / 16;
    max-width: 360px;
}
.blog-post-body lite-youtube.lyt-short::after,
.content-body lite-youtube.lyt-short::after {
    padding-bottom: 0;
}

/* Hover lift — purely decorative, gated behind prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
    .blog-post-body lite-youtube,
    .content-body lite-youtube {
        transition: transform 0.2s ease, box-shadow 0.2s ease;
    }
    .blog-post-body lite-youtube:hover,
    .content-body lite-youtube:hover {
        transform: translateY(-2px);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
    }
}

/* Deeper shadow on dark backgrounds — the 0.15 alpha disappears against #1a1a1a. */
[data-bs-theme="dark"] .blog-post-body lite-youtube,
[data-bs-theme="dark"] .content-body lite-youtube {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.45);
}
[data-bs-theme="dark"] .blog-post-body lite-youtube:hover,
[data-bs-theme="dark"] .content-body lite-youtube:hover {
    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.6);
}

/* ── Blog post body — inline images ──
   Defence-in-depth cap so a single oversized image never dominates the page even
   if the upload pipeline (ImageProcessingService + GenerateWebpDerivativesAsync)
   is bypassed for some reason. The 70vh ceiling keeps portrait phone photos from
   becoming a vertical wall; the 640px ceiling stops landscape photos from pushing
   the page below the fold. object-fit: contain preserves aspect ratio inside the
   ceiling so we never crop. The same rules apply to .markdown-editor__preview
   inside the admin editor so authors see the production layout while drafting. */
.blog-post-body img,
.content-body img,
.markdown-editor__preview img {
    display: block;
    max-width: 100%;
    width: auto;
    height: auto;
    max-height: min(70vh, 640px);
    object-fit: contain;
    margin: 1.5rem auto;
    border-radius: 0.375rem;
    /* picture > source srcset only applies when the wrapper is the <picture>; the
       inner <img> still gets these constraints. */
}

/* <picture> wrapper — needed because the rewriter emits <picture><source/><img/></picture>;
   without display:block the wrapper would be a 1px-line inline anchor. */
.blog-post-body picture,
.content-body picture,
.markdown-editor__preview picture {
    display: block;
    max-width: 100%;
}

/* ── Blog post — hero banner (16:3) ──
   The hero is intentionally a wide masthead rather than a hero photo, so it sits
   across the top of the post without pushing the title and body below the fold on
   a 1080p viewport. The aspect-ratio reserves the layout slot at parse time so
   the page doesn't shift when the image loads (CLS protection). object-fit: cover
   shields against legacy 16:9 heroes from before the ratio change — they get
   trimmed top/bottom rather than letterboxed. */
.blog-post-hero-banner {
    width: 100%;
    aspect-ratio: 16 / 3;
    object-fit: cover;
}

/* ── Blog post body — inline image size + border overrides ──
   Authors pick Small / Medium / Large + optional border in the upload modal on
   /AdminBlog/Edit (#blog-improvements). The picker writes a Markdig generic
   attribute on the image (e.g. `![alt](url){.blog-img-medium .blog-img-bordered}`)
   which Markdig converts to a real `class=` attribute on the rendered <img>.
   These class values are explicitly whitelisted in MarkdownPipelineFactory's
   HtmlSanitizer (AllowedClasses) — adding a new size or border variant requires
   updating BOTH this CSS rule AND the sanitizer allowlist. The 70vh / 640px ceiling
   from the base .blog-post-body img rule still applies; these classes only lower the
   max-width further (Small / Medium) and override no other axis. */
.blog-post-body img.blog-img-small,
.markdown-editor__preview img.blog-img-small {
    max-width: 320px;
}
.blog-post-body img.blog-img-medium,
.markdown-editor__preview img.blog-img-medium {
    max-width: 640px;
}
.blog-post-body img.blog-img-large,
.markdown-editor__preview img.blog-img-large {
    max-width: 100%;
}
.blog-post-body img.blog-img-bordered,
.markdown-editor__preview img.blog-img-bordered {
    border: 1px solid var(--bs-border-color);
    padding: 0.25rem;
    background: var(--bs-tertiary-bg);
}

/* ── Blog post body — inline image alignment ──
   Authors pick Position: Left / Center / Right in the upload modal alongside the
   size + border choice. Left and Right float the image so prose wraps around it;
   Center sits the image block-level with auto margins. These classes are paired
   with a size class (`.blog-img-small` / `.blog-img-medium`) — at "Large" width
   floating doesn't visually do anything because the image already spans the
   content column, so the modal hides the Position group when Large is selected.

   Responsive escape hatch: below 768px (Bootstrap `md` breakpoint) we drop the
   float and revert to block layout. On a phone, a 320px floated image next to
   100vw prose would crush the text column to nothing — block + center keeps the
   article readable. The desktop float is intentionally only an enhancement.

   These class values are explicitly whitelisted in MarkdownPipelineFactory's
   HtmlSanitizer (AllowedClasses) — adding a new alignment variant requires
   updating BOTH this CSS rule AND the sanitizer allowlist. */
.blog-post-body img.blog-img-align-left,
.markdown-editor__preview img.blog-img-align-left {
    float: left;
    margin: 0.25rem 1rem 0.5rem 0;
}
.blog-post-body img.blog-img-align-right,
.markdown-editor__preview img.blog-img-align-right {
    float: right;
    margin: 0.25rem 0 0.5rem 1rem;
}
.blog-post-body img.blog-img-align-center,
.markdown-editor__preview img.blog-img-align-center {
    display: block;
    margin-left: auto;
    margin-right: auto;
}
/* Float-clearing helper. Markdig wraps every standalone image in its own <p>, so
   the next paragraph begins a fresh block formatting context — usually enough to
   "end" the float on its own. This rule is a safety net for the case where two
   floated images appear back-to-back, or where the prose between them is shorter
   than the image height.

   IMPORTANT: ResponsiveImageRewriter wraps the bare <img> in <picture>, so on the
   server-rendered side the actual structure is <p><picture><img …></picture></p>.
   The selectors below match BOTH shapes: the raw <p>><img> case (the live preview
   pane during typing, before the server has rendered) AND the <p>><picture>><img>
   case (server-rendered HTML on the published page and the post preview). Updating
   one without the other breaks float clearing in exactly one of those two surfaces. */
.blog-post-body p:has(> img.blog-img-align-left)::after,
.blog-post-body p:has(> img.blog-img-align-right)::after,
.blog-post-body p:has(> picture > img.blog-img-align-left)::after,
.blog-post-body p:has(> picture > img.blog-img-align-right)::after,
.markdown-editor__preview p:has(> img.blog-img-align-left)::after,
.markdown-editor__preview p:has(> img.blog-img-align-right)::after,
.markdown-editor__preview p:has(> picture > img.blog-img-align-left)::after,
.markdown-editor__preview p:has(> picture > img.blog-img-align-right)::after {
    content: "";
    display: block;
    clear: both;
}
@media (max-width: 767.98px) {
    .blog-post-body img.blog-img-align-left,
    .blog-post-body img.blog-img-align-right,
    .markdown-editor__preview img.blog-img-align-left,
    .markdown-editor__preview img.blog-img-align-right {
        float: none;
        display: block;
        margin-left: auto;
        margin-right: auto;
    }
}

/* Paragraph-alignment wrapper rules. Emitted by AlignContainerExtension when an
   author writes `:::align left|center|right` … `:::` in Markdown. The class
   names align-left / align-center / align-right are explicitly whitelisted in
   MarkdownPipelineFactory's HtmlSanitizer (AllowedClasses) — adding a new
   variant requires updating BOTH the regex / sanitizer pair AND this CSS.

   Scoped to the public reading surfaces (`.blog-post-body`, `.doc-body`) and to
   the editor preview pane (`.markdown-editor__preview`) so the AdminBlog /
   AdminDocs / AdminAlbums Markdown previews show the same alignment the
   published page will, without risking a class collision in unrelated UI. */
.blog-post-body .align-left,
.doc-body .align-left,
.album-description .align-left,
.markdown-editor__preview .align-left {
    text-align: left;
}
.blog-post-body .align-center,
.doc-body .align-center,
.album-description .align-center,
.markdown-editor__preview .align-center {
    text-align: center;
}
.blog-post-body .align-right,
.doc-body .align-right,
.album-description .align-right,
.markdown-editor__preview .align-right {
    text-align: right;
}

/* -- Albums (PhotoSwipe lightbox) -- */
/* Hover affordance so users see thumbnails are clickable. Transform-only so it stays cheap
   on the compositor. Gated behind prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
    .album-item a {
        transition: transform 0.15s ease, box-shadow 0.15s ease;
    }
    .album-item a:hover,
    .album-item a:focus-visible {
        transform: scale(1.02);
        box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
    }
}
.album-item a:focus-visible {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: 2px;
    border-radius: 0.375rem;
}

/* PhotoSwipe caption block rendered by lightbox.js's custom-caption registration. */
.pswp__custom-caption {
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 0.95rem;
    line-height: 1.4;
    padding: 0.5rem 1rem;
    position: absolute;
    left: 50%;
    bottom: 1rem;
    transform: translateX(-50%);
    max-width: min(90%, 60ch);
    border-radius: 0.375rem;
    text-align: center;
}
.pswp__custom-caption[hidden] {
    display: none;
}

/* ── User Tags (#555) ──
   A self-contained 8-hue palette, intentionally decoupled from the Bootstrap theme and the
   icon pack so tag colors stay stable regardless of the active theme. Each hue exposes its
   light-mode values; the [data-bs-theme=dark] block below remaps them for dark mode. Surfaces
   that show tags (form picker chips, grid pills, filter chips, quick-apply swatches) all reuse
   the same `.ccmf-tag` base + `.ccmf-tag-{hue}` color class. */
.ccmf-tag {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.125rem 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.3;
    font-weight: 500;
    border-radius: 1rem;
    border: 1px solid var(--ccmf-tag-border, #cbd5e1);
    background-color: var(--ccmf-tag-bg, #f1f5f9);
    color: var(--ccmf-tag-fg, #334155);
    white-space: nowrap;
    max-width: 100%;
}

.ccmf-tag-name {
    overflow: hidden;
    text-overflow: ellipsis;
}

.ccmf-tag-remove {
    appearance: none;
    border: 0;
    background: transparent;
    color: inherit;
    opacity: 0.7;
    font-size: 1rem;
    line-height: 1;
    padding: 0 0.1rem;
    cursor: pointer;
    border-radius: 50%;
}

.ccmf-tag-remove:hover,
.ccmf-tag-remove:focus-visible {
    opacity: 1;
}

/* Small color dot used in the picker dropdown and quick-apply menu. */
.ccmf-tag-swatch {
    display: inline-block;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
    flex: 0 0 auto;
    border: 1px solid var(--ccmf-tag-border, #cbd5e1);
    background-color: var(--ccmf-tag-bg, #f1f5f9);
}

/* Light-mode hue definitions. */
.ccmf-tag-slate  { --ccmf-tag-bg: #f1f5f9; --ccmf-tag-fg: #334155; --ccmf-tag-border: #cbd5e1; }
.ccmf-tag-rose   { --ccmf-tag-bg: #ffe4e6; --ccmf-tag-fg: #9f1239; --ccmf-tag-border: #fda4af; }
.ccmf-tag-amber  { --ccmf-tag-bg: #fef3c7; --ccmf-tag-fg: #92400e; --ccmf-tag-border: #fcd34d; }
.ccmf-tag-teal   { --ccmf-tag-bg: #ccfbf1; --ccmf-tag-fg: #115e59; --ccmf-tag-border: #5eead4; }
.ccmf-tag-violet { --ccmf-tag-bg: #ede9fe; --ccmf-tag-fg: #5b21b6; --ccmf-tag-border: #c4b5fd; }
.ccmf-tag-green  { --ccmf-tag-bg: #dcfce7; --ccmf-tag-fg: #166534; --ccmf-tag-border: #86efac; }
.ccmf-tag-blue   { --ccmf-tag-bg: #dbeafe; --ccmf-tag-fg: #1e40af; --ccmf-tag-border: #93c5fd; }
.ccmf-tag-gray   { --ccmf-tag-bg: #e5e7eb; --ccmf-tag-fg: #374151; --ccmf-tag-border: #d1d5db; }

/* Dark-mode hue remaps — muted fills with bright text for contrast on dark surfaces. */
[data-bs-theme="dark"] .ccmf-tag-slate  { --ccmf-tag-bg: #334155; --ccmf-tag-fg: #e2e8f0; --ccmf-tag-border: #475569; }
[data-bs-theme="dark"] .ccmf-tag-rose   { --ccmf-tag-bg: #881337; --ccmf-tag-fg: #fecdd3; --ccmf-tag-border: #be123c; }
[data-bs-theme="dark"] .ccmf-tag-amber  { --ccmf-tag-bg: #78350f; --ccmf-tag-fg: #fde68a; --ccmf-tag-border: #b45309; }
[data-bs-theme="dark"] .ccmf-tag-teal   { --ccmf-tag-bg: #134e4a; --ccmf-tag-fg: #99f6e4; --ccmf-tag-border: #0f766e; }
[data-bs-theme="dark"] .ccmf-tag-violet { --ccmf-tag-bg: #4c1d95; --ccmf-tag-fg: #ddd6fe; --ccmf-tag-border: #6d28d9; }
[data-bs-theme="dark"] .ccmf-tag-green  { --ccmf-tag-bg: #14532d; --ccmf-tag-fg: #bbf7d0; --ccmf-tag-border: #15803d; }
[data-bs-theme="dark"] .ccmf-tag-blue   { --ccmf-tag-bg: #1e3a8a; --ccmf-tag-fg: #bfdbfe; --ccmf-tag-border: #1d4ed8; }
[data-bs-theme="dark"] .ccmf-tag-gray   { --ccmf-tag-bg: #374151; --ccmf-tag-fg: #e5e7eb; --ccmf-tag-border: #4b5563; }

/* Tag filter chips on /Pieces: a toggle-button variant of the pill. */
.ccmf-tag-filter {
    cursor: pointer;
    user-select: none;
    opacity: 0.65;
    transition: opacity 0.12s ease, box-shadow 0.12s ease;
}

@media (prefers-reduced-motion: reduce) {
    .ccmf-tag-filter { transition: none; }
}

.ccmf-tag-filter:hover {
    opacity: 0.9;
}

.ccmf-tag-filter[aria-pressed="true"] {
    opacity: 1;
    box-shadow: 0 0 0 2px var(--ccmf-tag-fg, #334155) inset;
}

/* Compact pill variant used on card-grid items where vertical space is tight. */
.ccmf-tag-sm {
    padding: 0.05rem 0.4rem;
    font-size: 0.72rem;
    line-height: 1.25;
}

/* Quick-apply "+" affordance on grid rows / cards and its checkbox menu. */
.ccmf-tag-add-btn {
    --bs-btn-padding-y: 0.05rem;
    --bs-btn-padding-x: 0.3rem;
    --bs-btn-font-size: 0.72rem;
    line-height: 1;
}

.ccmf-tag-add-btn .bi-plus {
    margin-left: -0.15rem;
}

.ccmf-tag-menu .dropdown-item:active,
.ccmf-tag-menu .dropdown-item:focus {
    background-color: var(--bs-tertiary-bg);
    color: inherit;
}

/* The quick-apply menu is portaled to <body> and fixed-positioned (#555 Slice 2b) so it escapes the
   Tabulator cell/tableholder `overflow:hidden` clipping. JS sets its top/left; these rules size it and
   lift it above sticky chrome. It keeps Bootstrap's .dropdown-menu base styling (bg/border/shadow). */
.ccmf-tag-menu {
    position: fixed;
    z-index: 1080;
    min-width: 14rem;
    max-height: 16rem;
    overflow-y: auto;
    margin: 0;
}

/* Inline "create a new tag" row pinned under the checkbox list. */
.ccmf-tag-create {
    border-top: 1px solid var(--bs-border-color);
}

.ccmf-tag-create .form-control {
    min-width: 0;
}

/* Keep the bulk-selection checkbox column visually quiet. */
.ccmf-select-col {
    color: var(--bs-secondary-color);
}

/* ── Breadcrumbs (#564) ──
   Single source of truth for the site-wide breadcrumb trail rendered by
   Views/Shared/_Breadcrumbs.cshtml. Bootstrap 5.3 already adapts the divider
   and active-segment colours via data-bs-theme, so the work here is the
   responsive truncation the acceptance criteria call for: keep the trail on
   one line and ellipsize long segments rather than wrapping. Full labels stay
   in the DOM (title="…") so nothing is lost to assistive tech. */
.ccmf-breadcrumb-nav .breadcrumb {
    flex-wrap: nowrap;
    margin-bottom: 0;
}
.ccmf-breadcrumb-nav .breadcrumb-item {
    min-width: 0; /* let flex items shrink so ellipsis can engage */
}
.ccmf-breadcrumb-nav .breadcrumb-item > a,
.ccmf-breadcrumb-nav .breadcrumb-item.active {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}
@media (max-width: 575.98px) {
    /* On phones, cap the intermediate (non-first, non-current) segments hard so
       the root and the current page stay readable; the current page gets a
       larger share. */
    .ccmf-breadcrumb-nav .breadcrumb-item:not(:first-child):not(.active) > a {
        max-width: 6rem;
    }
    .ccmf-breadcrumb-nav .breadcrumb-item.active {
        max-width: 12rem;
    }
}

/* ── FAQ help-centre layout (Docs/Section.cshtml, FAQ section) ─────────────────
   Notion-style: a centred hero search, a responsive grid of topic cards (the
   default view), and a single-topic detail view revealed on demand. Visibility is
   JS-driven via the `.faq--js` class added to [data-faq-root] on load; without JS
   the grid and the full topic list both render (progressive enhancement), so every
   answer stays reachable. */

.faq-hero-title {
    font-weight: 700;
}

.faq-hero-search {
    position: relative;
    max-width: 36rem;
}

.faq-hero-search .faq-hero-search-icon {
    position: absolute;
    left: 0.95rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--bs-secondary-color);
    pointer-events: none;
    z-index: 2;
}

.faq-hero-search .ccmf-search-input {
    padding-left: 2.5rem;
    border-radius: 2rem;
}

/* Topic card grid. auto-fill + minmax yields 1→N columns with no media queries;
   min(100%, 17rem) lets a card collapse to the full width at 320px. The
   :not([hidden]) guard lets the JS `hidden` property (grid/search/topic modes)
   win over this display:grid rule. */
.faq-grid:not([hidden]) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
    gap: 1rem;
    margin-top: 0.5rem;
}

.faq-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 1.1rem 1.15rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.75rem;
    background: var(--bs-body-bg);
    cursor: pointer;
}

.faq-card-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.65rem;
}

.faq-card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 0.6rem;
    font-size: 1.1rem;
    background: var(--bs-tertiary-bg);
    color: var(--bs-emphasis-color);
}

.faq-card-title {
    flex: 1 1 auto;
    min-width: 0;
}

.faq-card-title a {
    color: var(--bs-emphasis-color);
    text-decoration: none;
}

.faq-card-count {
    flex: 0 0 auto;
    background: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}

.faq-card-links {
    flex: 1 1 auto;
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
}

.faq-card-links li {
    margin-bottom: 0.3rem;
    font-size: 0.92rem;
    line-height: 1.35;
}

.faq-card-links a {
    color: var(--bs-secondary-color);
    text-decoration: none;
}

.faq-card-links a:hover {
    color: var(--bs-link-hover-color);
    text-decoration: underline;
}

.faq-card-all {
    margin-top: auto;
    font-size: 0.9rem;
    font-weight: 600;
    text-decoration: none;
}

.faq-card-all .bi {
    transition: transform 0.15s ease;
}

.faq-card:hover .faq-card-all .bi {
    transform: translateX(3px);
}

/* Detail view */
.faq-detail-bar {
    margin-bottom: 1.25rem;
}

.faq-back {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.85rem;
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    font-size: 0.95rem;
    color: var(--bs-link-color);
    text-decoration: none;
    cursor: pointer;
}

.faq-detail-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.faq-chip {
    border: 1px solid var(--bs-border-color);
    background: var(--bs-body-bg);
    color: var(--bs-secondary-color);
    font-weight: 500;
    text-decoration: none;
}

.faq-chip .faq-chip-count {
    margin-left: 0.25rem;
    opacity: 0.6;
}

.faq-chip:hover {
    border-color: var(--bs-primary);
    color: var(--bs-primary);
}

.faq-chip.is-active {
    background: var(--bs-primary);
    border-color: var(--bs-primary);
    color: #fff;
}

.faq-chip.is-active:hover {
    color: #fff;
}

.faq-chip.is-active .faq-chip-count {
    opacity: 0.85;
}

.faq-topic-group {
    margin-bottom: 2rem;
}

.faq-topic-heading {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    outline: none;
}

/* Programmatic focus lands here on topic drill-in; show a visible ring only
   when the topic was opened via keyboard so mouse clicks stay clean. */
.faq-topic-heading:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 4px;
    border-radius: 0.25rem;
}

.faq-topic-heading-icon {
    color: var(--bs-secondary-color);
}

.faq-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.faq-item {
    border: 1px solid var(--bs-border-color);
    border-radius: 0.6rem;
    background: var(--bs-body-bg);
    overflow: hidden;
}

.faq-item-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.85rem 1rem;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.faq-item-summary::-webkit-details-marker {
    display: none;
}

.faq-item-chevron {
    flex: 0 0 auto;
    color: var(--bs-secondary-color);
    transition: transform 0.2s ease;
}

.faq-item[open] .faq-item-chevron {
    transform: rotate(180deg);
}

.faq-item-body {
    padding: 0 1rem 1rem;
}

/* Hover lift — only when the user hasn't asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
    .faq-card {
        transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
    }

    .faq-card:hover {
        transform: translateY(-2px);
        border-color: var(--bs-primary);
        box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
    }
}

@media (prefers-reduced-motion: reduce) {
    .faq-item-chevron,
    .faq-card-all .bi {
        transition: none;
    }
}

.faq-card:focus-within {
    border-color: var(--bs-primary);
}

/* Dark theme — deeper hover shadow against the darker surface. */
[data-bs-theme="dark"] .faq-card:hover {
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
}

/* ── Cookie Consent Banner (#944 L2.1) ── */
.ccmf-cookie-consent {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030; /* Bootstrap "fixed" level: above page chrome, below modals/offcanvas so an open modal sits on top */
    background: var(--bs-body-bg, #fff);
    border-top: 1px solid var(--bs-border-color, #dee2e6);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.12);
    padding: 0.875rem 1rem;
}

.ccmf-cookie-consent[hidden] {
    display: none;
}

.ccmf-cookie-consent__inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
}

.ccmf-cookie-consent__body {
    flex: 1 1 320px;
    min-width: 0;
}

.ccmf-cookie-consent__text {
    font-size: 0.9rem;
    color: var(--bs-secondary-color, #6c757d);
}

.ccmf-cookie-consent__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.ccmf-cookie-consent__btn {
    min-height: 2.5rem; /* >=24px target; comfortable tap size */
}

@media (max-width: 575.98px) {
    .ccmf-cookie-consent__actions {
        width: 100%;
    }
    .ccmf-cookie-consent__btn {
        flex: 1 1 auto;
    }
}

/* Footer "Cookie preferences" trigger styled as a link to match its siblings. */
.ccmf-cookie-prefs-link {
    background: none;
    border: 0;
    padding: 0;
    color: #6c757d;
    text-decoration: none;
    font-size: 0.85rem;
    cursor: pointer;
}

.ccmf-cookie-prefs-link:hover,
.ccmf-cookie-prefs-link:focus-visible {
    color: var(--ccmf-blue);
    text-decoration: underline;
}

[data-bs-theme="dark"] .ccmf-cookie-consent {
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.5);
}

/* ── Seller dashboard: clickable status-filter cards (Sale Pending follow-up) ──
   The "For sale" / "For trade" / "Sale pending" summary cards double as one-at-a-time
   filters for the listings table. They reuse Bootstrap's .card chrome and only add the
   button reset + interactive affordances here. */
.listing-filter-card {
    appearance: none;
    -webkit-appearance: none;
    color: inherit;
    cursor: pointer;
    transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}

.listing-filter-card:hover:not(:disabled) {
    border-color: var(--bs-primary, #0d6efd);
}

.listing-filter-card:focus-visible {
    outline: 2px solid var(--bs-primary, #0d6efd);
    outline-offset: 2px;
}

.listing-filter-card[aria-pressed="true"] {
    border-color: var(--bs-primary, #0d6efd);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb, 13, 110, 253), .25);
}

.listing-filter-card:disabled {
    cursor: default;
    opacity: .65;
}

@media (prefers-reduced-motion: reduce) {
    .listing-filter-card {
        transition: none;
    }
}

/* ── /Messages thread list: readable active (selected) row ──────────────────────
   The selected conversation row layers Bootstrap's .active (white text on a primary
   background) with .bg-body-tertiary, which forces the background back to a light
   tertiary fill. In light mode that left the title white-on-light and invisible.
   Drop the white-on-primary scheme in favour of theme-aware body text on the tertiary
   fill, with a primary inset accent to keep the selected row distinct in both themes. */
.thread-row.list-group-item.active {
    color: var(--bs-body-color);
    background-color: var(--bs-tertiary-bg);
    border-color: var(--bs-border-color);
    box-shadow: inset 3px 0 0 var(--bs-primary, #0d6efd);
}

/* ── /Messages received bubbles: de-emphasized text that still clears WCAG AA ──────
   Received message bubbles use Bootstrap's .text-bg-light fill, which is light in BOTH
   themes (--bs-light-rgb is never overridden in dark mode). De-emphasized text on them
   must therefore be a theme-independent dark colour. Bootstrap's .text-black-50
   (rgba(0,0,0,.5)) only reaches ~3.96:1 on #f8f9fa — below AA's 4.5:1 for the
   deleted/redacted body content. This slightly darker fixed value clears AA (~6.8:1)
   while staying clearly lighter than the #000 body text, so timestamps/metadata read
   as de-emphasized. Symmetric with the sent bubble's .text-white-50 on the blue fill. */
.text-meta-on-light {
    color: rgba(0, 0, 0, 0.65) !important;
}

/* ════════════════════════════════════════════════════════════════════════════════
   Accessibility — Phase 3: WCAG 2.2 AA colour contrast (#966)
   --------------------------------------------------------------------------------
   Contrast was verified with deterministic offline WCAG math against the real
   Bootstrap 5.3.3 token values this app ships. The fixes below target the specific
   pairs that fell below 4.5:1 (normal text) / 3:1 (non-text); everything else in the
   palette already passes and is left untouched. Source order places this block last
   in site.css so its !important overrides win over Bootstrap's own !important
   utilities (equal specificity → later source wins).
   ════════════════════════════════════════════════════════════════════════════════ */

/* ── A3.5 / A3.3: status TEXT utilities (.text-success/.text-danger/.text-warning/
   .text-info) ───────────────────────────────────────────────────────────────────
   Bootstrap's base status colours are tuned as *fill* colours (white/black text on a
   solid swatch), not as foreground text on the page background. Measured as text they
   fail AA in real surfaces:
     • light: .text-warning #ffc107 (1.63:1 on white) and .text-info #0dcaf0 (1.96:1)
       are essentially illegible; .text-success #198754 and .text-danger #dc3545 pass
       on pure white (4.53:1) but fall to 4.30:1 / 3.49:1 on the tinted card and
       *-subtle badge backgrounds the app uses everywhere.
     • dark:  .text-success #198754 (3.40:1) and .text-danger #dc3545 (3.41:1) on the
       dark body are too dark.
   Bootstrap 5.3 ships a token built for exactly this — *-text-emphasis — and redefines
   it per theme, so binding the utilities to it fixes BOTH themes in one block and
   keeps the colours on-brand (just darker in light, lighter in dark). Verified AA on
   white, tinted (#f8f9fa), *-subtle, dark body (#212529) and dark tertiary (#2b3035):
   5.27–8.93:1 across the board. */
.text-success { color: var(--bs-success-text-emphasis) !important; }
.text-danger  { color: var(--bs-danger-text-emphasis) !important; }
.text-warning { color: var(--bs-warning-text-emphasis) !important; }
.text-info    { color: var(--bs-info-text-emphasis) !important; }

/* ── A3.5: solid warning / info badges ────────────────────────────────────────────
   A bare `.badge.bg-warning` / `.badge.bg-info` inherits .badge's default white
   foreground, giving white-on-yellow (1.63:1) and white-on-cyan (~1.9:1). Bootstrap's
   own .text-bg-warning / .text-bg-info pick BLACK text for these light fills; mirror
   that so unguarded badges (those without an explicit text-dark) are legible. Black on
   #ffc107 = 12.9:1, black on #0dcaf0 = 10.7:1. Scoped to .badge so background-only
   uses (progress bars, etc.) and the pale .bg-*-subtle variants are unaffected. */
.badge.bg-warning,
.badge.bg-info { color: #000 !important; }

/* ── A3.8: forced-colors (Windows High Contrast) mode ─────────────────────────────
   In forced-colors mode the browser drops box-shadow, so the app's box-shadow focus
   rings vanish — a keyboard user loses the focus indicator entirely. outline IS
   honoured (mapped to a system colour), so restore a visible focus ring with it.
   Status/meaning conveyed purely by a flattened background also needs a border to stay
   distinguishable, so give badges a system-colour outline. */
@media (forced-colors: active) {
    .btn:focus-visible,
    .btn-link.nav-link:focus,
    .nav-link:focus-visible,
    .form-control:focus,
    .form-select:focus,
    .form-check-input:focus,
    a:focus-visible,
    [tabindex]:focus-visible {
        outline: 2px solid CanvasText;
        outline-offset: 2px;
    }

    .badge {
        border: 1px solid CanvasText;
    }
}

/* ── Pre-order registry picker (dealer / catalog typeahead popups) ──
   The on-focus "Most used"/"Recent" suggestion menu and live search results
   pop up immediately over identical-looking form fields, so a plain opaque
   surface (bg-body) still blends into the page. Render the popup as a clearly
   contrasting grey floating menu instead. All colors use Bootstrap 5.3
   --bs-* tokens so they auto-flip under [data-bs-theme="dark"]. */
.preorder-picker-results.list-group {
    background-color: var(--bs-secondary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    box-shadow: var(--bs-box-shadow);
}
/* Rows are transparent so the grey surface shows through — except the
   keyboard-active row, which keeps Bootstrap's solid .active highlight. */
.preorder-picker-results .list-group-item:not(.active) {
    background-color: transparent;
}
.preorder-picker-results .list-group-item-action:not(.active):hover,
.preorder-picker-results .list-group-item-action:not(.active):focus {
    background-color: var(--bs-tertiary-bg);
}

/* ── Admin hub ──
   Grouped hub landing at /Admin (light card menu + "Needs attention" queue; see
   Views/Admin/Index.cshtml). Cards are <a class="admin-hub-card card"> so the whole card is one
   keyboard-reachable link. Motion is gated behind prefers-reduced-motion per WCAG 2.3.3. */
.admin-hub-group-title {
  display: flex;
  align-items: center;
  margin-bottom: 0.75rem;
  color: var(--bs-secondary-color);
}

.admin-hub-card {
  color: inherit;
  text-decoration: none;
}

.admin-hub-card:hover,
.admin-hub-card:focus-visible {
  border-color: var(--bs-primary);
  color: inherit;
}

.admin-hub-card:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .admin-hub-card {
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
  }

  .admin-hub-card:hover,
  .admin-hub-card:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
  }
}

.admin-hub-card-icon {
  font-size: 1.5rem;
  line-height: 1;
  color: var(--bs-primary);
}

.admin-hub-card-title {
  font-weight: 600;
}

.admin-hub-card-desc {
  font-size: 0.875rem;
  color: var(--bs-secondary-color);
}

/* Feature-flagged card in its "off" state: muted but still a clear link (points at Feature Flags,
   not a 404). Hover/focus affordances are inherited from .admin-hub-card so it stays discoverable. */
.admin-hub-card--off {
  opacity: 0.72;
}

.admin-hub-card--off .admin-hub-card-icon,
.admin-hub-card--off .admin-hub-card-title {
  color: var(--bs-secondary-color);
}

/* Condensed async site-stats strip on the hub (a compact, wrapping row of small tiles; see the
   #admin-hub-stats loader in Views/Admin/Index.cshtml). Theme-aware tokens cover light + dark. */
.admin-hub-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.admin-hub-stat {
  flex: 1 1 auto;
  min-width: 6.5rem;
  padding: 0.4rem 0.75rem;
  text-align: center;
  background: var(--bs-tertiary-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
}

.admin-hub-stat-value {
  display: block;
  font-size: 1.2rem;
  font-weight: 600;
  line-height: 1.15;
}

.admin-hub-stat-label {
  display: block;
  font-size: 0.72rem;
  color: var(--bs-secondary-color);
}

/* Skeleton placeholders need an explicit size (Bootstrap .placeholder gets its width from grid
   classes we don't use here). */
.admin-hub-stat-value.placeholder {
  width: 2.4rem;
  height: 1.2rem;
  border-radius: 0.25rem;
}

.admin-hub-stat-label.placeholder {
  width: 4rem;
  height: 0.72rem;
  margin-top: 0.3rem;
  border-radius: 0.25rem;
}

/* ── Shared image dropzone (_ImageDropzone.cshtml, #1369) ──
   One consistent uploader for the dealer-portal hero, logo, and banner. The dashed
   zone wraps a real <input type="file">; image-dropzone.js adds drag/paste/preview.
   Colors use Bootstrap CSS variables so light and dark themes are handled for free. */
.ccmf-image-dropzone {
  border: 2px dashed var(--bs-border-color);
  border-radius: 0.5rem;
  padding: 1rem;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.ccmf-image-dropzone.dragover {
  border-color: var(--bs-primary);
  background-color: rgba(var(--bs-primary-rgb), 0.06);
}

.ccmf-image-dropzone:focus,
.ccmf-image-dropzone:focus-visible {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

.ccmf-image-dropzone-preview {
  margin-bottom: 0.75rem;
}

@media (prefers-reduced-motion: reduce) {
  .ccmf-image-dropzone {
    transition: none;
  }
}

/* ──────────────────────────────────────────────────────────────────────────
   Unified search (issue #700)
   Navbar typeahead dropdown + Cmd/Ctrl+K command palette.
   Shared scope-row + result-row markup keeps the dropdown and palette
   visually consistent without each owning their own template.
   ────────────────────────────────────────────────────────────────────────── */

/* ── Navbar input ────────────────────────────────────────────────────────── */
.navbar-search-wrap {
    flex: 1 1 auto;
    max-width: 28rem;
    margin: 0 0.75rem;
}

.navbar-search-form {
    width: 100%;
}

.navbar-search-input-group {
    width: 100%;
    position: relative;
}

.navbar-search-icon {
    position: absolute;
    top: 50%;
    left: 0.625rem;
    transform: translateY(-50%);
    color: var(--bs-secondary-color);
    pointer-events: none;
    font-size: 0.95rem;
}

.navbar-search-input {
    padding-left: 2rem;
    padding-right: 3rem;
    height: 36px;
    border-radius: 999px;
    background: var(--bs-tertiary-bg);
    border-color: transparent;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.navbar-search-input::placeholder {
    color: var(--bs-secondary-color);
    opacity: 0.85;
}

.navbar-search-input:focus,
.navbar-search-input:focus-visible {
    background: var(--bs-body-bg);
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 0.2rem rgba(var(--bs-primary-rgb), 0.25);
    outline: none;
}

.navbar-search-kbd {
    position: absolute;
    top: 50%;
    right: 0.375rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    min-width: 32px;
    min-height: 24px;
}

.navbar-search-kbd:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

.navbar-search-kbd-key {
    font-size: 0.7rem;
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    padding: 0.05rem 0.4rem;
    border-radius: 4px;
    font-family: inherit;
    line-height: 1.4;
}

.navbar-search-mobile-btn {
    min-width: 36px;
    min-height: 36px;
}

/* ── Dropdown (typeahead) ────────────────────────────────────────────────── */
.navbar-search-dropdown {
    position: absolute;
    top: calc(100% + 0.375rem);
    left: 0;
    right: 0;
    z-index: 1050;
    max-height: 32rem;
    overflow-y: auto;
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

[data-bs-theme="dark"] .navbar-search-dropdown,
[data-bs-theme="dark"] .search-palette-panel {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}

/* ── Shared dropdown + palette result rows ───────────────────────────────── */
.unified-search-group-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.875rem 0.25rem;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--bs-secondary-color);
    background: transparent;
}

.unified-search-group-heading .see-all-link {
    font-size: 0.7rem;
    font-weight: 500;
    text-transform: none;
    letter-spacing: normal;
}

.unified-search-result {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.5rem 0.875rem;
    min-height: 44px;
    color: var(--bs-body-color);
    text-decoration: none;
    border-left: 3px solid transparent;
    transition: background-color 0.1s ease, border-left-color 0.1s ease;
}

.unified-search-result:hover,
.unified-search-result:focus,
.unified-search-result.is-active {
    background: var(--bs-tertiary-bg);
    border-left-color: var(--bs-primary);
    color: var(--bs-body-color);
    text-decoration: none;
}

.unified-search-result:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

.unified-search-result-icon {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
    font-size: 1rem;
}

.unified-search-result-text {
    flex: 1 1 auto;
    min-width: 0; /* enables truncation in flex child */
}

.unified-search-result-title {
    font-size: 0.9rem;
    color: var(--bs-body-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.unified-search-result-subtitle {
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.unified-search-result-barcode {
    background: rgba(var(--bs-primary-rgb), 0.08);
    border-left-color: var(--bs-primary);
}

.unified-search-empty,
.unified-search-loading {
    padding: 1.25rem 1rem;
    text-align: center;
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
}

/* Rate-limit (HTTP 429) message: search still works, so point at the full /Search page. */
.unified-search-ratelimit-title {
    font-weight: 600;
    color: var(--bs-body-color);
    margin-bottom: 0.25rem;
}

.unified-search-ratelimit-link {
    display: inline-block;
    padding: 0.35rem 0.1rem;
    font-weight: 600;
    color: var(--bs-link-color);
    text-decoration: underline;
}

.unified-search-ratelimit-link:hover,
.unified-search-ratelimit-link:focus-visible {
    color: var(--bs-link-hover-color);
}

/* ── Command palette modal ───────────────────────────────────────────────── */
.search-palette {
    position: fixed;
    inset: 0;
    z-index: 1080;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 4rem 1rem 1rem;
}

.search-palette[hidden] {
    display: none;
}

.search-palette-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(2px);
    animation: search-palette-fade-in 0.15s ease;
}

.search-palette-panel {
    position: relative;
    width: 100%;
    max-width: 38rem;
    max-height: calc(100vh - 6rem);
    background: var(--bs-body-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.75rem;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.25);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: search-palette-slide-in 0.15s ease;
}

.search-palette-header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.search-palette-search-icon {
    color: var(--bs-secondary-color);
    font-size: 1.15rem;
}

.search-palette-input {
    flex: 1 1 auto;
    border: 0;
    background: transparent;
    font-size: 1rem;
    color: var(--bs-body-color);
    outline: none;
    padding: 0.25rem 0;
}

.search-palette-input::placeholder {
    color: var(--bs-secondary-color);
}

.search-palette-esc {
    font-size: 0.7rem;
    background: var(--bs-secondary-bg);
    color: var(--bs-secondary-color);
    border: 1px solid var(--bs-border-color);
    padding: 0.05rem 0.4rem;
    border-radius: 4px;
}

.search-palette-close {
    min-width: 32px;
    min-height: 32px;
    color: var(--bs-secondary-color);
    padding: 0.25rem;
}

.search-palette-close:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

.search-palette-results {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.25rem 0;
}

.search-palette-footer {
    border-top: 1px solid var(--bs-border-color);
    padding: 0.5rem 1rem;
    font-size: 0.75rem;
    color: var(--bs-secondary-color);
    background: var(--bs-tertiary-bg);
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.search-palette-footer-note {
    font-size: 0.7rem;
}

.search-palette-footer-note code {
    font-size: 0.7rem;
    padding: 0 0.2rem;
}

.search-palette-footer kbd {
    background: var(--bs-secondary-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.7rem;
    margin: 0 0.1rem 0 0.3rem;
}

/* Recent searches surfaced when the palette opens with an empty input. */
.search-palette-recent-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.875rem;
    min-height: 36px;
    cursor: pointer;
    color: var(--bs-body-color);
    background: transparent;
    border: 0;
    width: 100%;
    text-align: left;
}

.search-palette-recent-row:hover,
.search-palette-recent-row:focus,
.search-palette-recent-row.is-active {
    background: var(--bs-tertiary-bg);
}

.search-palette-recent-row:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

.search-palette-recent-icon {
    color: var(--bs-secondary-color);
    font-size: 0.9rem;
}

/* ── Hide kbd-hint on coarse pointers / narrow screens ───────────────────── */
@media (max-width: 991.98px) {
    .navbar-search-kbd {
        display: none;
    }
    .navbar-search-input {
        padding-right: 0.75rem;
    }
}

/* ── Reduced motion: drop the entry animations ───────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .search-palette-backdrop,
    .search-palette-panel,
    .navbar-search-input,
    .unified-search-result {
        animation: none;
        transition: none;
    }
}

@keyframes search-palette-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes search-palette-slide-in {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
}

