/* Copied from perk360-web/wwwroot/css/site.css so the console looks exactly
   like the app it manages. Two deployables, so two copies - when this file
   changes there, copy it again here rather than editing it in place.
   Console-only additions live in admin.css, which loads after this. */

html {
  font-size: 14px;
}

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

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

/* ===================== Theme tokens ===================== */
:root,
[data-bs-theme="light"] {
  --brand: #7C3AED;
  --brand-hover: #6425D6;
  --brand-active: #5417BF;
  --brand-light: #A78BFA;
  --brand-subtle: #F3EEFF;
  --brand-subtle-border: #E4D9FB;

  --surface: #FFFFFF;
  --surface-alt: #FAF9FD;
  --surface-raised: #FFFFFF;
  --text-body: #211D2C;
  --text-muted: #6B6478;
  --border-color: #E7E2F1;
  --shadow-color: rgba(124, 58, 237, 0.08);

  color-scheme: light;
}

[data-bs-theme="dark"] {
  --brand: #A78BFA;
  --brand-hover: #BBA3FC;
  --brand-active: #C9B6FD;
  --brand-light: #C4B5FD;
  --brand-subtle: #1C1626;
  --brand-subtle-border: #362A4D;

  --surface: #000000;
  --surface-alt: #121017;
  --surface-raised: #16131C;
  --text-body: #F1EDFB;
  --text-muted: #A79FC0;
  --border-color: #2A2438;
  --shadow-color: rgba(167, 139, 250, 0.12);

  color-scheme: dark;
}

/* ===================== Scale tokens ===================== */
:root {
  /* Spacing scale (rem) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.5rem;

  /* Radius scale - flattened to sharp, square corners app-wide (enterprise
     "box, no curves" look, matching perk360-mob-wallet's default style).
     Every rule below still reads through these tokens rather than a literal
     0, so restoring rounded corners later is a one-line change here. */
  --radius-sm: 0;
  --radius-md: 0;
  --radius-lg: 0;
  --radius-pill: 0;

  /* Bootstrap's own corner-radius tokens - drives every stock Bootstrap
     component (.card, .modal, .dropdown-menu, .alert, .form-control,
     .btn, .list-group, .pagination, .toast, .progress, .tooltip,
     .popover, .accordion, .offcanvas, etc.) that isn't already covered by
     the custom tokens above. */
  --bs-border-radius: 0;
  --bs-border-radius-sm: 0;
  --bs-border-radius-lg: 0;
  --bs-border-radius-xl: 0;
  --bs-border-radius-xxl: 0;
  --bs-border-radius-2xl: 0;
  --bs-border-radius-pill: 0;

  /* Shadow scale - tinted via --shadow-color, which is already redefined
     per theme above, so these need no dark-mode duplication. */
  --shadow-sm: 0 1px 3px var(--shadow-color);
  --shadow-md: 0 8px 24px var(--shadow-color);
  --shadow-lg: 0 12px 32px var(--shadow-color);
}

/* ===================== Base ===================== */
body {
  background-color: var(--surface-alt);
  color: var(--text-body);
  font-family: "Segoe UI Variable", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
}

h1 {
  font-size: 1.45rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  margin-bottom: 0.35rem;
}

h1 + p.text-muted {
  margin-top: -0.1rem;
}

/* :not(.h1..h6) guards every rule below - Bootstrap's own heading-size
   utility classes (e.g. <h2 class="h5 card-title">, used ~18 places in this
   app to give a heading a different visual size than its semantic level)
   have equal selector specificity to a plain element selector, so without
   this guard whichever rule loads later in the cascade would win regardless
   of intent. site.css loads after bootstrap.min.css, so leaving this out
   would silently override every "h2 sized as h5" case in the app. */
h2:not(.h1):not(.h2):not(.h3):not(.h4):not(.h5):not(.h6) {
  font-size: 1.2rem;
  font-weight: 650;
  letter-spacing: -0.005em;
  margin-bottom: 0.5rem;
}

h3:not(.h1):not(.h2):not(.h3):not(.h4):not(.h5):not(.h6) {
  font-size: 1.05rem;
  font-weight: 650;
  margin-bottom: 0.5rem;
}

h4:not(.h1):not(.h2):not(.h3):not(.h4):not(.h5):not(.h6),
h5:not(.h1):not(.h2):not(.h3):not(.h4):not(.h5):not(.h6),
h6:not(.h1):not(.h2):not(.h3):not(.h4):not(.h5):not(.h6) {
  font-size: 0.95rem;
  font-weight: 650;
  margin-bottom: 0.5rem;
}

/* Shared page-header convention (Views/Shared/_PageHeader.cshtml and the
   documented inline .page-header flex pattern for pages needing an action
   button or rich subtitle markup) - both share this class so spacing stays
   identical regardless of which form a page uses. */
.page-header h1 {
  margin-bottom: 0.35rem;
}

.page-header-title-suffix {
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--text-muted);
  margin-left: 0.35rem;
}

a {
  color: var(--brand);
}
a:hover {
  color: var(--brand-hover);
}

::selection {
  background-color: var(--brand-light);
  color: #1a0a3d;
}

/* ===================== Navbar ===================== */
.navbar {
  background-color: var(--surface) !important;
  border-bottom: 1px solid var(--border-color) !important;
  box-shadow: 0 2px 12px var(--shadow-color);
}

.navbar-brand {
  font-weight: 700;
  letter-spacing: -0.01em;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.navbar .nav-link {
  color: var(--text-body) !important;
  font-weight: 500;
  transition: color 0.15s ease;
}

.navbar .nav-link:hover,
.navbar .nav-link:focus {
  color: var(--brand) !important;
}

.navbar .nav-link.active {
  color: var(--brand) !important;
}

.navbar-toggler {
  border-color: var(--border-color);
}

.theme-toggle {
  border: 1px solid var(--border-color);
  background: var(--surface-alt);
  color: var(--text-body);
  border-radius: var(--radius-pill);
  width: 2.25rem;
  height: 2.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.theme-toggle:hover {
  border-color: var(--brand);
  color: var(--brand);
}

.theme-toggle .icon-dark { display: none; }
[data-bs-theme="dark"] .theme-toggle .icon-light { display: none; }
[data-bs-theme="dark"] .theme-toggle .icon-dark { display: inline; }

/* ===================== Buttons ===================== */
.btn-primary {
  --bs-btn-bg: var(--brand);
  --bs-btn-border-color: var(--brand);
  --bs-btn-hover-bg: var(--brand-hover);
  --bs-btn-hover-border-color: var(--brand-hover);
  --bs-btn-active-bg: var(--brand-active);
  --bs-btn-active-border-color: var(--brand-active);
  --bs-btn-disabled-bg: var(--brand);
  --bs-btn-disabled-border-color: var(--brand);
  --bs-btn-color: #ffffff;
  --bs-btn-hover-color: #ffffff;
  --bs-btn-active-color: #ffffff;
}

.btn-outline-secondary {
  --bs-btn-color: var(--text-body);
  --bs-btn-border-color: var(--border-color);
  --bs-btn-hover-bg: var(--brand-subtle);
  --bs-btn-hover-border-color: var(--brand);
  --bs-btn-hover-color: var(--brand);
}

.btn-link {
  color: var(--brand);
}
.btn-link:hover {
  color: var(--brand-hover);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus,
.form-control:focus, .form-check-input:focus, .form-select:focus {
  box-shadow: 0 0 0 0.15rem var(--surface), 0 0 0 0.3rem var(--brand-light);
}

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

/* ===================== Surfaces: cards, tables, forms ===================== */
.card {
  background-color: var(--surface-raised);
  border-color: var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.table {
  --bs-table-color: var(--text-body);
  --bs-table-bg: transparent;
  --bs-table-border-color: var(--border-color);
}

.table > thead {
  --bs-table-color: var(--text-muted);
}

.table-hover > tbody > tr:hover > * {
  --bs-table-accent-bg: var(--brand-subtle);
  color: var(--text-body);
}

.form-label {
  font-weight: 500;
  color: var(--text-body);
}

.form-text, .text-muted {
  color: var(--text-muted) !important;
}

.breadcrumb {
  background-color: var(--brand-subtle);
  border: 1px solid var(--brand-subtle-border);
  border-radius: 0;
  padding: 0.5rem 1rem;
}

.breadcrumb-item.active {
  color: var(--text-muted);
}

.breadcrumb-item a {
  color: var(--brand);
  text-decoration: none;
}
.breadcrumb-item a:hover {
  text-decoration: underline;
}

.badge.bg-primary {
  background-color: var(--brand) !important;
}

.alert-danger {
  --bs-alert-bg: color-mix(in srgb, #dc3545 12%, var(--surface));
  --bs-alert-border-color: color-mix(in srgb, #dc3545 35%, var(--surface));
}

.alert-success {
  --bs-alert-bg: color-mix(in srgb, #198754 12%, var(--surface));
  --bs-alert-border-color: color-mix(in srgb, #198754 35%, var(--surface));
}

.alert-warning {
  --bs-alert-bg: color-mix(in srgb, #ffc107 15%, var(--surface));
  --bs-alert-border-color: color-mix(in srgb, #ffc107 35%, var(--surface));
}

/* ===================== Footer ===================== */
.footer {
  background-color: var(--surface);
  border-color: var(--border-color) !important;
  color: var(--text-muted);
}

/* ===================== Auth (Login) card ===================== */
.auth-wrapper {
  min-height: calc(100vh - 220px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 0;
}

.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--surface-raised);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-lg);
  padding: var(--space-7);
  box-shadow: var(--shadow-lg);
}

.auth-card .auth-brand {
  font-weight: 700;
  font-size: 1.5rem;
  text-align: center;
  margin-bottom: 0.25rem;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.auth-card .auth-subtitle {
  text-align: center;
  color: var(--text-muted);
  margin-bottom: 1.75rem;
  font-size: 0.95rem;
}

/* ===================== App shell ===================== */
.app-body {
  min-height: 100vh;
}

.app-sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: 250px;
  background: var(--surface);
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  z-index: 1040;
  overflow-y: auto;
  transition: transform 0.2s ease;
  /* The nav still scrolls on a short window, but its scrollbar stays out
     of the way until the pointer is in the sidebar - two permanent
     scrollbars side by side (nav + page) read as a cramped, broken frame,
     especially in the desktop app's smaller window. */
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
}

.app-sidebar:hover,
.app-sidebar:focus-within {
  scrollbar-color: var(--border-color) transparent;
}

.app-sidebar::-webkit-scrollbar {
  width: 6px;
}

.app-sidebar::-webkit-scrollbar-thumb {
  background: transparent;
  border-radius: 3px;
}

.app-sidebar:hover::-webkit-scrollbar-thumb,
.app-sidebar:focus-within::-webkit-scrollbar-thumb {
  background: var(--border-color);
}

.app-brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1rem 1.15rem;
  text-decoration: none;
}

.app-brand-mark {
  width: 2rem;
  height: 2rem;
  border-radius: 0;
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
}

.app-brand-mark img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.app-brand-text {
  font-weight: 700;
  letter-spacing: -0.01em;
  font-size: 1.05rem;
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  white-space: nowrap;
}

.app-nav {
  padding: 0.25rem 0.75rem 1.5rem;
}

.app-nav-section {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  padding: 1.1rem 0.5rem 0.35rem;
}

.app-nav-link {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.5rem 0.65rem;
  margin-bottom: 2px;
  border-radius: var(--radius-sm);
  color: var(--text-body);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 500;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.app-nav-link svg {
  flex-shrink: 0;
  opacity: 0.75;
}

.app-nav-link:hover {
  background: var(--brand-subtle);
  color: var(--brand);
}

.app-nav-link.active {
  background: var(--brand-subtle);
  color: var(--brand);
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--brand);
}

.app-nav-link.active svg {
  opacity: 1;
}

.app-main {
  margin-left: 250px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.app-topbar {
  position: sticky;
  top: 0;
  z-index: 1030;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 1.5rem;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border-color);
}

.app-sidebar-toggle {
  border: 1px solid var(--border-color);
  background: var(--surface-alt);
  color: var(--text-body);
  border-radius: 0;
  width: 2.25rem;
  height: 2.25rem;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 0;
}

/* Was hidden below md (d-none d-md-flex) so the "Managing: X / Stop"
   impersonation indicator never reached phones at all - wrapping here
   instead of hiding lets it drop to its own right-aligned row when the
   topbar is too narrow for everything on one line, rather than disappearing
   or overflowing. */
.app-topbar-actions {
  flex-wrap: wrap;
  row-gap: 0.5rem;
  justify-content: flex-end;
}

.app-user {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.app-avatar {
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%);
  color: #fff;
  font-size: 0.85rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.app-user-name {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--bs-body-color);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Group / role badge shown beside the user's name in the top bar. */
.app-user-badge {
  background: color-mix(in srgb, var(--brand) 14%, transparent);
  color: var(--brand);
  border: 1px solid color-mix(in srgb, var(--brand) 30%, transparent);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.app-content {
  flex: 1;
  padding: 1.75rem 1.5rem 2.5rem;
  max-width: 1320px;
  width: 100%;
}

/* Without the sidebar (login, logged-out home) the capped content block
   would hug the left edge on wide screens - center it instead. */
body:not(.app-body) .app-content {
  margin-inline: auto;
}

.app-footer {
  padding: 1rem 1.5rem;
  border-top: 1px solid var(--border-color);
  color: var(--text-muted);
  font-size: 0.82rem;
  background: var(--surface);
}

.app-sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 1035;
}

@media (max-width: 991.98px) {
  .app-sidebar {
    transform: translateX(-100%);
  }
  .app-sidebar.open {
    transform: translateX(0);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.25);
  }
  .app-sidebar-backdrop.show {
    display: block;
  }
  .app-main {
    margin-left: 0;
  }
  .app-sidebar-toggle {
    display: inline-flex;
  }
}

/* ===================== Enterprise polish ===================== */
.btn {
  border-radius: var(--radius-sm);
}

/* Equal-width segmented mode pickers (Checker's Campaign/Promotion/Perk,
   Raffle's Merge entries, Void's Customer/Campaign/Promotion/Perk) -
   Bootstrap's .btn-group sizes each button to its own label by default,
   which looks uneven when the labels are different lengths. */
.btn-group[role="group"] > .btn {
  flex: 1 1 0;
}

/* Icon-only row-action buttons (table rows: Manage/Clone/Delete/etc.) */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  line-height: 1;
}
.btn-icon svg {
  flex-shrink: 0;
}

.form-control, .form-select {
  border-radius: var(--radius-sm);
  background-color: var(--surface);
  border-color: var(--border-color);
}

/* Tables: wrap in a raised panel with quiet headers */
.app-content .table {
  background: var(--surface-raised);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.app-content .table > thead th {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  background: var(--surface-alt);
  border-bottom: 1px solid var(--border-color);
  padding: 0.65rem 0.9rem;
}

.app-content .table > tbody td {
  padding: 0.7rem 0.9rem;
  border-color: var(--border-color);
  vertical-align: middle;
}

.app-content .table > tbody > tr:last-child > td {
  border-bottom: 0;
}

/* Softened status badges */
.badge {
  font-weight: 600;
  padding: 0.35em 0.7em;
  border-radius: var(--radius-pill);
}

.badge.bg-success {
  background-color: color-mix(in srgb, #198754 16%, var(--surface)) !important;
  color: color-mix(in srgb, #198754 80%, var(--text-body)) !important;
  border: 1px solid color-mix(in srgb, #198754 35%, var(--surface));
}

.badge.bg-danger {
  background-color: color-mix(in srgb, #dc3545 14%, var(--surface)) !important;
  color: color-mix(in srgb, #dc3545 82%, var(--text-body)) !important;
  border: 1px solid color-mix(in srgb, #dc3545 35%, var(--surface));
}

.badge.bg-warning {
  background-color: color-mix(in srgb, #ffc107 20%, var(--surface)) !important;
  color: color-mix(in srgb, #b07d00 90%, var(--text-body)) !important;
  border: 1px solid color-mix(in srgb, #ffc107 45%, var(--surface));
}

.badge.bg-secondary {
  background-color: var(--surface-alt) !important;
  color: var(--text-muted) !important;
  border: 1px solid var(--border-color);
}

.badge.bg-primary {
  background-color: var(--brand-subtle) !important;
  color: var(--brand) !important;
  border: 1px solid var(--brand-subtle-border);
}

/* List toolbar */
.list-toolbar {
  background: var(--surface-raised);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 0.6rem 0.9rem;
  margin-bottom: 0.75rem !important;
}

/* Home dashboard tiles */
.home-tile {
  color: var(--text-body);
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.home-tile:hover {
  border-color: var(--brand);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  color: var(--text-body);
}

.home-tile-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 0;
  background: var(--brand-subtle);
  border: 1px solid var(--brand-subtle-border);
  color: var(--brand);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.75rem;
}

/* Nav tabs (checker entry methods) */
.nav-tabs .nav-link {
  color: var(--text-muted);
  font-weight: 500;
}

.nav-tabs .nav-link.active {
  color: var(--brand);
  font-weight: 600;
}

/* ===================== Misc ===================== */
.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;
}

@media print {
  .app-sidebar, .app-topbar, .app-footer, .theme-toggle {
    display: none !important;
  }
  .app-main {
    margin-left: 0;
  }
}

/* ===================== Mobile friendliness ===================== */

/* Page/card header toolbars (title + action button, or card-header with a
   button) wrap instead of squeezing onto one line on narrow screens. */
.d-flex.justify-content-between.align-items-center {
  flex-wrap: wrap;
  row-gap: 0.5rem;
}

/* Dynamic form rows (criteria builder rows, qualified-list upload row)
   wrap instead of overflowing the viewport horizontally. */
.criteria-row .d-flex,
.qualified-list-picker .d-flex {
  flex-wrap: wrap;
  row-gap: 0.35rem;
}

/* Alternating background per criterion - a .criteria-row already wraps both
   the selector line and that same criterion's API-parameters panel as one
   unit, so tinting every other row makes the grouping obvious without
   touching any markup. Same --brand-subtle token .table-striped already
   uses for zebra striping elsewhere, for a consistent look - plus a left
   border accent, since --brand-subtle's dark-theme value is close enough
   to the card background that the fill alone doesn't read clearly there. */
.criteria-row {
  padding: var(--space-2);
  border-left: 3px solid transparent;
}
.criteria-row:nth-child(even) {
  background-color: var(--brand-subtle);
  border-left-color: var(--brand-subtle-border);
}

/* ===================== API integration builder rows ===================== */
/* Grid (not flex) so every row's columns share the same width regardless of
   that row's own content - a flex <select> without a fixed width sizes
   itself to its currently-selected option's text, so e.g. "String" vs
   "Boolean" or "Single value" vs "Comma-delimited list" made same-column
   dropdowns different widths from row to row. */

/* Name/value pairs: header rows in both API integration builders. */
.header-row,
.api-header-row {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) minmax(160px, 1fr) auto;
  gap: 0.35rem;
  align-items: center;
}

/* Request body field rows (Customer API Integration): Key, Value, Type. */
.body-field-row {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) minmax(160px, 1.3fr) 110px auto;
  gap: 0.35rem;
  align-items: center;
}

/* Request body parameter rows (Criterion Data Source): Name, Type, Match. */
.api-param-row {
  display: grid;
  grid-template-columns: minmax(140px, 1fr) 170px 120px auto;
  gap: 0.35rem;
  align-items: center;
}

/* Response parameter mapping rows: Field -> Target. */
.response-param-row {
  display: grid;
  grid-template-columns: minmax(160px, 1fr) auto minmax(180px, 230px) auto;
  gap: 0.35rem;
  align-items: center;
}

@media (max-width: 575.98px) {
  .header-row,
  .api-header-row,
  .body-field-row,
  .api-param-row,
  .response-param-row {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 575.98px) {
  .app-topbar {
    padding: 0.6rem 1rem;
  }
  .app-content {
    padding: 1.25rem 1rem 2rem;
  }
  .app-footer {
    padding: 1rem;
  }
  .auth-card {
    padding: 1.75rem 1.25rem;
  }
  /* Criteria-row selects have a fixed max-width for desktop; let them and
     the value input take the full row width once wrapped on narrow phones. */
  .criteria-row .criteria-datasource,
  .criteria-row .criteria-operator,
  .criteria-row .criteria-value-container {
    max-width: 100% !important;
    flex: 1 1 100%;
  }

  /* Bootstrap's .btn-group is inline-flex with no wrap, so a group with
     enough buttons (e.g. Void's 4-way Customer/Campaign/Promotion/Perk
     mode picker) overflows its card on narrow phones instead of shrinking.
     Letting it wrap to a second row keeps every button fully visible and
     tappable instead of clipped past the card edge. */
  .btn-group[role="group"] {
    flex-wrap: wrap;
  }
  .btn-group[role="group"] > .btn {
    flex: 1 1 auto;
  }
}

/* Global flash-message toasts (see wwwroot/js/toast.js) - a thin bar along
   the bottom that shrinks over the same duration as the auto-hide delay, so
   the toast visibly counts down instead of just vanishing abruptly. */
.toast-progress {
  height: 3px;
  background: rgba(255, 255, 255, .7);
  width: 100%;
  animation-name: toast-progress-shrink;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

.toast.text-bg-warning .toast-progress {
  background: rgba(0, 0, 0, .35);
}

@keyframes toast-progress-shrink {
  from { width: 100%; }
  to { width: 0%; }
}

/* ===================== Utility classes (style= reduction) =====================
   Named for what they constrain, not their pixel value, so they're reusable
   across pages instead of each page repeating its own inline style="...". */
.field-w-2xs { max-width: 90px; }    /* e.g. floor plan scale-percent input */
.field-w-xs  { max-width: 7rem; }    /* e.g. width/height mm inputs */
.field-w-sm2 { max-width: 140px; }   /* e.g. floor plan seat-assignment customer id input */
.field-w-sm  { max-width: 200px; }   /* e.g. award/deduct trigger selects */
.field-w-md  { max-width: 280px; }   /* e.g. list search boxes */
.field-w-lg  { max-width: 20rem; }   /* e.g. balance-field inputs */
.content-w-md { max-width: 760px; }  /* e.g. long-form text pages (Privacy) */
.label-w-fixed { min-width: 10rem; } /* fixed-width labels beside an inline control */
.scroll-pane-sm { max-height: 320px; overflow: auto; white-space: pre-wrap; } /* API test-response panes */
.logo-preview-sm { max-height: 80px; max-width: 240px; } /* tenant brand logo preview */
.thumb-120 { width: 120px; height: 120px; object-fit: cover; } /* Campaign/Promotion/Perk/Tenant image upload thumbnail */
.dropdown-w-md { min-width: 220px; }
.dropdown-menu-w-md { min-width: 260px; max-height: 320px; overflow-y: auto; }
.min-w-0 { min-width: 0; } /* Bootstrap flex-child overflow fix */
.text-brand-gradient {
  /* Same gradient-text technique as .auth-card .auth-brand, reused for the
     anonymous home hero's "Perk360" heading. */
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-light) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.chart-frame { position: relative; width: 100%; height: var(--chart-h, 260px); }
.voucher-preview { font-family: "Consolas", monospace; font-size: 0.875rem; }

/* Claim/Redeem Access checkbox tree (_PerkAccessFields.cshtml): the
   Location→Device expand toggle needs a real hit target and visible
   affordance - a bare unicode triangle at 14px reads as a stray pixel,
   not a control, against either theme. */
.perk-access-tree-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 4px;
  /* A solid brand fill, not a subtle tint - --surface-alt/--brand-subtle
     sit too close to the page's own background in dark mode for a 22px
     control to read as a button at all rather than a stray pixel. */
  background: var(--brand);
  color: #fff;
  cursor: pointer;
}
.perk-access-tree-toggle:hover { background: var(--brand-hover); }
.perk-access-tree-chevron { transition: transform .15s ease; }
.perk-access-tree-toggle[aria-expanded="true"] .perk-access-tree-chevron { transform: rotate(90deg); }
.perk-access-tree-toggle-spacer { display: inline-block; width: 1.375rem; height: 1.375rem; flex: none; }
/* Bootstrap's .form-check-input relies on a negative margin-left to pull
   the checkbox back under its label's reserved indent - fine for a plain
   label+checkbox pair, but it drags the checkbox left on top of the
   toggle button sitting before it in this flex row. Neutralize it here;
   the toggle/checkbox/label spacing already comes from the row's gap. */
.perk-access-tree-node .form-check-input { margin-left: 0; }

/* ===================== Loading overlay ===================== */
/* Full-page veil shown while a navigation or form post is in flight - see
   wwwroot/js/loading-overlay.js. Fixed and above every Bootstrap layer
   (modal is 1055) so it also covers a modal that submitted a form. */
.app-loading-overlay {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: blur(2px);
  /* Faded in on a short delay instead of instantly: a fast local navigation
     finishes before the veil is ever visible, so quick pages don't flash. */
  opacity: 0;
  transition: opacity .2s ease;
  pointer-events: all;
}
.app-loading-overlay.is-visible { opacity: 1; }
.app-loading-spinner {
  width: 2.5rem;
  height: 2.5rem;
  border: 3px solid var(--brand-subtle-border);
  border-top-color: var(--brand);
  border-radius: 50%;
  animation: app-loading-spin .7s linear infinite;
}
.app-loading-text {
  font-size: 0.875rem;
  color: var(--text-muted);
}
@keyframes app-loading-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .app-loading-overlay { transition: none; }
  .app-loading-spinner { animation-duration: 2s; }
}

/* ===================== Phone layout ===================== */
@media (max-width: 767.98px) {
  /* List tables scroll sideways on a phone, which pushed each row's action
     buttons (edit / copy / deactivate) off the right edge with nothing to
     say they were there. Pin that column to the right instead: the columns
     in between still scroll, the actions never leave the screen. Keyed on
     the icon buttons themselves, so every list with them gets this without
     per-page markup; the matching header cell is pinned too so the column
     reads as one piece. */
  /* The site-wide table style sets overflow: hidden, which makes the table
     itself the box a sticky cell sticks to - and the table never scrolls, so
     the pin had no effect. The .table-responsive wrapper does the scrolling. */
  .table-responsive table:has(td:last-child .btn-icon) {
    overflow: visible;
  }

  .table-responsive td:last-child:has(.btn-icon),
  .table-responsive table:has(td:last-child .btn-icon) thead th:last-child {
    position: sticky;
    right: 0;
    z-index: 1;
    background-color: var(--surface);
    white-space: nowrap;
    /* Inset rather than a border: borders on sticky cells in a collapsed
       table don't travel with the cell. */
    box-shadow: inset 1px 0 0 var(--border-color);
  }
}

@media (max-width: 575.98px) {
  /* Tabs scroll sideways in one line rather than wrapping onto a second
     row, where the active tab's underline no longer sits on the tab strip.
     The last tab shows cut off at the edge, which is the cue to swipe. */
  .nav-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
  }

  .nav-tabs .nav-link {
    white-space: nowrap;
  }
}
