/* SafeList theme tokens. Teal is the default palette (bare :root below); Rose is the
   user-toggleable alternative (:root[data-theme="rose"]), applied by wwwroot/js/theme-toggle.js
   and pre-applied before first paint by the inline <head> script in the _Layout / _AdminLayout /
   _DashboardLayout views (localStorage 'safelist-theme' || 'teal'). This is a skin only, not a
   layout: every rule reads var(--accent) etc., so a theme switch recolors but never reshapes -
   --radius and --font-heading are shared and identical between the two. Identity's scaffolded
   Register/Login/Manage pages inherit it automatically. A no-JS visitor gets the default (teal)
   since the bare :root carries it. */

:root {
  --bg: #ffffff;
  --bg-alt: #f9fafb;
  --text: #111827;
  --text-muted: #6b7280;
  --border: #e5e7eb;
  --accent: #0d9488;
  --accent-dark: #0f766e;
  --accent-bg: #f0fdfa;
  --radius: 12px;
  --shadow: 0 1px 2px rgba(17, 24, 39, 0.04), 0 4px 20px rgba(17, 24, 39, 0.06);
  --shadow-hover: 0 2px 4px rgba(17, 24, 39, 0.05), 0 16px 32px rgba(17, 24, 39, 0.1);
  --focus-ring: 0 0 0 3px var(--accent-bg), 0 0 0 4.5px color-mix(in srgb, var(--accent) 45%, transparent);
  --max-width: 1140px;
  --font-heading: "Inter", sans-serif;
}

/* Rose theme - the blush/boutique palette, offered as a user-toggleable alternative rather than
   the default. Same variable set as the teal default above, so every rule that already reads
   var(--accent) etc. re-themes for free - only the colors differ (plus --shadow's tint, to match).
   Applied via [data-theme="rose"] on <html>, set by wwwroot/js/theme-toggle.js. */
:root[data-theme="rose"] {
  --bg: #ffffff;
  --bg-alt: #fdf6f8;
  --text: #3f2a33;
  --text-muted: #9c7986;
  --border: #f3dde5;
  --accent: #c25c81;
  --accent-dark: #a1445f;
  --accent-bg: #fdeef2;
  --shadow: 0 1px 2px rgba(194, 92, 129, 0.06), 0 4px 20px rgba(194, 92, 129, 0.1);
  --shadow-hover: 0 2px 4px rgba(194, 92, 129, 0.08), 0 18px 36px rgba(194, 92, 129, 0.18);
}

* {
  box-sizing: border-box;
}

html {
  font-size: 16px;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--accent);
  text-decoration: none;
  transition: color 0.2s ease;
}

a:hover {
  color: var(--accent-dark);
}

::selection {
  background: var(--accent);
  color: #ffffff;
}

/* Custom scrollbar - thin, theme-tinted, rounded, instead of the browser's boxy default. */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 999px;
  border: 2px solid var(--bg);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--accent);
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

/* Consistent, visible keyboard-focus ring across interactive elements, replacing the browser's
   default outline - :focus-visible so mouse clicks don't show it, only keyboard/AT navigation. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
.nav-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: 4px;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  font-weight: 600;
  letter-spacing: -0.01em;
}

p, .text-muted {
  color: var(--text-muted) !important;
}

.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}

/* Admin/Moderator pages get a wider container than the marketing/buyer pages (see _Layout.cshtml,
   which adds this class only for those two controllers, and _AdminLayout.cshtml which uses it
   directly) - their data tables (Listings/Orders/Users/Reports) have 7-9 columns and need real
   room, where 1140px forces horizontal scrolling even with every cell already nowrap (see the
   .table td rule below). Uncapped (was 1600px) rather than raised to a new fixed number - .wrap-wide
   sits inside .admin-main, which is already flex:1 with no cap of its own, so the table can use
   every pixel a wide monitor has instead of hitting a second, redundant ceiling. */
.wrap-wide {
  max-width: none;
}

.eyebrow {
  display: inline-block;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 10px;
}

/* A Wanted listing's own category eyebrow (Details/_ListingCard) matches the fixed light-blue
   .category-tile-wanted already uses (#2563eb) rather than the theme's rose/teal accent, so it reads
   as the same "this is a request, not an item for sale" signal wherever it shows up. Urgent takes
   priority over Wanted when both apply - same fixed alert-red (#dc3545) the Urgent badge/strip/fee
   icon all already use. */
.eyebrow-wanted {
  color: #2563eb;
}

.eyebrow-urgent {
  color: #dc3545;
}

/* Header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255, 255, 255, 0.85);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

.site-header .navbar {
  background: transparent !important;
  padding: 16px 0;
  box-shadow: none !important;
}

.site-header .navbar-brand {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 23px;
  color: var(--text) !important;
  letter-spacing: -0.01em;
}

/* Artegra Sans Bold is a licensed desktop font, not a web font on Google Fonts, so it's
   self-hosted from wwwroot/fonts rather than linked externally. */
@font-face {
  font-family: "Artegra Sans Bold";
  src: url("/fonts/ArtegraSans-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Wordmark uses Artegra Sans Bold specifically (matches the brand's approved logo font) rather
   than --font-heading (Inter), which stays the site's body/heading font everywhere else - this is
   scoped to just the "SafeList" text next to the icon, not a sitewide font swap. */
.site-header .navbar-brand-text {
  font-family: "Artegra Sans Bold", var(--font-heading);
  font-weight: 700;
}

.site-header .navbar-brand-icon {
  flex-shrink: 0;
}

.logo-mark {
  flex-shrink: 0;
}

.site-header .navbar-brand-text .accent {
  color: var(--accent);
}

/* Theme toggle - always visible next to the mobile hamburger, not tucked inside the
   collapsible nav, so it's reachable on every screen size without opening the menu. */
.theme-toggle-switch {
  position: relative;
  width: 40px;
  height: 22px;
  padding: 0;
  flex-shrink: 0;
  border: none;
  border-radius: 999px;
  background: var(--accent);
  cursor: pointer;
  transition: background 0.2s ease;
}

.theme-toggle-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}

:root[data-theme="teal"] .theme-toggle-knob {
  transform: translateX(18px);
}

/* Manual/help download icon button - same "transparent icon button, accent on hover" shape as
   .admin-sidebar-toggle further down, reused across the public header (Home page only - see
   _Layout.cshtml's own controller/action check), the Dashboard topbar, the Admin topbar, and the
   Moderator nav for each portal's own manual download link. */
.manual-help-btn {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  font-size: 17px;
  text-decoration: none;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.manual-help-btn:hover {
  background: var(--accent-bg);
  color: var(--accent);
  border-color: var(--accent);
}

/* Generic on/off pill switch (e.g. the dashboard's sound toggle) - same shape as
   .theme-toggle-switch above, but state-driven by an .is-on class instead of the theme attribute,
   and dims to a neutral track when off instead of always showing the accent color - both themes are
   equally "on" for .theme-toggle-switch, but a muted sound toggle should visibly read as off. */
.pill-toggle {
  position: relative;
  width: 40px;
  height: 22px;
  padding: 0;
  flex-shrink: 0;
  border: none;
  border-radius: 999px;
  background: var(--border);
  cursor: pointer;
  transition: background 0.2s ease;
}

.pill-toggle.is-on {
  background: var(--accent);
}

.pill-toggle-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}

.pill-toggle.is-on .pill-toggle-knob {
  transform: translateX(18px);
}

.site-header .nav-link {
  font-size: 14px;
  font-weight: 500;
  color: var(--text) !important;
  position: relative;
  padding: 6px 0 !important;
  margin: 0 16px;
  /* Keep each label on a single line - without this a crowded row breaks multi-word items like
     "My Listings"/"My Offers" at their space onto a second line. */
  white-space: nowrap;
}

.site-header .nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--accent);
  transition: width 0.25s ease;
}

.site-header .nav-link:hover::after {
  width: 100%;
}

/* Sign In / Register are the primary CTAs for a signed-out visitor, not just more nav items -
   stays accent-colored (re-themes with the rose/teal switch) rather than the same dark text as
   Browse/My Listings/etc. Needs !important to beat .site-header .nav-link's own !important. */
.site-header .nav-link.nav-link-cta {
  color: var(--accent) !important;
  font-weight: 600;
}

.site-header .nav-link.nav-link-cta:hover {
  color: var(--accent-dark) !important;
}

/* "Moderator" (only ever shown to a signed-in moderator) is a fixed red, not var(--accent) -
   deliberately NOT re-themed by the rose/teal switch, since it's meant to read as "elevated/
   staff area" regardless of which theme is active. Same red Bootstrap's own .text-danger/
   .btn-danger already use elsewhere on this site (e.g. AgentStatus.Rejected), not a new color. */
.site-header .nav-link.nav-link-moderator {
  color: #dc3545 !important;
  font-weight: 600;
}

.site-header .nav-link.nav-link-moderator:hover {
  color: #b02a37 !important;
}

/* Every Admin/Moderator page opens with an identical <h1 class="mb-4">Admin</h1> or
   ...>Moderator</h1> heading (see _Layout.cshtml's staffAreaClass) - same fixed red as the
   Moderator nav-link above, for the same "elevated/staff area" reason. */
main.staff-area-admin h1,
main.staff-area-moderator h1 {
  color: #dc3545;
}

/* Mobile menu - the collapsible nav sits inside the same flex row as the brand/toggler (not
   Bootstrap's own .navbar flex container, which wraps this for you), so without an explicit
   wrap it squeezed into the leftover space beside the logo instead of dropping to its own
   full-width row below - clipped text, overlapping the hero section underneath. */
@media (max-width: 767.98px) {
  .site-header .navbar > .wrap {
    flex-wrap: wrap;
  }

  .site-header .navbar-collapse {
    flex-basis: 100%;
    width: 100%;
  }

  .site-header .navbar-collapse.show {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
  }

  .site-header .navbar-nav {
    width: 100%;
  }

  .site-header .nav-link {
    margin: 0 !important;
    padding: 10px 0 !important;
  }

  .site-header .nav-link::after {
    display: none;
  }

  /* The brand + action-icons row needs to fit on one line here too - the flex-wrap above exists
     so the collapsible nav menu can drop to its own row when opened, but the same wrap was also
     triggering between the brand and the "?"/FAQ/theme-toggle/hamburger cluster on narrow phones
     in portrait (reported live: showing on two lines) - confirmed the combined width was only a
     few pixels over budget once Bootstrap's default .navbar-brand margin is counted, so a modest
     trim across every piece is enough, rather than one large cut that would look undersized on
     its own. */
  .site-header .navbar-brand {
    margin-right: 0.5rem;
    font-size: 20px;
  }

  .header-actions {
    gap: 6px !important;
  }

  .manual-help-btn {
    width: 28px;
    height: 28px;
    font-size: 15px;
  }

  .header-faq-btn {
    padding: 4px 10px;
    font-size: 12.5px;
  }

  .theme-toggle-switch {
    width: 34px;
    height: 19px;
  }

  .theme-toggle-knob {
    width: 15px;
    height: 15px;
  }

  :root[data-theme="teal"] .theme-toggle-knob {
    transform: translateX(15px);
  }

  .site-header .navbar-toggler {
    padding: 3px 8px;
    font-size: 1.05rem;
  }
}

.site-header form button.btn-link {
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  padding: 6px 0;
}

/* Buttons - override Bootstrap's default square, saturated buttons with soft pill styling */

.btn {
  border-radius: 999px !important;
  font-weight: 600;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn:active {
  transform: translateY(0) scale(0.97);
}

.btn-primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-dark)) !important;
  border-color: var(--accent) !important;
  box-shadow: 0 6px 16px color-mix(in srgb, var(--accent) 25%, transparent);
}

.btn-primary:hover, .btn-primary:focus {
  background: linear-gradient(135deg, var(--accent-dark), var(--accent-dark)) !important;
  border-color: var(--accent-dark) !important;
  box-shadow: 0 8px 20px color-mix(in srgb, var(--accent) 32%, transparent);
}

.btn-outline-primary, .btn-outline-secondary {
  color: var(--text) !important;
  border-color: var(--border) !important;
  background: transparent !important;
}

.btn-outline-primary:hover, .btn-outline-secondary:hover {
  border-color: var(--accent) !important;
  color: var(--accent) !important;
  background: var(--accent-bg) !important;
}

/* .btn-outline-primary's own !important background above otherwise beats Bootstrap's stock
   ".active" styling outright, regardless of specificity - a JS-toggled ".active" class (e.g. the
   CourierGuy/SafeList Standard delivery-mode toggle in _DeliveryOptionsFields.cshtml) would silently
   never show any visual difference at all without this. Two classes beats one even among
   "!important" rules, so this wins. */
.btn-outline-primary.active {
  background: linear-gradient(135deg, var(--accent), var(--accent-dark)) !important;
  border-color: var(--accent) !important;
  color: #fff !important;
}

.btn-success {
  background: #7c9a7e !important;
  border-color: #7c9a7e !important;
}

.btn-danger, .btn-outline-danger:hover {
  background: #c65d6e !important;
  border-color: #c65d6e !important;
  color: #fff !important;
}

.btn-outline-danger {
  color: #c65d6e !important;
  border-color: #f3d3d8 !important;
  background: transparent !important;
}

.btn-outline-warning {
  color: #b8793f !important;
  border-color: #f1ddc0 !important;
  background: transparent !important;
}

.btn-outline-warning:hover {
  background: #fdf6ec !important;
  border-color: #b8793f !important;
  color: #b8793f !important;
}

.btn-outline-success {
  color: #5c7d5e !important;
  border-color: #d3e0d0 !important;
  background: transparent !important;
}

.btn-outline-success:hover {
  background: #f1f5ee !important;
  border-color: #7c9a7e !important;
  color: #5c7d5e !important;
}

/* Cards */

.card {
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

a.listing-card {
  display: block;
  color: inherit;
}

a.listing-card:hover .card {
  transform: translateY(-5px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border)) !important;
  color: inherit;
}

/* Image crops to the card's rounded top corners and zooms slightly on hover - the image itself
   scales, not the card, so the card's shadow/lift transition above stays independent of it. */
.listing-card-img-wrap {
  overflow: hidden;
  border-radius: var(--radius) var(--radius) 0 0;
}

.listing-card-img-wrap .listing-card-img {
  transition: transform 0.4s ease;
}

a.listing-card:hover .listing-card-img-wrap .listing-card-img {
  transform: scale(1.06);
}

.card .icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--accent-bg);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  margin-bottom: 16px;
}

/* Animated "how it works" step icons */

.step-card {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.step-icon {
  width: 100%;
  max-width: 200px;
  height: auto;
  margin-bottom: 12px;
}

/* Numbered badge replacing the old "1. " text prefix in each step heading */

.step-number {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 16px;
  margin-bottom: 14px;
}

/* Sequential reveal (script in Views/Home/Index.cshtml cycles .is-active on a timer) - dims the
   two steps not currently "in focus" rather than hiding them outright, so the row doesn't
   reflow/collapse and the story of list -> ship -> confirm reads as one step at a time. */

.step-sequence .step-card {
  opacity: 0.4;
  filter: saturate(0.5);
  transition: opacity 0.6s ease, filter 0.6s ease, transform 0.6s ease;
}

.step-sequence .step-card.is-active {
  opacity: 1;
  filter: none;
  transform: translateY(-3px);
}

.step-sequence .step-card.is-active .step-number {
  box-shadow: 0 0 0 5px var(--accent-bg);
}

/* Home page "How it works" flow - a distinct, richer variant of the .step-card/.step-number
   pattern above (still shared with Agent/Register.cshtml's plainer 4-step list, so those two
   classes are left untouched) - an icon circle + large number + dark label pill per step, with
   connecting arrows that light up in sequence alongside the existing .step-sequence reveal timer. */
.step-flow {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 4px;
  flex-wrap: wrap;
}

.step-flow-item {
  flex: 1 1 200px;
  max-width: 230px;
}

.step-flow .step-icon-circle {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 34px;
  margin: 0 auto 14px;
  transition: box-shadow 0.6s ease;
}

.step-flow .step-number {
  display: block;
  background: none;
  border-radius: 0;
  box-shadow: none;
  width: auto;
  height: auto;
  color: var(--accent);
  font-size: 32px;
  font-weight: 800;
  line-height: 1.1;
  margin: 0 0 12px;
}

/* .step-sequence .step-card.is-active .step-number (further up this file) puts a ring
   box-shadow on the active step's number - correct for the plain circular badge it was written
   for, but .step-flow's own number is deliberately bare text with the glow moved to
   .step-icon-circle below, so it needs a higher-specificity override to actually win. */
.step-flow.step-sequence .step-card.is-active .step-number {
  box-shadow: none;
}

.step-flow .step-label {
  display: inline-block;
  background: var(--text);
  color: #ffffff;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 13.5px;
  line-height: 1.3;
  padding: 8px 14px;
  border-radius: 10px;
  margin-bottom: 10px;
}

.step-flow .step-desc {
  font-size: 13.5px;
  color: var(--text-muted);
  margin: 0;
}

.step-flow-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 32px;
  padding-top: 32px;
  font-size: 24px;
  color: var(--border);
  transition: color 0.6s ease;
}

.step-flow.step-sequence .step-flow-item.is-active .step-icon-circle {
  box-shadow: 0 0 0 6px var(--accent-bg);
}

.step-flow-arrow.is-active {
  color: var(--accent);
}

@media (max-width: 900px) {
  .step-flow {
    flex-direction: column;
    align-items: center;
  }

  .step-flow-item {
    max-width: 320px;
  }

  .step-flow-arrow {
    width: auto;
    padding: 4px 0;
    transform: rotate(90deg);
  }
}

/* Hero */

.hero {
  padding: 64px 0 48px;
  text-align: center;
  background:
    radial-gradient(60% 90% at 15% 0%, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 60%),
    radial-gradient(50% 80% at 85% 10%, color-mix(in srgb, var(--accent) 10%, transparent) 0%, transparent 60%),
    linear-gradient(180deg, var(--accent-bg) 0%, #ffffff 70%);
  border-bottom: 1px solid var(--border);
}

.hero h1 {
  max-width: 680px;
  margin: 0 auto 18px;
  font-size: clamp(30px, 5vw, 42px);
}

.hero p.lead {
  max-width: 540px;
  margin: 0 auto 28px;
  font-size: 18px;
  color: var(--text-muted);
}

.hero-actions {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Home hero's Gumtree-style search box - submits straight to Browse's own `q` search (see
   ListingsController.Index), no controller changes needed. */
.hero-search {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 560px;
  margin: 22px auto;
  padding: 6px 6px 6px 18px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: var(--shadow);
}

.hero-search-icon {
  color: var(--text-muted);
  font-size: 18px;
}

.hero-search-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-size: 15px;
  color: var(--text);
  padding: 8px 0;
}

.hero-search-input::placeholder {
  color: var(--text-muted);
}

.hero-search-btn {
  border-radius: 999px;
  padding: 10px 22px;
  flex: 0 0 auto;
}

/* "Browse by category" mega-menu (Views/Home/Index.cshtml) - a Gumtree-style icon grid. Desktop
   reveals each tile's subcategories on hover/focus via pure CSS; a chevron button gives touch/
   keyboard users an equivalent click-to-toggle path (.is-open), handled in Index.cshtml's own
   inline script. Below 768px the flyout drops floating-popover positioning entirely and expands
   inline in the grid flow instead, so it can never overflow a narrow viewport. */
.category-tile-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

@media (min-width: 576px) {
  .category-tile-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 768px) {
  .category-tile-grid { grid-template-columns: repeat(4, 1fr); }
}

@media (min-width: 992px) {
  .category-tile-grid { grid-template-columns: repeat(6, 1fr); }
}

.category-tile-wrap {
  position: relative;
}

.category-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  height: 100%;
  padding: 22px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  box-shadow: var(--shadow);
  color: var(--text);
  text-align: center;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

/* Listing-count pill - "in the right hand corner of each tile", floating just outside the card's
   own edge so it never collides with the .category-tile-expand chevron sitting just inside it. */
.category-tile-count {
  position: absolute;
  top: -8px;
  right: -8px;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow);
  z-index: 1;
}

/* "Most active categories" strip - a smaller, simpler tile with no expand/flyout affordance
   (that's the full "Browse by category" directory's job below it) - just icon, name, and count. */
.category-tile-simple {
  padding: 18px 12px;
}

.category-tile-simple .category-tile-icon {
  width: 40px;
  height: 40px;
  font-size: 18px;
}

.category-tile:hover,
.category-tile:focus-visible {
  transform: translateY(-3px);
  box-shadow: var(--shadow-hover);
  border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
  color: var(--text);
}

.category-tile-icon {
  width: 48px;
  height: 48px;
  border-radius: 999px;
  background: var(--accent-bg);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  flex: 0 0 auto;
}

.category-tile-name {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
}

/* Wanted's tile gets a fixed light-blue signal (not the theme's own rose/teal accent) so it reads
   as its own distinct thing among the other categories - it works differently (post a request,
   not an item for sale), same reasoning as Urgent's fixed alert-red above. */
.category-tile-wanted .category-tile-icon {
  background: #dbeafe;
  color: #2563eb;
}

.category-tile-wanted .category-tile-name {
  color: #2563eb;
}

.category-tile-expand {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 24px;
  height: 24px;
  border: none;
  border-radius: 999px;
  background: var(--bg-alt);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, transform 0.2s ease;
}

.category-tile-expand:hover {
  background: var(--accent-bg);
  color: var(--accent);
}

.category-tile-wrap.is-open .category-tile-expand {
  transform: rotate(180deg);
}

.category-tile-flyout {
  display: none;
  flex-direction: column;
  gap: 2px;
  padding: 10px;
}

.category-tile-flyout a {
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--text);
}

.category-tile-flyout a:hover {
  background: var(--accent-bg);
  color: var(--accent);
}

@media (min-width: 768px) {
  .category-tile-flyout {
    /* Flush against the tile (no gap) - the classic hover-dropdown bug is a dead zone between
       trigger and menu: the pointer crosses space that belongs to neither element, :hover breaks
       mid-transition, the flyout closes, and the click lands on whatever's underneath (the next
       tile down). Zero gap means the pointer never leaves a hoverable box while moving from tile
       into flyout. The existing 10px padding already gives the same breathing room the old 6px
       top-offset did, just inside the box instead of outside it. */
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 20;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-hover);
  }

  .category-tile-wrap:hover .category-tile-flyout,
  .category-tile-wrap:focus-within .category-tile-flyout,
  .category-tile-wrap.is-open .category-tile-flyout {
    display: flex;
  }

  /* Collapsing via the chevron removes .is-open, but the pointer is still sitting on the tile
     right after that click - the :hover rule above would otherwise keep the flyout showing until
     the pointer actually leaves. .is-closing (Index.cshtml's script, cleared on mouseleave) forces
     it shut immediately instead. Same specificity as the rule above, so source order is what makes
     this win while both classes are present. */
  .category-tile-wrap.is-closing .category-tile-flyout {
    display: none;
  }
}

@media (max-width: 767.98px) {
  .category-tile-wrap.is-open .category-tile-flyout {
    display: flex;
    background: var(--bg-alt);
    border-radius: 0 0 var(--radius) var(--radius);
    margin-top: -1px;
  }
}

/* Hero trust signals + trust-pillar cards */
.trust-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 26px;
}
.trust-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  color: var(--accent-dark);
  font-size: 13px;
  font-weight: 600;
}

/* Half-size variant for compact trust-pill rows (the home hero, under the live ticker; Agent
   Register's own hero) - scoped to this modifier rather than shrinking .trust-pill itself, since
   other pages reuse the base class at full size. Padding/gap are a literal half of the base
   values; font-size is held at 9.5px rather than a literal 6.5px (half of 13px) - below ~9px,
   icon+text pills stop being legible regardless of how much smaller they read.

   Width is a CSS custom property, not a fixed value, because every row's longest label is a
   different length ("Identities masked" on the home hero vs. "Never costs a seller more" on
   Agent Register) - each .trust-pills-sm wrapper sets its own --pill-w inline, sized to fit that
   row's own longest label, so every pill in a given row still matches its siblings' width. */
.trust-pills-sm .trust-pill {
  gap: 3px;
  padding: 3px 7px;
  font-size: 9.5px;
  width: var(--pill-w, auto);
  justify-content: center;
  white-space: nowrap;
}
.pillar-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-bg);
  color: var(--accent);
  font-size: 24px;
  margin-bottom: 14px;
}

/* Buy/Sell segmented toggle + live activity ticker (home hero). Both read var(--accent) only -
   no color of their own - so they re-theme for free with the rose/teal switch exactly like every
   other accent-colored element on the site; nothing here is hardcoded to either theme. */

.segmented {
  display: inline-flex;
  padding: 4px;
  border-radius: 999px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  margin-bottom: 22px;
}

.segmented button {
  appearance: none;
  border: none;
  background: transparent;
  cursor: pointer;
  font: inherit;
  font-family: var(--font-heading);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text-muted);
  padding: 9px 22px;
  border-radius: 999px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: color 0.15s ease, background 0.15s ease;
}

.segmented button.active {
  background: var(--bg);
  color: var(--text);
  box-shadow: var(--shadow);
}

/* Small accent dot marking the selected mode - same visual language as Dashboard's .type-dot,
   just inline before the label rather than before a heading. Absent (not just transparent) on the
   unselected buttons so their padding stays centered rather than reserving dead space for a dot
   that isn't there. */
.segmented button.active::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

/* A full-bleed bar (see Home/Index.cshtml's own comment on why this sits outside .wrap) rather
   than the centered pill this used to be - top/bottom rule lines stand in for the pill's border
   now that there's no left/right edge of its own to draw one around. Bottom margin is 0, not
   symmetric with the 26px top, matching the old pill's spacing - .trust-pills (the next section's
   content) already carries its own 26px margin-top, so this and that stack to the 26px gap above
   (hero-actions has no margin-bottom of its own) rather than double up to 50px if this also had 26. */
.live-ticker {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  margin: 26px 0 0;
  padding: 10px max(24px, calc((100% - var(--max-width)) / 2 + 24px));
  background: var(--bg);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.live-dot-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
}

.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  position: relative;
  flex-shrink: 0;
}

.live-dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1.5px solid var(--accent);
  opacity: 0.5;
  animation: live-dot-pulse 2s ease-out infinite;
}

@keyframes live-dot-pulse {
  0% { transform: scale(0.6); opacity: 0.6; }
  100% { transform: scale(1.8); opacity: 0; }
}

.ticker-viewport {
  overflow: hidden;
  /* Was a fixed min(420px, 60vw) back when this sat inside the centered pill - now that
     .live-ticker is a full-bleed bar, this fills whatever's left after the "Live" label instead of
     capping itself, which is the whole point of widening it. min-width: 0 is the standard flex-item
     fix so a long-running ticker-track's own intrinsic (nowrap) width can't force this - and the
     bar around it - wider than the viewport. */
  flex: 1;
  min-width: 0;
  position: relative;
}

.ticker-viewport::before,
.ticker-viewport::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 24px;
  z-index: 2;
}

.ticker-viewport::before { left: 0; background: linear-gradient(90deg, var(--bg), transparent); }
.ticker-viewport::after { right: 0; background: linear-gradient(270deg, var(--bg), transparent); }

/* width: max-content is load-bearing, not cosmetic - a display:flex element with no explicit width
   is still a normal block-level box as far as its OWN sizing goes, so absent this it would default
   to filling .ticker-viewport's width rather than its own (much wider, duplicated) content's
   natural width. transform: translateX(-50%) resolves its percentage against that box width, not
   against scrollWidth - so without this, -50% moved the track by half the *viewport's* width
   instead of half its actual content width, cutting the animation off partway through a lap
   instead of completing a full, seamless pass (reported live: content exits the left, nothing new
   appears on the right - the shift was simply too short to ever get there). */
.ticker-track {
  display: flex;
  width: max-content;
  gap: 34px;
  white-space: nowrap;
  font-size: 13px;
  color: var(--text-muted);
  animation: ticker-scroll 16s linear infinite;
}

.ticker-track[hidden] { display: none; }

/* Items are clickable (HeroFeedService only sets a link when the underlying listing is still
   publicly viewable) - styled to read the same as the plain-text fallback items until hovered, and
   paused on hover so a moving target doesn't dodge the click. */
.ticker-track a {
  color: inherit;
  text-decoration: none;
}

.ticker-track a:hover {
  color: var(--accent);
  text-decoration: underline;
}

.ticker-track:hover {
  animation-play-state: paused;
}

@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; }
  .live-dot::after { animation: none; }
}

@media (max-width: 560px) {
  .live-ticker { padding: 8px 16px; gap: 10px; }
}

/* Professional bordered pillar grid - an alternative to the icon+card treatment used elsewhere
   (Agent Register's own trust pillars keep the original .card/.pillar-icon look; this one is
   scoped to .pillar-grid so it doesn't affect that page). */

.pillar-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.pillar-tile {
  background: var(--bg);
  padding: 28px 24px;
}

.pillar-tile .tile-icon {
  width: 38px;
  height: 38px;
  border-radius: 9px;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  color: var(--accent-dark);
  font-size: 17px;
}

.pillar-tile h3 {
  font-size: 15.5px;
  margin: 0 0 8px;
}

.pillar-tile p {
  font-size: 13.5px;
  margin: 0;
}

@media (max-width: 900px) { .pillar-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .pillar-grid { grid-template-columns: 1fr; } }

/* Trust-intro video, sitting below the pillar grid - a wide 16:9 frame rather than a small inline
   clip, since this is meant to be the section's closing statement (show, not just tell). */
.trust-video-frame {
  position: relative;
  margin-top: 32px;
  /* Fixed, not var(--radius) - the rose/teal themes each carry their own corner roundness
     (18px/12px) for cards and buttons, but that made the frame visibly change shape when the
     visitor switched themes; a black 16:9 rectangle reads as a "screen", so it stays one fixed
     shape regardless of which theme is active. */
  border-radius: 16px;
  overflow: hidden;
  box-shadow: var(--shadow);
  aspect-ratio: 16 / 9;
  background: #000;
}

.trust-video-frame video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Overlay button standing in for the video until clicked - see the click handler in Index.cshtml,
   which is what actually attaches the <source>'s real URL (kept off it as data-src up to that
   point), so the browser makes no request for the clip at all until this is pressed. */
.trust-video-unlock {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  border: none;
  background: rgba(10, 12, 14, 0.6);
  color: #ffffff;
  cursor: pointer;
  padding: 24px;
  font-family: var(--font-heading);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
}

.trust-video-unlock:hover {
  background: rgba(10, 12, 14, 0.72);
}

.trust-video-unlock-icon {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36px;
  flex-shrink: 0;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  transition: transform 0.15s ease, background-color 0.15s ease;
}

/* The player-play glyph's triangle isn't optically centered in its own box - nudge it right so it
   reads as centered inside the circle instead of drifting left. */
.trust-video-unlock-icon i {
  margin-left: 4px;
}

.trust-video-unlock:hover .trust-video-unlock-icon {
  background: var(--accent-dark);
  transform: scale(1.06);
}

/* Agent Program banner - compact single card with inline stats, replacing the old full-width
   centered section for a tighter, more "product" feel. */

.agent-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 34px 40px;
  background: linear-gradient(120deg, var(--bg), var(--bg-alt));
}

.agent-banner .agent-banner-copy h2 {
  font-size: 21px;
  margin: 8px 0 8px;
}

.agent-banner .agent-banner-copy p {
  font-size: 14px;
  margin: 0;
  max-width: 46ch;
}

.agent-stats {
  display: flex;
  gap: 26px;
  flex-shrink: 0;
}

.agent-stat { text-align: center; }

.agent-stat strong {
  display: block;
  font-family: var(--font-heading);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.agent-stat span {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

/* Agent Dashboard's tier-promotion progress bar - how many of the current 5 (SalesPerPromotion)
   direct-referral sales have landed since the last promotion. Glows via a pulsing box-shadow on
   the fill itself (var(--accent), so it re-themes with the rose/teal switch like everything
   else) rather than a static bar, since this is meant to read as "live progress toward
   something," not just a plain stat. */
.tier-progress-track {
  height: 10px;
  border-radius: 999px;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  overflow: hidden;
}

.tier-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-dark));
  width: 0%;
  /* Starts at 0 and is set to its real width by JS only once scrolled into view (see
     Views/Agent/Dashboard.cshtml) - this transition is what makes that read as a deliberate
     fill rather than an instant jump. No glow yet at this point - see .is-filled below. */
  transition: width 1s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Added by JS once the width transition above finishes (transitionend) - the glow only starts
   pulsing after the bar has actually finished filling, not concurrently with it. */
.tier-progress-fill.is-filled {
  animation: tier-progress-glow 1.8s ease-in-out infinite;
}

@keyframes tier-progress-glow {
  0%, 100% { box-shadow: 0 0 6px 1px color-mix(in srgb, var(--accent) 55%, transparent); }
  50% { box-shadow: 0 0 14px 3px color-mix(in srgb, var(--accent) 85%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .tier-progress-fill.is-filled { animation: none; box-shadow: 0 0 6px 1px color-mix(in srgb, var(--accent) 55%, transparent); }
}

.tier-progress-label {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 5px;
}

/* Agent Dashboard's status stat-tiles (Status / Level / Referrals / Tier) - same bordered-grid
   shape as .pillar-grid, but sized for a short label+value pair rather than an icon+heading+
   paragraph. Replaces what used to be a plain dl.row list, so the Dashboard reads as part of the
   same designed site as the home hero/pillar grid instead of a bare admin form. */
.dash-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 20px;
}

.dash-stat-tile {
  background: var(--bg);
  padding: 16px 14px;
  border-top: 3px solid transparent;
}

.dash-stat-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.dash-stat-value {
  display: block;
  font-family: var(--font-heading);
  font-size: 19px;
  font-weight: 700;
}

.dash-stat-sub {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Commission Tier gets the most visual weight of the four - it's the one number an agent
   actually cares about - via a colored top edge and tinted background matching the same
   per-tier colors already used by the badge-soft-* classes elsewhere on this page. */
.dash-stat-tile-tier.tier-accent-bronze { border-top-color: #a4692f; background: #fdf3e4; }
.dash-stat-tile-tier.tier-accent-silver { border-top-color: #9c7986; background: #f6ecef; }
.dash-stat-tile-tier.tier-accent-gold { border-top-color: var(--accent-dark); background: var(--accent-bg); }
.dash-stat-tile-tier.tier-accent-platinum { border-top-color: #4f7052; background: #eef4ec; }

@media (max-width: 700px) {
  .dash-stat-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 420px) {
  .dash-stat-grid { grid-template-columns: 1fr; }
}

/* Agent Dashboard's first-visit tier explainer - a small dismissible callout, not a modal, with
   a speech-bubble tail pointing down at the progress bar it's explaining. Shown once; "Got it"
   sets localStorage so it never shows again on this browser (see Views/Agent/Dashboard.cshtml -
   same durable-dismiss pattern the rose/teal theme toggle already uses). Rendered `hidden` by
   default so there's no flash of it before that check runs. */
.tier-hint-balloon {
  position: relative;
  background: var(--accent-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: 12px;
  padding: 12px 34px 12px 14px;
  margin-bottom: 14px;
  font-size: 13px;
  color: var(--text);
}

.tier-hint-balloon::after {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 28px;
  width: 14px;
  height: 14px;
  background: var(--accent-bg);
  border-right: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  transform: rotate(45deg);
}

.tier-hint-close {
  position: absolute;
  top: 8px;
  right: 10px;
  border: none;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 2px 4px;
}

.tier-hint-close:hover { color: var(--text); }

/* Home intro loader - shown once per session on the home page. A progress ring fills the whole
   ~2s while the centre morphs product -> cart -> shield, the shield's check drawing in as the ring
   seals shut. Uses var(--accent) so it matches the active theme. See _Layout.cshtml for the markup
   + the (session-gated) show/hide scripts.
   Deliberately doesn't lock scroll with `overflow: hidden` on html/body while the loader is up -
   .page-loader below already covers the entire viewport (position: fixed; inset: 0) with an opaque
   background, so nothing behind it is visible anyway, and overflow:hidden removes the scrollbar's
   reserved width outright (confirmed empirically - scrollbar-gutter: stable does NOT keep it
   reserved through an overflow:hidden toggle). That's what caused the whole page to visibly jump
   sideways by the scrollbar's width the instant the loader finished and overflow was restored. */
.page-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 28px;
  background: #ffffff;
  transition: opacity 0.4s ease;
}
html.loading .page-loader { display: flex; }
.page-loader.loader-hide { opacity: 0; pointer-events: none; }

.loader-ring-wrap { position: relative; width: 232px; height: 232px; }
.loader-ring { position: absolute; inset: 0; transform: rotate(-90deg); }
.loader-ring-track { fill: none; stroke: color-mix(in srgb, var(--accent) 16%, #ffffff); stroke-width: 6; }
.loader-ring-prog {
  fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round;
  stroke-dasharray: 290; stroke-dashoffset: 290;
  animation: loader-ring 2s ease forwards;
}
@keyframes loader-ring { 0% { stroke-dashoffset: 290; } 80%, 100% { stroke-dashoffset: 0; } }

.loader-stage { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; opacity: 0; }
.loader-ico { font-size: 84px; color: var(--accent); }
.loader-stage-product { animation: loader-product 2s ease forwards; }
@keyframes loader-product { 0% { opacity: 0; transform: scale(.7); } 5%, 26% { opacity: 1; transform: scale(1); } 34%, 100% { opacity: 0; transform: scale(1.12); } }
.loader-stage-cart { animation: loader-cart 2s ease forwards; }
@keyframes loader-cart { 0%, 30% { opacity: 0; transform: scale(.7); } 38%, 54% { opacity: 1; transform: scale(1); } 62%, 100% { opacity: 0; transform: scale(1.12); } }
.loader-stage-shield { animation: loader-shield 2s ease forwards; }
@keyframes loader-shield { 0%, 58% { opacity: 0; transform: scale(.72); } 68%, 100% { opacity: 1; transform: scale(1); } }
.loader-shield-svg { width: 152px; height: 152px; }
.loader-shield-body { fill: var(--accent); }
.loader-shield-check {
  /* Path length of "M23 37 L31 45 L47 28" is sqrt(128)+sqrt(545) =~ 34.66, not 30 - the old value
     was short of the real length, so the draw-on animation's dash/gap never lined up with the
     path's own start/end and a fragment of the second (tail) segment flashed into view before
     disappearing again, ahead of the checkmark actually drawing in. stroke-dasharray/dashoffset
     must equal the true path length for a clean single-pass draw. */
  fill: none; stroke: #ffffff; stroke-width: 6; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 34.7; stroke-dashoffset: 34.7;
  animation: loader-check 2s ease forwards;
}
@keyframes loader-check { 0%, 74% { stroke-dashoffset: 34.7; } 90%, 100% { stroke-dashoffset: 0; } }

.loader-wordmark { font-weight: 700; font-size: 40px; letter-spacing: -0.01em; color: var(--text); opacity: 0; animation: loader-word 2s ease forwards; }
.loader-wordmark span { color: var(--accent); }
@keyframes loader-word { 0%, 62% { opacity: 0; } 78%, 100% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .loader-ring-prog, .loader-stage-product, .loader-stage-cart, .loader-stage-shield,
  .loader-shield-check, .loader-wordmark {
    animation-duration: 0.01ms !important;
    animation-fill-mode: forwards !important;
  }
}

/* Admin portal shell (Views/Admin/_AdminLayout.cshtml) - a dedicated back-office console layout,
   entirely separate from the public site's header/nav/footer. Sidebar groups nav into labeled
   sections (Overview/Marketplace/Trust & Safety/People/Support/Reports) rather than the single
   flat button row it replaced (see the 2026-08-10 CHANGELOG entry). Built entirely from the
   existing rose/teal tokens so it re-themes for free, same as the rest of the site. */
.admin-shell {
  display: flex;
  min-height: 100vh;
  background: var(--bg-alt);
}

.admin-sidebar {
  width: 240px;
  flex: 0 0 240px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 24px 16px;
  background: var(--bg);
  border-right: 1px solid var(--border);
  overflow-x: hidden;
  overflow-y: auto;
  transition: width 0.2s ease, flex-basis 0.2s ease;
}

.admin-sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
}

.admin-sidebar-brand {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 8px;
  font-weight: 700;
  font-size: 17px;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
}

.admin-sidebar-brand:hover { color: var(--text); }
.admin-sidebar-brand .accent { color: var(--accent); }
.admin-sidebar-brand img { flex: 0 0 auto; }

.admin-sidebar-brand small {
  margin-left: 2px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  white-space: nowrap;
}

.admin-sidebar-toggle {
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.admin-sidebar-toggle:hover {
  background: var(--accent-bg);
  color: var(--accent);
}

.admin-sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 20px;
  flex: 1;
}

.admin-sidebar-section-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  padding: 0 8px;
  margin-bottom: 6px;
}

.admin-sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  transition: background 0.15s ease, color 0.15s ease;
}

.admin-sidebar-link i {
  font-size: 17px;
  color: var(--text-muted);
  transition: color 0.15s ease;
}

.admin-sidebar-link:hover {
  background: var(--accent-bg);
  color: var(--text);
}

.admin-sidebar-link.active {
  background: var(--accent);
  color: #ffffff;
  box-shadow: var(--shadow);
}

.admin-sidebar-link.active i,
.admin-sidebar-link:hover i {
  color: inherit;
}

.admin-sidebar-logout {
  padding: 0 8px;
}

.admin-main {
  /* The sole source of left/right breathing room for admin content - Views/Admin/_AdminLayout.cshtml
     wraps RenderBody() in a bare .wrap-wide (unlike every other page, which pairs it with .wrap:
     "wrap wrap-wide"), so it only ever gets .wrap-wide's own max-width:none, never .wrap's
     padding:0 24px. Left/right must match here, or content sits flush against one side - it
     previously had 20px left but 0 right, leaving the right edge flush against the browser window. */
  flex: 1;
  min-width: 0;
  padding: 24px 20px 48px;
}

.admin-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 0 24px 20px;
}

.admin-topbar h1 {
  margin: 0;
  font-size: 22px;
}

/* Collapsed sidebar - an icon-only rail. State lives on <html> (see _AdminLayout.cshtml's
   flash-prevention script in <head> and its toggle script before </body>), persisted via
   localStorage the same durable-preference way the rose/teal theme toggle already works. Scoped to
   >=768px only - below that the sidebar already stacks full-width above the content (see the
   existing mobile media query below), where an icon-only rail doesn't make sense. */
@media (min-width: 768px) {
  html.admin-sidebar-collapsed .admin-sidebar {
    width: 68px;
    flex-basis: 68px;
  }

  html.admin-sidebar-collapsed .admin-sidebar-brand span,
  html.admin-sidebar-collapsed .admin-sidebar-brand small,
  html.admin-sidebar-collapsed .admin-sidebar-section-label,
  html.admin-sidebar-collapsed .admin-sidebar-link span,
  html.admin-sidebar-collapsed .admin-sidebar-logout button span {
    display: none;
  }

  html.admin-sidebar-collapsed .admin-sidebar-header {
    flex-direction: column;
    gap: 12px;
  }

  html.admin-sidebar-collapsed .admin-sidebar-link,
  html.admin-sidebar-collapsed .admin-sidebar-logout button {
    justify-content: center;
  }
}

/* Below tablet width the sidebar stacks full-width above the content instead of squeezing into a
   narrow column - simplest robust behavior for a desktop-first admin console rather than a JS
   collapse toggle. */
@media (max-width: 767.98px) {
  .admin-shell {
    flex-direction: column;
  }

  .admin-sidebar {
    width: 100%;
    flex: 0 0 auto;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }

  /* The collapse toggle is desktop/tablet-only - below this width the sidebar already stacks
     full-width above the content, where an icon-only rail wouldn't save any real space. */
  .admin-sidebar-toggle {
    display: none;
  }

  .admin-sidebar-nav {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 12px;
  }

  .admin-sidebar-section {
    flex: 1 1 auto;
    min-width: 140px;
  }
}

/* Sections */

section {
  padding: 56px 0;
}

section.tight {
  padding: 40px 0;
}

section.alt {
  background: var(--bg-alt);
}

.section-head {
  text-align: center;
  max-width: 600px;
  margin: 0 auto 32px;
}

/* Manual pages (User/Dashboard/Moderator/Admin) - a sticky jump-link TOC beside a stack of
   card-per-section content, reused identically across all four portals' own layout/chrome so each
   manual reads the same way regardless of which site it's opened from. Colors all read the app's
   own --accent/--text tokens rather than a fixed palette, so the manual re-themes for free with the
   rose/teal toggle instead of clashing with whichever the reader has picked. */
.manual-layout {
  display: grid;
  /* minmax(0, 1fr), not a bare 1fr - a bare fr track has an implicit min-width:auto, so it won't
     shrink below its content's intrinsic width. The User and Dashboard manuals both have a <table>
     in their content column; without this, that table's min-content width stretches the whole grid
     wider than the viewport instead of the table's own .table-responsive wrapper taking the
     overflow, breaking mobile layout only on the pages that happen to contain a table (confirmed
     live: FAQ, which has none, never hit this). */
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
  max-width: 920px;
  margin: 0 auto;
}

.manual-toc {
  /* top clears .site-header's own sticky height (~81px, z-index:100) plus breathing room - the
     public _Layout is the only one of the four portal layouts with a sticky header, but this class
     is shared across all four manual pages, so the same offset is used everywhere rather than
     forking it per layout for a purely cosmetic difference on the other three. */
  position: sticky;
  top: 96px;
  padding: 20px;
}

.manual-toc-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  font-weight: 700;
  margin-bottom: 12px;
}

.manual-toc nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.manual-toc nav a {
  font-size: 13.5px;
  color: var(--text);
  text-decoration: none;
  padding: 6px 0 6px 11px;
  border-left: 2px solid var(--border);
  line-height: 1.35;
}

.manual-toc nav a:hover {
  color: var(--accent);
  border-left-color: var(--accent);
}

.manual-toc nav a.active {
  color: var(--accent);
  border-left-color: var(--accent);
  font-weight: 700;
}

.manual-intro {
  padding: 22px 26px;
  margin-bottom: 26px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.manual-intro .manual-version {
  font-size: 13px;
  color: var(--text-muted);
}

.manual-section {
  padding: 26px 28px;
  margin-bottom: 18px;
  scroll-margin-top: 20px;
}

.manual-section-icon {
  width: 38px;
  height: 38px;
  border-radius: 9px;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 14px;
  color: var(--accent);
  font-size: 18px;
  background: var(--accent-bg);
}

.manual-section h2 {
  font-size: 19px;
  margin: 0 0 12px;
}

.manual-section h3.manual-sub {
  font-size: 14.5px;
  margin: 18px 0 6px;
  font-weight: 700;
  color: var(--text);
}

.manual-section table {
  font-size: 13.5px;
}

@media (max-width: 860px) {
  .manual-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Categories/section links stay a plain vertical list at every width - the same base
     .manual-toc nav styling further up this file already applies here unchanged; this block used
     to switch to wrapped pills on mobile, which put several items side by side per row (reported
     live as untidy on the FAQ page's 9-category list) instead of reading top to bottom like the
     desktop sidebar does. */
  .manual-toc {
    position: static;
  }
}

/* Print stylesheet - exists so the manual pages (User/Dashboard/Moderator/Admin) can be rendered
   straight to PDF (headless Chromium print-to-pdf against the live page) as a clean standalone
   document instead of a screenshot of the whole site chrome. Strips the nav/footer/cookie banner
   and the manual's own "Download PDF" button (meaningless inside the PDF itself), forces a plain
   white/black palette regardless of theme, and stacks the TOC above the content instead of a fixed
   sidebar - the TOC's anchor links stay real, clickable in-document links in the resulting PDF. */
@media print {
  .site-header,
  .site-footer,
  #cookieConsentBanner,
  .manual-download-btn {
    display: none !important;
  }

  body {
    background: #ffffff;
    color: #111111;
  }

  .manual-layout {
    display: block;
  }

  .manual-toc {
    position: static;
    margin-bottom: 24px;
    box-shadow: none;
    border: 1px solid #ddd;
  }

  .manual-section,
  .manual-intro {
    box-shadow: none;
    border: 1px solid #ddd;
    break-inside: avoid;
  }

  /* .table-responsive's overflow-x:auto (Bootstrap default) clips anything past its own box width
     in print instead of wrapping - there's no scrollbar on paper to reveal the rest. Confirmed live:
     a long Dashboard Manual table cell got silently cut off mid-word. Printed tables should just
     wrap their cell text in full instead. */
  .table-responsive {
    overflow-x: visible;
  }
}

/* FAQ page - native <details>/<summary> per question (no JS needed, keyboard/screen-reader
   friendly for free) grouped into .manual-section cards by category, reusing the same
   .manual-layout/.manual-toc jump-list shell as the manual pages. */
.faq-list {
  display: flex;
  flex-direction: column;
}

.faq-item {
  border-bottom: 1px solid var(--border);
  padding: 13px 0;
}

.faq-item:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.faq-item summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 14.5px;
  color: var(--text);
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::after {
  content: "+";
  font-size: 19px;
  line-height: 1;
  color: var(--text-muted);
  flex: none;
}

.faq-item[open] summary::after {
  content: "\2212";
}

.faq-item[open] summary {
  color: var(--accent-dark);
}

.faq-item p {
  margin: 10px 0 0;
  font-size: 13.5px;
  color: var(--text-muted);
}

/* In-answer feature links ("My Offers", "Sell Something", etc.) - re-themes with the rose/teal
   toggle for free since it's the same var(--accent) every other themed control already uses. */
.faq-feature-link {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}

.faq-feature-link:hover {
  color: var(--accent-dark);
  text-decoration: underline;
}

/* Forms */

.form-label {
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
}

.form-control, .form-select {
  border: 1px solid var(--border);
  border-radius: 8px !important;
  padding: 10px 14px;
  font-size: 14px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.form-control:focus, .form-select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

.form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}

/* Soft status badges - color-coded via a small helper (StatusBadgeHelper) rather than
   Bootstrap's default flat grey .badge, so order/listing state reads at a glance. */

.badge-soft {
  display: inline-flex;
  align-items: center;
  padding: 5px 14px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.badge-soft-success { background: #eef4ec; color: #4f7052; }

/* Empty states (no purchases/listings/offers/reviews yet, etc.) - a soft accent-tinted icon circle
   over a muted message, reused everywhere a page would otherwise fall back to a bare <p> with no
   visual weight. */
.empty-state {
  text-align: center;
  padding: 40px 20px;
}

.empty-state-icon {
  width: 56px;
  height: 56px;
  margin: 0 auto 14px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
}

/* Verification chips (Option D - bordered chip + shield). One badge per user: "Email" once the
   email is confirmed, "Email + KYC" once identity is also verified. */
.verify-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 1px solid;
  background: transparent;
}
.verify-chip-email { color: var(--accent-dark); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.verify-chip-full { color: #2f7d5b; border-color: color-mix(in srgb, #2f7d5b 45%, transparent); }
.badge-soft-info { background: var(--accent-bg); color: var(--accent-dark); }
.badge-soft-warning { background: #fdf3e4; color: #a4692f; }
.badge-soft-danger { background: #fbe9ec; color: #c65d6e; }
.badge-soft-muted { background: var(--bg-alt); color: var(--text-muted); }

/* Listing cards */

.listing-card-img {
  height: 180px;
  object-fit: cover;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--bg-alt);
}

.listing-card-img-placeholder {
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  background: var(--bg-alt);
  border-radius: var(--radius) var(--radius) 0 0;
  font-size: 13px;
}

.price-tag {
  color: var(--accent);
  font-weight: 700;
}

/* "Post New Listing" CTA bar (Views/Listings/Index.cshtml) - always visible on Browse, whether
   filtered to a category or not. The button carries ViewBag.CategoryId straight through to
   Create's own categoryId param (see ListingsController.Create) so a seller browsing a specific
   category lands on the form with that category already selected. */
.post-listing-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 18px 22px;
  margin-bottom: 28px;
  background: var(--accent-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, var(--border));
  border-radius: var(--radius);
}

.post-listing-bar-text {
  display: flex;
  align-items: center;
  gap: 14px;
}

.post-listing-bar-icon {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: var(--bg);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  flex: 0 0 auto;
}

.post-listing-bar-title {
  font-weight: 700;
  font-size: 15px;
}

.post-listing-bar-sub {
  font-size: 13px;
}

/* Browse Listings' left-rail Categories card + the two-column layout it sits in (Views/Listings/
   Index.cshtml, via the CategoriesSidebar view component) - collapses to a single column with the
   card above the grid once there's no room for a real rail beside it. */
.listings-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  gap: 28px;
  align-items: start;
}

@media (max-width: 767px) {
  .listings-layout {
    grid-template-columns: 1fr;
  }
}

.categories-sidebar {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  background: var(--bg);
}

.categories-sidebar-head {
  background: linear-gradient(135deg, var(--accent), var(--accent-dark));
  color: #fff;
  padding: 13px 16px;
  font-weight: 700;
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.categories-sidebar-list {
  list-style: none;
  margin: 0;
  padding: 6px;
}

.categories-sidebar-list li + li {
  border-top: 1px solid var(--border);
}

.categories-sidebar-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 10px;
  border-radius: 8px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--accent-dark);
  text-decoration: none;
}

.categories-sidebar-list a:hover {
  background: var(--accent-bg);
}

.categories-sidebar-list a.is-active {
  background: var(--accent-bg);
  color: var(--accent-dark);
}

/* "Wanted" is requests, not items for sale, so it's pinned last (see
   CategoriesSidebarViewComponent) rather than competing for "most active" position - the light
   blue reinforces that it's a different kind of category, not just the bottom of a ranked list.
   Same fixed #0d6efd used for the "Sponsored" badge elsewhere on Browse Listings, so blue reads as
   one consistent "not an ordinary category/listing" signal site-wide rather than a one-off color. */
.categories-sidebar-list li.is-wanted-item {
  border-top-color: color-mix(in srgb, #0d6efd 20%, var(--border));
}

.categories-sidebar-list a.is-wanted {
  color: #0d6efd;
}

.categories-sidebar-list a.is-wanted:hover,
.categories-sidebar-list a.is-wanted.is-active {
  background: color-mix(in srgb, #0d6efd 12%, transparent);
  color: #0d6efd;
}

.categories-sidebar-list a.is-wanted .categories-sidebar-count {
  background: color-mix(in srgb, #0d6efd 14%, transparent);
  color: #0d6efd;
}

.categories-sidebar-list a.is-wanted.is-active .categories-sidebar-count {
  background: #fff;
}

.categories-sidebar-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-alt);
  color: var(--text-muted);
  font-size: 11.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.categories-sidebar-list a.is-active .categories-sidebar-count {
  background: #fff;
}

/* Sponsored (boosted listings) strip - a separate, clearly-labeled section above Browse's organic
   grid (see ListingsController.Index's ViewBag.SponsoredListings). Reuses _ListingCard as-is; the
   badge/tile treatment is layered on via a wrapping element rather than changing that shared partial.
   Fixed blue (#0d6efd) - not var(--accent) - so "Sponsored" reads as one consistent paid-placement
   signal under both the rose and teal themes rather than blending into whichever accent is active.
   Urgent below takes the fixed alert-red instead, so the two badges stay clearly distinct signals. */
.sponsored-strip {
  margin-bottom: 32px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--border);
}

.sponsored-strip-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

.sponsored-strip-head .eyebrow {
  color: #0d6efd;
}

/* Modernized tile treatment: a permanent soft blue ring + glow (not just on hover, unlike a plain
   listing card) so a sponsored placement reads as premium at a glance, intensifying on hover
   alongside the shared lift/shadow transition every .card already has.

   Tile-size hierarchy (Sponsored biggest, Urgent medium, organic listings smallest) is set by each
   grid's own row-cols-md-N in the view, not here - fewer columns per row means each .col (and the
   .card filling it) gets more of the row's width: _SponsoredStrip.cshtml uses row-cols-md-2,
   _UrgentStrip.cshtml row-cols-md-3, and the organic grids (Home/Index.cshtml's "Latest Listings",
   Listings/Index.cshtml's Browse grid) row-cols-md-4. */
.sponsored-wrap {
  position: relative;
}

.sponsored-wrap .card {
  position: relative;
  overflow: hidden;
  border-color: color-mix(in srgb, #0d6efd 28%, var(--border)) !important;
  box-shadow: 0 0 0 1px color-mix(in srgb, #0d6efd 12%, transparent), 0 0 10px 1px rgba(13, 110, 253, 0.28), var(--shadow);
}

.sponsored-wrap:hover .card {
  border-color: color-mix(in srgb, #0d6efd 55%, var(--border)) !important;
  box-shadow: 0 0 0 1px color-mix(in srgb, #0d6efd 25%, transparent), 0 0 16px 2px rgba(13, 110, 253, 0.45), var(--shadow-hover);
}

/* Glossy "magazine cover" shimmer - a diagonal light band that periodically sweeps the card,
   entering low-left and exiting high-right (the ~20deg gradient angle plus a horizontal-only
   translate is what makes a purely sideways sweep read as diagonal). Sits above the image/text
   (z-index 2, matching .sponsored-badge) but pointer-events:none so it never blocks clicks. The
   card's own overflow:hidden above clips it to the card's rounded corners. */
.sponsored-wrap .card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  background: linear-gradient(
    115deg,
    transparent 40%,
    rgba(255, 255, 255, 0.55) 48%,
    rgba(255, 255, 255, 0.85) 50%,
    rgba(255, 255, 255, 0.55) 52%,
    transparent 60%
  );
  transform: translateX(-140%);
  animation: sponsored-shimmer 4.5s ease-in-out infinite;
  pointer-events: none;
}

@keyframes sponsored-shimmer {
  0% { transform: translateX(-140%); }
  30%, 100% { transform: translateX(140%); }
}

@media (prefers-reduced-motion: reduce) {
  .sponsored-wrap .card::after {
    animation: none;
    display: none;
  }
}

.sponsored-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 11px 4px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: linear-gradient(135deg, #2f8fff, #0d6efd);
  color: #fff;
  box-shadow: 0 3px 10px rgba(13, 110, 253, 0.35);
}

.sponsored-badge i {
  font-size: 12px;
}

/* Urgent listings strip - same shape as .sponsored-strip/.sponsored-badge above, rendered directly
   beneath it on Home/Browse (see _UrgentStrip.cshtml). Fixed alert-red (#dc3545), matching the same
   red the Create/Edit fee icon and its "Mark Urgent" state already use - keeps "Urgent" reading as
   one consistent signal color across the whole site rather than a different shade in each spot. */
.urgent-strip {
  margin-bottom: 32px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--border);
}

.urgent-strip-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
}

.urgent-strip-head .eyebrow {
  color: #dc3545;
}

.urgent-wrap {
  position: relative;
}

.urgent-badge {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 2;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  background: #dc3545;
  color: #fff;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}

/* Info hover bubble (e.g. the commission-breakdown "i" next to Price on Create/Edit) - pure CSS
   show/hide via :hover/:focus, no JS needed, so it works identically wherever it's dropped in. */

.info-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #fdf3e4;
  color: #d97706;
  font-size: 16px;
  cursor: help;
  margin-left: 6px;
  vertical-align: middle;
}

/* Ticking the Urgent checkbox on Create/Edit switches this icon from orange to red, alongside the
   fee breakdown recalculating with CommissionTier.UrgentPercent instead of Percent (see
   listing-fee-calculator.js) - a plain hardcoded color like the base rule above, not a theme token,
   since it's a state indicator, not part of the rose/teal visual identity. Matches the same fixed
   alert-red (#dc3545) the Urgent badge/strip use, so "Urgent" reads as one consistent signal color
   everywhere it appears. */
.info-icon.is-urgent {
  background: #fbdbdf;
  color: #dc3545;
}

.info-icon .info-popover {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  margin-top: 8px;
  width: 320px;
  max-height: 380px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-hover);
  padding: 14px 16px;
  cursor: default;
  white-space: normal;
  text-align: left;
  font-weight: 400;
}

.info-icon:hover .info-popover,
.info-icon:focus .info-popover,
.info-icon:focus-within .info-popover {
  display: block;
}

.info-popover table {
  font-size: 12px;
}

/* Two full rate tables live in the popover (see _CommissionInfoIcon.cshtml) - only one shows at a
   time, matching whichever rate the live fee breakdown next to the icon is currently using. The
   icon's own .is-urgent class (toggled by listing-fee-calculator.js alongside its color) is what
   this reacts to, so no JS is needed inside the popover itself. */
.info-popover .urgent-rates-table {
  display: none;
}

.info-icon.is-urgent .info-popover .normal-rates-table {
  display: none;
}

.info-icon.is-urgent .info-popover .urgent-rates-table {
  display: table;
}

.info-popover th,
.info-popover td {
  padding: 3px 8px;
}

/* Tables */

.table {
  --bs-table-border-color: var(--border);
}

.table thead th {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border-bottom-width: 1px;
  font-weight: 600;
}

/* Every data table's cells must stay on one line - without this, a merely-average-length value
   (a long category name, "Kimberley, Northern Cape", a badly-localized price, a wrapped date/time)
   wraps onto 2-3 lines while its neighbouring cells stay single-line, making every row a different
   height and the whole table look "double-lined" and hard to scan. The card wrapping each table
   scrolls horizontally instead, so nowrap never clips content off the right edge. */
.table td, .table th {
  white-space: nowrap;
}

.card:has(> .table) {
  overflow-x: auto;
}

/* Alerts */

.alert {
  border-radius: var(--radius);
  border: 1px solid transparent;
}

.alert-info {
  background: var(--accent-bg);
  color: var(--accent-dark);
}

.alert-warning {
  background: #fdf3e4;
  color: #92601f;
}

.alert-success {
  background: #eef4ec;
  color: #4f7052;
}

.alert-danger {
  background: #fbe9ec;
  color: #a1445f;
}

/* Message thread */

.message-bubble {
  border-radius: 12px;
  padding: 10px 14px;
  margin-bottom: 10px;
  max-width: 80%;
}

.message-bubble.mine {
  background: var(--accent-bg);
  margin-left: auto;
}

.message-bubble.theirs {
  background: var(--bg-alt);
  border: 1px solid var(--border);
}

/* System notices in a thread (address confirmed/updated) - centered, not a bubble. */
.message-system {
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
  margin: 10px auto;
  max-width: 90%;
}

/* The message composer's input + Send button - plain flex with a gap instead of Bootstrap's
   .input-group, which welds the two into one seamless control (shared border, no gap) and reads as
   the input running straight into the button. */
.composer-row {
  display: flex;
  gap: 10px;
}

.composer-row .form-control {
  flex: 1;
}

/* Footer */

.site-footer {
  border-top: 1px solid var(--border);
  margin-top: 64px;
  font-size: 14px;
}

/* Single-line bar: one thin row (brand + nav), a hairline, then a centered copyright/TradeSafe
   line underneath - the most minimal option so far, plain type on the page's own surface with no
   color block of its own (unlike the two-tone split this replaced). */
.footer-bar-row1 {
  padding: 20px 0 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 28px;
  flex-wrap: wrap;
}

.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}

.footer-brand-text {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 15px;
  color: var(--text);
}

.footer-brand-text .accent {
  color: var(--accent);
}

.footer-bar-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 20px;
  font-size: 13px;
}

.footer-bar-nav a {
  color: var(--text);
  text-decoration: none;
}

.footer-bar-nav a:hover {
  color: var(--accent);
}

.footer-bar-row2 {
  border-top: 1px solid var(--border);
  padding: 14px 0 18px;
  text-align: center;
  font-size: 12px;
  color: var(--text-muted);
}

.footer-bar-row2 a {
  color: var(--text-muted);
  text-decoration: none;
}

.footer-bar-row2 a:hover {
  color: var(--accent);
}

.footer-bar-row2 .dot {
  padding: 0 8px;
}

@media (max-width: 767px) {
  .footer-bar-row1 {
    flex-direction: column;
    align-items: center;
  }
}

/* Cookie consent banner (wwwroot/js/cookie-consent.js) - fixed to the bottom rather than a modal,
   since it's asking permission for something optional (analytics), not blocking access to the
   page the way a hard consent wall would. Shown/hidden entirely by JS (display:none inline in the
   markup is the only initial state) - only rendered by _Layout.cshtml at all when GA4 is actually
   configured. */
.cookie-consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1050;
  background: var(--bg);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.08);
  padding: 16px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.cookie-consent-banner p {
  font-size: 14px;
  color: var(--text-muted);
  max-width: 640px;
}

/* Share button (Views/Listings/Details.cshtml) - sits right next to the H1, so it needs to read as
   a small secondary action rather than compete with the title; trimmed below Bootstrap's own
   .btn-sm padding/font-size. */
.listing-share-btn {
  padding: 4px 10px;
  font-size: 13px;
  line-height: 1.4;
}

/* Live stats dashboard (Views/Dashboard/) */

.dashboard-topbar {
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
  padding: 14px 0;
  margin-bottom: 16px;
}

.dashboard-live-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  margin-right: 4px;
  animation: dashboard-live-pulse 2s ease-in-out infinite;
}

@keyframes dashboard-live-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* Flat border by default so an alert tile visibly gains one, rather than every tile shifting size
   when .dashboard-tile-alert toggles on/off as the JS poll updates it. */
.dashboard-tile {
  border: 2px solid transparent;
}

.dashboard-tile.dashboard-tile-alert {
  border-color: var(--bs-danger, #dc3545);
}

.dashboard-tile.dashboard-tile-alert .price-tag {
  color: var(--bs-danger, #dc3545);
}

/* Brief highlight flash whenever the JS poll actually changes a tile's value - a quiet visual cue
   that this number just moved, without anything as disruptive as a full page reload. */
@keyframes dashboard-tile-flash {
  0% { background-color: var(--accent-bg); }
  100% { background-color: transparent; }
}

.dashboard-tile-pulse {
  animation: dashboard-tile-flash 900ms ease-out;
  border-radius: var(--radius, 8px);
}

/* Background job status dots (System Health section's job list) - solid, not the pulsing
   .dashboard-live-dot animation above, since 9 of these animating at once would be visual noise
   rather than a useful "is this alive" signal the way the single top-bar one is. */
.job-status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.job-status-dot.is-healthy { background: #4f7052; }
.job-status-dot.is-unhealthy { background: var(--bs-danger, #dc3545); }

/* Day/Month/Year range toggle (Views/Dashboard/Charts.cshtml). A pill-shaped track holding
   pill-shaped buttons, rather than Bootstrap's .btn-group (which squares off the middle button's
   corners) - this app's buttons are pill-shaped everywhere else, so a segmented .btn-group would
   have been the one place that looked like a different app. */
.segmented-control {
  display: inline-flex;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px;
  gap: 2px;
}

/* button (Dashboard/Charts' own JS-toggled filters) and a (Admin Reports' tabs - real navigation,
   a different URL per tab, no JS needed to drive the active state) share the same pill look. */
.segmented-control button,
.segmented-control a {
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 13.5px;
  font-weight: 600;
  padding: 7px 18px;
  border-radius: 999px;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  transition: background 0.2s ease, color 0.2s ease;
}

.segmented-control button:hover:not(.is-active),
.segmented-control a:hover:not(.is-active) {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--text);
}

.segmented-control button.is-active,
.segmented-control a.is-active {
  /* --type-accent is set inline per-button/link (Dashboard/Charts type filters, Admin Reports
     tabs - e.g. style="--type-accent:#38a169") so each business-domain pill lights up in its own
     color when selected instead of every pill turning the same generic site accent; Overview/
     Range/Chart colors/Layout buttons never set it, so they fall back to the ordinary accent
     unchanged. */
  background: var(--type-accent, var(--accent));
  color: #ffffff;
}

/* Small colored dot before a group/card heading (Dashboard's type-pure groups, Charts' cards) -
   the same 5 colors as the type-filter pills above, so a reader learns "green = Listings" once
   and it holds everywhere, not just while a filter is active. Falls back to the plain accent for
   anything that doesn't set --type-accent (there shouldn't be any, but this keeps a missed one
   visible instead of invisible). */
.type-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--type-accent, var(--accent));
  margin-right: 2px;
}

.chart-wrap {
  position: relative;
  height: 320px;
  transition: opacity 0.15s ease;
}

.chart-wrap.is-loading {
  opacity: 0.4;
}

/* Layout picker (Views/Dashboard/Charts.cshtml) - each dropdown item shows a tiny bar-count
   preview (1/2/3 vertical bars) instead of relying on the reader to picture what "3 columns"
   looks like from the word alone. */
.layout-preview {
  display: inline-flex;
  align-items: stretch;
  gap: 2px;
  width: 24px;
  height: 16px;
  flex-shrink: 0;
}

.layout-preview span {
  flex: 1;
  background: var(--accent);
  border-radius: 2px;
  opacity: 0.45;
}

.dropdown-item.layout-option:hover .layout-preview span,
.dropdown-item.layout-option.is-active .layout-preview span {
  opacity: 1;
}

.dropdown-item.layout-option.is-active {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--text);
  font-weight: 600;
}

/* Utility */

.text-center { text-align: center; }

/* Bootstrap 5 has no min-width utility (that's a Tailwind convention) - this exists because a flex
   item's default min-width is auto (its content's natural width), which silently defeats
   text-truncate on a child: the item just grows the row instead of shrinking and truncating. */
.min-w-0 { min-width: 0; }
