/* Hybrid Celebrate (js/celebrate.js) - full-viewport, never intercepts a
   click, sits above everything including open modals. */
#celebrate-canvas { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 500; }

:root {
  --green-dark: #184f2a;
  --green: #2f7a44;
  --green-light: #eaf5e8;
  --green-soft: #f4faf3;
  --white: rgba(255, 255, 255, 0.94);
  --white-soft: rgba(255, 255, 255, 0.82);
  --blue-dark: #244b63;
  --text: #26332a;
  --muted: #66736a;
  --line: rgba(24, 79, 42, 0.14);
  --shadow: 0 12px 28px rgba(24, 79, 42, 0.08);
  --radius: 10px;
  --radius-sm: 7px;
}

* { box-sizing: border-box; }

/* Without this, position:fixed elements that are deliberately translated
   off-screen (the slide-in basket panel when closed) or decorative
   background shapes that bleed past the edge still count toward the page's
   scrollable width, since overflow-x on body alone doesn't clip fixed
   descendants - only html does. */
html {
  overflow-x: hidden;
}

body {
  margin: 0;
  font-family: Arial, Helvetica, sans-serif;
  color: var(--text);
  line-height: 1.42;
  min-height: 100vh;
  position: relative;
  /* overflow-x:hidden lives on <html> only, not here too - setting it on
     BOTH makes overflow-y compute to "auto" on both per the CSS overflow
     spec's cross-axis rule (an explicit non-visible overflow-x forces the
     other axis off "visible"), creating two candidate scroll containers
     even though only <html> actually scrolls. position:sticky descendants
     ended up resolving against <body> (the nearer ancestor, but the one
     that never actually receives scroll deltas) and never stuck. */
  background: #f6faf5;
}

body::before {
  content: "";
  position: fixed;
  inset: 0;
  background:
    linear-gradient(120deg, rgba(255, 255, 255, 0.86), rgba(234, 245, 232, 0.82)),
    radial-gradient(circle at top left, rgba(47, 122, 68, 0.12), transparent 30%),
    radial-gradient(circle at top right, rgba(36, 75, 99, 0.08), transparent 26%),
    url("snack-wrapper-background.png");
  background-size: cover;
  background-position: center;
  z-index: -2;
}

body::after {
  content: "";
  position: fixed;
  inset: 0;
  background:
    repeating-linear-gradient(110deg, rgba(47, 122, 68, 0.03) 0 2px, transparent 2px 18px);
  z-index: -1;
  pointer-events: none;
}

.page {
  width: min(1040px, calc(100% - 28px));
  margin: 24px auto;
}

/* index.html only: a little extra width so the snack gallery can run 3
   larger columns alongside the sticky cart sidebar without either shrinking
   below what the standard 1040px page allowed. Also the positioning
   context for #cashback-notifications-bell below this width - see there
   for why. */
.page-wide {
  width: min(1280px, calc(100% - 28px));
  position: relative;
}

/* Keep customer/profile controls out of view until Firebase Auth and the
   effective linked profile have both resolved. This prevents blank or stale
   fields from flashing briefly before the correct profile is rendered. */
body.auth-pending #page-body,
body.auth-failed #page-body {
  display: none;
}

.auth-loader {
  min-height: 330px;
  margin-top: 18px;
  padding: 58px 28px 42px;
  border: 1px solid var(--line);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow);
  display: none;
  place-items: center;
  align-content: center;
  gap: 18px;
  text-align: center;
}

body.auth-pending .auth-loader {
  display: grid;
}

.auth-loader-mark {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 28px;
}

.auth-loader-mark span {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--green);
  animation: auth-loader-rise 1s ease-in-out infinite;
}

.auth-loader-mark span:nth-child(2) { animation-delay: 0.14s; }
.auth-loader-mark span:nth-child(3) { animation-delay: 0.28s; }

.auth-loader-copy h1 {
  margin: 0 0 5px;
  color: var(--green-dark);
  font-size: clamp(1.3rem, 3vw, 1.75rem);
  font-weight: 600;
}

.auth-loader-copy p {
  margin: 0;
  color: var(--muted);
}

.auth-loader-skeleton {
  width: min(410px, 88vw);
  display: grid;
  grid-template-columns: 42px 1fr;
  gap: 9px 13px;
  align-items: center;
  margin-top: 10px;
}

.auth-skeleton-avatar,
.auth-skeleton-line {
  display: block;
  background: linear-gradient(100deg, #e9f1e9 25%, #f7faf7 45%, #e9f1e9 65%);
  background-size: 220% 100%;
  animation: auth-skeleton-shimmer 1.35s linear infinite;
}

.auth-skeleton-avatar {
  grid-row: 1 / span 2;
  width: 42px;
  height: 42px;
  border-radius: 50%;
}

.auth-skeleton-line {
  width: 62%;
  height: 9px;
  border-radius: 6px;
}

.auth-skeleton-line-long {
  width: 100%;
}

@keyframes auth-loader-rise {
  0%, 60%, 100% { transform: translateY(0); opacity: 0.45; }
  30% { transform: translateY(-8px); opacity: 1; }
}

@keyframes auth-skeleton-shimmer {
  from { background-position: 100% 0; }
  to { background-position: -120% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-loader-mark span,
  .auth-skeleton-avatar,
  .auth-skeleton-line {
    animation: none;
  }
}

header,
.brand-card,
.card,
.snack-card,
details {
  background: var(--white-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
}

header {
  padding: 18px 20px;
  margin-bottom: 14px;
  border-top: 4px solid var(--green-dark);
}

.header-row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  align-items: flex-start;
}

.header-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Narrow screens: side-by-side (title left, nav right) leaves the nav
   buttons wrapping into a ragged jumble squeezed against the right edge.
   Stack instead - lead full-width on top, then the nav as a tidy
   two-column grid of equal buttons. The .hidden utility still wins on
   individual buttons (sign-out, admin-users) since display:none on the
   child is untouched by the parent's grid. */
@media (max-width: 700px) {
  .header-row {
    flex-direction: column;
  }

  .header-actions {
    display: grid;
    gap: 8px;
    grid-template-columns: 1fr 1fr;
    width: 100%;
  }

  .header-actions .btn,
  .header-actions button {
    margin: 0;
    text-align: center;
  }

  /* The fixed notifications bell (top:18/right:18) otherwise sits right
     on top of the header's first row - keep the header's content clear
     of that corner on pages that have the bell. */
  body:has(.admin-notifications-bell) .header-row {
    padding-right: 48px;
  }
}

/* Shared across every admin page (js/admin-notifications.js injects this
   button into document.body directly, not into any page's own header), so
   it's fixed rather than relying on a .header-actions row existing.
   Deliberately its own rule rather than reusing .basket-notification's
   convention - that one's base rule is `display: none` with no override
   anywhere that sets it back to flex, so unhiding it via .hidden removal
   alone never actually shows it. This badge's un-hidden state is its own
   default (display: flex); JS only ever adds/removes .hidden to toggle it. */
.admin-notifications-bell {
  align-items: center;
  background: var(--green-dark);
  border: 0;
  border-radius: 50%;
  box-shadow: 0 4px 12px rgba(24, 79, 42, 0.24);
  color: #fff;
  display: flex;
  height: 44px;
  justify-content: center;
  padding: 0;
  position: fixed;
  right: 18px;
  top: 18px;
  width: 44px;
  z-index: 65;
}

.admin-notifications-bell svg {
  height: 21px;
  width: 21px;
}

.admin-notifications-bell span {
  align-items: center;
  background: #fff;
  border: 2px solid var(--green-dark);
  border-radius: 999px;
  color: var(--green-dark);
  display: flex;
  font-size: 11px;
  font-weight: 600;
  height: 21px;
  justify-content: center;
  min-width: 21px;
  padding: 0 4px;
  position: absolute;
  right: -4px;
  top: -4px;
}

#nav-qr-toast,
#feedback-toast {
  position: fixed;
  z-index: 120;
  right: 18px;
  bottom: 18px;
  max-width: min(360px, calc(100vw - 36px));
  padding: 11px 14px;
  border-radius: 10px;
  background: #123e24;
  color: #fff;
  box-shadow: 0 12px 30px rgba(0, 0, 0, .2);
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease;
}

#nav-qr-toast.error { background: #7b2323; }

#nav-qr-toast.show,
#feedback-toast.show {
  opacity: 1;
  transform: translateY(0);
}

.nav-qr-dialog {
  width: min(440px, calc(100vw - 28px));
  padding: 22px;
  border: 0;
  border-radius: 12px;
  background: #fff;
  color: var(--text);
}

.nav-qr-dialog::backdrop { background: rgba(8, 24, 14, .58); }

.nav-qr-modal-head,
.nav-qr-modal-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.nav-qr-modal-head h2 { margin: 2px 0 0; }

.nav-qr-eyebrow {
  color: var(--green);
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .12em;
}

.nav-qr-modal-close {
  min-width: 36px;
  padding: 2px 7px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--green-dark);
  font-size: 1.8rem;
  line-height: 1;
}

.nav-qr-modal-close:hover { background: var(--green-light); }
.nav-qr-modal-close:active { transform: translateY(2px) scale(.96); }

.nav-qr-preview {
  display: grid;
  place-items: center;
  margin: 20px auto;
  padding: 12px;
  background: #fff;
}

.nav-qr-preview svg {
  display: block;
  width: min(290px, 72vw);
  height: auto;
}

.nav-qr-readable {
  display: grid;
  gap: 5px;
  padding: 12px;
  border-radius: 6px;
  background: #f1f5f2;
}

.nav-qr-readable span {
  color: var(--muted);
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
}

.nav-qr-readable a {
  overflow-wrap: anywhere;
  color: var(--green-dark);
  font-size: .86rem;
}

.nav-qr-modal-actions {
  justify-content: flex-end;
  margin-top: 16px;
}

.nav-qr-modal-actions button {
  border: 0;
  border-radius: 5px;
  box-shadow: none;
}

.nav-qr-modal-actions button:active { transform: translateY(2px) scale(.98); }

@media (max-width: 520px) {
  .nav-qr-dialog { padding: 18px; }
  .nav-qr-modal-actions { align-items: stretch; flex-direction: column-reverse; }
  .nav-qr-modal-actions button { width: 100%; }
}

h1 {
  margin: 0 0 6px;
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.04em;
  color: var(--green-dark);
}

h2 {
  margin: 0 0 12px;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--green-dark);
}

h3 {
  margin: 0 0 10px;
  font-size: 16px;
  font-weight: 600;
  color: var(--green-dark);
}

p {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

/* Sitewide button system, redesigned ghost/outline-minimal per request:
   transparent by default (just a clean colored border + text) and fills
   in solid on hover, instead of the old thin-translucent-border +
   subtle-gradient-fill look. .primary stays solid-filled at rest -
   removing ALL fill distinction between primary and secondary buttons
   would erase the visual hierarchy that tells an admin which action is
   the main one on a row/modal (Confirm payment vs. Cancel, etc.) - so
   primary leads with color, everything else leads with an outline and
   only fills to confirm on hover. Deliberately NOT applied to bespoke
   compact controls with their own established look (.basket-float-btn,
   .stepper button, .modal-close, .notif-dismiss, .row-menu-toggle,
   .toggle) - those aren't general-purpose CTA buttons. */
button,
.btn {
  background: transparent;
  border: 1.5px solid var(--green);
  border-radius: var(--radius-sm);
  color: var(--green-dark);
  cursor: pointer;
  display: inline-block;
  font-size: 14px;
  font-weight: 500;
  padding: 8.5px 12px;
  text-decoration: none;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
  white-space: nowrap;
}

button:not(:disabled):hover,
.btn:hover {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}

button.primary {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}

button.primary:not(:disabled):hover {
  background: var(--green-dark);
  border-color: var(--green-dark);
}

button.danger {
  background: transparent;
  border-color: #b9504f;
  color: #8a2f2f;
}

button.danger:not(:disabled):hover {
  background: #8a2f2f;
  border-color: #8a2f2f;
  color: #fff;
}

button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.brand-card {
  padding: 18px;
  margin-bottom: 14px;
  background: linear-gradient(135deg, rgba(255,255,255,0.96), rgba(244,250,243,0.92));
}

.brand-grid {
  display: grid;
  grid-template-columns: auto 1fr 0.92fr;
  gap: 16px;
  align-items: center;
}

.avatar {
  width: 106px;
  height: 106px;
  border-radius: 50%;
  background: linear-gradient(145deg, #ffffff, #e7f3e5);
  border: 4px solid rgba(47, 122, 68, 0.18);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.9), 0 10px 20px rgba(24, 79, 42, 0.07);
  display: grid;
  place-items: center;
  overflow: hidden;
  flex-shrink: 0;
  padding: 6px;
}

.avatar img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: 50%;
}

.brand-title {
  font-size: 28px;
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--green-dark);
  overflow-wrap: anywhere;
}

.header-logo {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 1px solid rgba(47, 122, 68, 0.2);
}

/* Fresh Snacks wordmark badge, linked back to the tab page from every
   header — unlike .header-logo (a circular profile-icon crop), this is
   the actual badge artwork so it must not be clipped to a circle. */
.brand-logo-link {
  display: inline-flex;
  flex-shrink: 0;
  align-self: center;
}

.brand-logo {
  width: 40px;
  height: auto;
  display: block;
}

/* ---- top banner ---- */

header.banner {
  position: relative;
  overflow: hidden;
  padding: 0;
  border: none;
  background: #1d5c31;
  color: #fff;
}

.banner-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.banner-video {
  object-fit: cover;
}

/* keeps the left-side text readable over the video */
.banner-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg,
    rgba(20, 68, 36, 0.55),
    rgba(20, 68, 36, 0.2) 55%,
    rgba(20, 68, 36, 0) 78%);
}

@media (prefers-reduced-motion: reduce) {
  .banner-video {
    display: none;
  }
}

.banner-inner {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 22px 24px;
}

.banner-menu,
.banner-profile {
  background: rgba(255, 255, 255, 0.96);
}

/* the banner's icon buttons/badges hold inline SVGs (crisp at any zoom,
   no emoji-rendering variance across OS/browser) instead of glyphs */
.banner-menu {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.banner-menu svg {
  width: 19px;
  height: 19px;
  color: var(--green-dark);
}

.banner-track .ico svg {
  width: 15px;
  height: 15px;
  color: #fff;
}

.banner-pill svg {
  width: 15px;
  height: 15px;
  vertical-align: -3px;
  margin-right: 2px;
}

.banner-profile .chev svg {
  width: 11px;
  height: 11px;
  display: block;
}

.banner-badge {
  width: 92px;
  height: auto;
  flex-shrink: 0;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.3));
}

.banner-main {
  flex: 1;
  min-width: 0;
}


.banner-tagline {
  font-family: "Segoe Script", "Brush Script MT", "Comic Sans MS", cursive;
  color: #c8e96a;
  font-size: 18px;
  margin: 2px 0 12px;
}

.banner-tagline .tick {
  color: #a9cf3e;
}

.banner-subrow {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.banner-track {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 14px;
  color: #eef7ea;
}

.banner-track .ico {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.45);
  font-size: 13px;
}

.banner-pill {
  background: #fff;
  color: var(--green-dark);
  border-radius: 999px;
  padding: 7px 15px;
  font-size: 13.5px;
  font-weight: 600;
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
  white-space: nowrap;
}

.banner-profile {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-radius: 999px;
  overflow: hidden;
}

.banner-profile .chev {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--green-light);
  color: var(--green-dark);
  font-size: 11px;
}

.settings-session-actions {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.settings-session-actions button {
  width: 100%;
}

.settings-session-actions .drawer-note {
  margin: 8px 0 0;
}

.account-menu-item {
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: flex;
  justify-content: space-between;
  margin: 4px 2px 14px;
  padding: 13px 14px;
  text-align: left;
  width: calc(100% - 4px);
}

.account-signup {
  margin: 4px 2px 14px;
  width: calc(100% - 4px);
}

.account-menu-item strong,
.account-menu-item small {
  display: block;
}

.account-menu-item strong {
  color: var(--green-dark);
  font-weight: 600;
}

.account-menu-item small {
  color: var(--muted);
  margin-top: 3px;
}

.profile-attention {
  align-items: center;
  background: #b23b32;
  border-radius: 50%;
  color: #fff;
  display: inline-flex;
  flex: 0 0 24px;
  font-weight: 700;
  height: 24px;
  justify-content: center;
}

.saved-invite {
  border-top: 1px solid var(--line);
  margin-top: 16px;
  padding-top: 12px;
}

.saved-invite .quiet-button {
  background: transparent;
  border: 0;
  color: var(--muted);
  margin-top: 8px;
  width: 100%;
}

.brand-subtitle {
  margin-top: 7px;
  font-size: 15px;
  color: var(--muted);
}

/* auto-fit so this reflows cleanly whatever number of stat boxes it ends
   up holding, rather than a column count that assumes a fixed count. */
.brand-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
}

.stat-box {
  background: rgba(255,255,255,0.80);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 13px;
}

a.stat-box {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: box-shadow 150ms ease;
}

a.stat-box:hover, a.stat-box:focus-visible {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
}

/* "Pay in full now" chooser - two square tiles side by side. In-person is
   always enabled; PayPal stays visible below its J$3,000 minimum but greys
   out and disables rather than disappearing, so the customer can see it's
   something they'll unlock instead of wondering why it's missing. */
.pay-options-list { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
.pay-option {
  aspect-ratio: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 14px;
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, #fff, #f7faf7);
  cursor: pointer;
}
/* The bare `button:not(:disabled):hover` rule up top already wins the
   background/border here (a same class-count tie breaks on its extra
   element-selector point) and fills solid --green with white text - so
   the button itself already goes solid green on hover regardless of
   anything set here. What was missing is that this button's own inner
   text has its own explicit (dark green) color, which - being set
   directly on those elements, not inherited - doesn't pick up that
   white automatically, leaving near-invisible dark-on-dark text. */
.pay-option strong { font-size: 15px; color: var(--green-dark); }
.pay-option-note { font-size: 12.5px; color: var(--muted); line-height: 1.35; }
.pay-option:not(.is-disabled):hover strong,
.pay-option:not(.is-disabled):hover .pay-option-note,
.pay-option:not(.is-disabled):hover .pay-option-brand-mark { color: #fff; }
.pay-option-text { display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 0; }
.pay-option-icon-shell {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #fff;
  color: var(--green-dark);
  box-shadow: 0 3px 9px rgba(46, 75, 58, 0.10);
}
.pay-option-icon-shell img { width: 19px; }
/* currentColor now (see index.html) - black at rest, matching PayPal's
   own default styling, white on hover via the rule above. */
.pay-option-brand-mark { height: 22px; width: auto; flex-shrink: 0; color: #000; }

.pay-option.is-disabled {
  cursor: not-allowed;
  border-color: var(--line);
  background: #f4f5f3;
  filter: grayscale(1);
  opacity: 0.55;
}
.pay-option.is-disabled:hover { border-color: var(--line); background: #f4f5f3; }

@media (max-width: 480px) {
  .pay-options-list { grid-template-columns: 1fr; }
  .pay-option { aspect-ratio: auto; flex-direction: row; justify-content: flex-start; text-align: left; padding: 12px 14px; }
  .pay-option-text { flex: 1 1 auto; align-items: flex-start; }
}

.paypal-pay-breakdown {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin: 14px 0;
  padding: 10px 0;
}

.paypal-pay-line {
  display: flex;
  justify-content: space-between;
  padding: 4px 0;
  font-size: 14px;
}

.paypal-pay-line-total {
  border-top: 1px dashed var(--line);
  margin-top: 4px;
  padding-top: 8px;
  font-weight: 700;
}

.stat-label {
  color: var(--muted);
  font-size: 13px;
  margin-bottom: 4px;
}

.stat-value {
  font-size: 23px;
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* Balance preview fade (see animateBalancePreview in index.html) - the
   number cross-fades to what the balance will look like once a just-
   reported payment is confirmed, then fades back to the real, still-
   pending figure. Opacity only, so layout never shifts mid-animation. */
#stat-balance.balance-preview-fade { transition: opacity 0.35s ease; }
#stat-balance.balance-preview-fade.is-faded { opacity: 0; }

.green { color: var(--green-dark); }
.warm { color: var(--green); }
.blue { color: var(--blue-dark); }

/* Deliberately more prominent than any other stat box - a blue card with
   white text, since the balance is the one number a customer should
   notice first when they land on the page. Also reused as-is (this
   plain, compact form) by admin.html's "Open balance" dashboard stat -
   see .balance-stat--hero below for the enlarged, icon+wave-textured
   variant used only on index.html's own balance card, so a much larger
   admin-wide total never has to fit the same compact box this class
   was originally sized for. */
.balance-stat {
  background: linear-gradient(180deg, var(--green), var(--green-dark));
  border-color: var(--green-dark);
}

.balance-stat .stat-label {
  color: rgba(255, 255, 255, 0.75);
}

.balance-stat .stat-value {
  color: #fff;
}

/* index.html's own "Current balance" card only - a bigger hero
   treatment with an icon badge and the cashback demo's wave-texture
   background (cashback-demo.css's .metric-card--balance), layered under
   a visible diagonal sheen so the texture actually reads at this size
   (the raw demo asset alone is too subtle against a solid dark green
   fill once cropped down to a compact card). */
.balance-stat--hero {
  align-items: center;
  background-color: var(--green-dark);
  background-image:
    linear-gradient(135deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0) 45%),
    url("assets/cashback-demo/backgrounds/svg/balance-card.svg");
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  background-size: cover, cover;
  border-radius: 18px;
  display: flex;
  gap: 16px;
  padding: 18px 20px;
}

.stat-box-icon {
  align-items: center;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  display: grid;
  flex: none;
  height: 46px;
  justify-content: center;
  width: 46px;
}

.stat-box-icon img { height: 22px; width: 22px; }

.stat-box-body { min-width: 0; }

/* Always-available route to the Pay in person/PayPal chooser (see
   openPayOptionsModal) - the cashback card's "Pay in full now" button is
   the same modal, but that whole card disappears whenever there's no live
   cashback offer, which is most of the time a real balance just sits
   between purchases. This lives on the balance hero card instead, so it's
   reachable purely off having a balance to pay, with no dependency on
   cashback status. */
.balance-pay-btn {
  background: #fff;
  border: none;
  border-radius: 999px;
  color: var(--green-dark);
  cursor: pointer;
  flex: none;
  font-size: 14px;
  font-weight: 700;
  margin-left: auto;
  padding: 10px 18px;
}

.balance-pay-btn:hover {
  background: rgba(255, 255, 255, 0.86);
}

.balance-stat--hero .stat-value {
  font-size: 28px;
}

/* ---- early-payment cashback card ----
   Transplanted from the supplied cashback_html_package_paypal_official
   redesign as closely as possible (same class names/structure/sizing).
   Deliberate departures: colors are hardcoded instead of the redesign's
   own :root variables (those names - --muted/--line/--shadow etc - would
   collide with this app's existing global variables of the same name and
   silently change colors elsewhere on the site); h1/h2/p are scoped to
   .offer-card instead of left as bare element selectors (this page has
   many other headings); the cashback badge and "Pay in full now"/"Clear
   tab with PayPal" buttons use their own class names (.offer-primary/
   .paypal-branded-button, not .primary) because this app already has a real,
   differently-styled button.primary used everywhere else; the hourly
   urgency bar and the expired card's dismissible close (x) are additions
   this app already needed that aren't part of the original mockup. */

.offer-card {
  position: relative;
  overflow: hidden;
  margin-bottom: 18px;
  border: 1px solid rgba(25, 72, 45, .12);
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 8px 20px rgba(33, 72, 49, .08);
}

.offer-card p { margin-top: 0; }
.offer-card h2 { margin-top: 0; margin-bottom: 0; font-size: 17px; line-height: 1.25; letter-spacing: -.01em; color: #173f2d; }

.accent {
  position: absolute;
  inset: 0 auto 0 0;
  width: 5px;
  background: linear-gradient(#f2a500, #d98b00);
}

.active-offer {
  padding: 16px 16px 16px 22px;
}

.active-top, .active-bottom {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 150px;
  gap: 18px;
  align-items: center;
}

/* The CTA now carries the balance amount, so its column must size to
   the label instead of the fixed 150px the wallet column above uses. */
.active-bottom { grid-template-columns: minmax(0, 1fr) auto; }

.cashback-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 4px 13px 4px 11px;
  border-radius: 999px;
  color: #fff;
  background: linear-gradient(#f4a900, #e28a00);
  font-size: 13px;
  font-weight: 800;
  box-shadow: 0 3px 8px rgba(229,143,0,.25);
}
.cashback-pill img { width: 13px; filter: brightness(0) invert(1); }

.active-top h2 { margin: 10px 0 6px; font-size: 17px; line-height: 1.25; }
.lead { max-width: 620px; margin-bottom: 0; color: #65706b; font-size: 13px; line-height: 1.5; }

/* Purely a human-urgency visual, not tied to the actual payout rule
   (which only checks calendar days, never the hour) - tracks real
   elapsed hours toward the one deadline that genuinely exists: midnight,
   when today's rate steps down. */
.cashback-day-bar { display: flex; gap: 4px; margin: 8px 0 10px; }
/* 60 minute-segments need a much tighter gap than the 8 hour-segments to
   stay readable as a bar instead of mostly whitespace. */
.cashback-day-bar.is-minutes { gap: 1.5px; }
.cashback-day-segment {
  flex: 1;
  min-width: 0;
  height: 8px;
  border-radius: 999px;
  background: #e8790a;
  transition: background-color 0.3s ease, transform 220ms ease, box-shadow 220ms ease;
}
.cashback-day-segment.is-spent { background: rgba(214, 130, 30, 0.16); }
.cashback-day-segment.is-current { transform: scaleY(1.35); box-shadow: 0 0 0 2px rgba(232, 121, 10, 0.15); }
/* Final hour of the window - whatever is still lit goes red with a slow
   pulse, and becomes clickable: tapping it flips the whole bar into a
   60-minute countdown of that last hour (and back). */
.cashback-day-segment.is-final {
  background: #c62828;
  cursor: pointer;
  animation: cashback-final-pulse 2.4s ease-in-out infinite;
}
.cashback-day-segment.is-final.is-current { box-shadow: 0 0 0 2px rgba(198, 40, 40, 0.2); }
@keyframes cashback-final-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .cashback-day-segment.is-final { animation: none; }
}

.wallet { justify-self: center; width: min(96px, 100%); mix-blend-mode: multiply; }

.divider { height: 1px; margin: 14px 0 12px; background: #e1e8e3; }

/* Transaction flow (compact adaptation of professional_cashback_html):
   one card per purchase-day still earning, ascending by date left to
   right, each flowing through an arrow into the next and finally into
   the earn-back total card. */
.transaction-flow { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; min-width: 0; flex: 1; }
.transaction-card, .reward-card {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 190px;
  min-height: 78px;
  padding: 10px 14px;
  border-radius: 12px;
}
.transaction-card {
  border: 1px solid rgba(224, 157, 26, 0.34);
  background: linear-gradient(180deg, #fffaf0, #fff8eb);
}
.reward-card {
  background: linear-gradient(180deg, #f7faf7, #f0f6f2);
  border: 1px solid rgba(25, 105, 61, 0.08);
}
.icon-shell {
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 3px 9px rgba(46, 75, 58, 0.10);
}
.icon-shell img { width: 17px; }
.eyebrow { margin: 0 0 2px; color: #a5690a; font-size: 10.5px; font-weight: 850; letter-spacing: .03em; text-transform: uppercase; white-space: nowrap; }
.eyebrow-reward { color: #177746; }
.transaction-card strong, .reward-card strong { display: block; font-size: 18px; line-height: 1.1; letter-spacing: -.02em; }
.reward-amount { color: #d68b00; }
.subtext { display: block; margin-top: 2px; color: #65706b; font-size: 11.5px; white-space: nowrap; }
.flow-arrow { width: 18px; flex: 0 0 auto; }

.cashback-learn-more { display: inline-block; margin-top: 6px; font-size: 11.5px; color: #a5690a; text-decoration: underline; }

/* Balance + CTA, fenced off on the right - the balance keeps its slot
   and size no matter how many transaction cards render on the left. */
.payment-zone { display: flex; align-items: center; gap: 18px; padding-left: 20px; border-left: 1px solid #e1e8e3; }

/* Expired-window state: the earning chips are gone, so the payment zone
   is the only thing in the bottom row - without this it would fall into
   the full-width grid column and the CTA (width:100%) would stretch
   across the whole card. Collapse to one right-aligned, content-sized
   group instead, and drop the now-pointless left fence. */
.active-offer.window-over .active-bottom { grid-template-columns: 1fr; justify-items: end; }
.active-offer.window-over .payment-zone { border-left: 0; padding-left: 0; }
.active-offer.window-over .offer-primary { width: auto; }
.balance-block { min-width: 92px; text-align: center; }
.balance-block span { display: block; color: #65706b; font-size: 12px; }
.balance-block strong { display: block; margin-top: 3px; font-size: 22px; line-height: 1; letter-spacing: -.03em; }

.offer-primary {
  min-height: 40px;
  padding: 0 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 0;
  border-radius: 10px;
  color: #fff;
  background: linear-gradient(#177344, #0e4e30);
  box-shadow: 0 5px 12px rgba(14,78,48,.16);
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: .18s;
  width: 100%;
  white-space: nowrap;
}
.offer-primary:hover { transform: translateY(-1px); box-shadow: 0 7px 14px rgba(14,78,48,.22); }
.offer-primary img { width: 16px; filter: brightness(0) invert(1); }

/* A "these bonuses expired" reminder once a balance has sat unpaid past
   both tiers - dashed/muted/struck-through, deliberately reading like a
   lapsed coupon rather than a live offer (compare .active-offer, which
   is warm/active). Purely informational - never affects an actual
   payout. */
.expired-card { padding: 16px 16px 18px 22px; }
.expired-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding-right: 20px; }
.heading-row { display: flex; align-items: center; gap: 10px; }
.round-icon { width: 34px; height: 34px; flex: 0 0 auto; display: grid; place-items: center; border-radius: 50%; background: #fff4dd; }
.round-icon img { width: 16px; }
.expired-pill {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border: 1.5px solid #d45359;
  border-radius: 8px;
  color: #b3262d;
  text-transform: uppercase;
  letter-spacing: .07em;
  font-size: 11px;
  font-weight: 850;
}
.expired-pill img { width: 12px; }
.bonus-grid { display: flex; flex-wrap: wrap; gap: 10px; margin: 12px 0 0 44px; }
.bonus {
  flex: 0 1 190px;
  min-width: 150px;
  padding: 9px 13px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  border: 1.5px dashed #c8d3cc;
  border-radius: 11px;
  background: rgba(255,255,255,.8);
}
.bonus > div { display: grid; gap: 2px; }
.bonus span { color: #65706b; font-weight: 700; font-size: 12px; }
.bonus strong { font-size: 17px; text-decoration: line-through; }
.ghost-star { flex: none; width: 32px; height: 32px; display: grid; place-items: center; border-radius: 50%; background: #f3f6f4; }
.ghost-star img { width: 15px; filter: grayscale(1); opacity: .35; }

.card-close {
  position: absolute;
  z-index: 4;
  top: 10px;
  right: 10px;
  display: grid;
  width: 24px;
  height: 24px;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: #65706b;
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.card-close:hover, .card-close:focus-visible { background: rgba(0, 0, 0, 0.08); }

/* PayPal payment entry card */
.payment-card {
  min-height: 0;
  padding: 16px 16px 16px 22px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 130px;
  gap: 18px;
  align-items: center;
}
.payment-heading { align-items: flex-start; }
.payment-heading p { margin: 5px 0 0; color: #65706b; font-size: 13px; line-height: 1.5; }
/* Official-style PayPal button: gold #FFC439 with the real two-tone PayPal
   wordmark centered, matching how PayPal's own SDK renders its smart
   button (their brand guidelines: gold fill, "Pay"/"Pal" in their own two
   brand blues, no added text, logo only - not a flat black wordmark, which
   isn't how PayPal's own gold button actually looks). */
.paypal-branded-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: auto;
  min-width: 220px;
  min-height: 40px;
  margin: 12px 0 0 44px;
  padding: 0 24px;
  border: 0;
  border-radius: 20px;
  background: #ffc439;
  cursor: pointer;
  transition: filter .15s ease;
}
.paypal-branded-button:hover { filter: brightness(.95); }
/* The wordmark's two paths carry their own fixed brand-blue fills now
   (see index.html), not currentColor - so hovering this button (which
   turns solid green with white text, same as every other button on the
   page - see the button:not(:disabled):hover rule up top) needs its own
   explicit override to turn the logo white too, or it would stay blue
   on green instead of matching the rest of the hover state. */
.paypal-branded-button:hover svg path { fill: #fff; }
.paypal-branded-button svg { height: 22px; width: auto; display: block; }
.shield { justify-self: center; width: min(90px, 100%); opacity: .82; mix-blend-mode: multiply; }

@media (max-width: 640px) {
  .active-offer, .expired-card, .payment-card { padding: 14px 14px 16px 20px; }
  .active-top, .active-bottom { grid-template-columns: 1fr; gap: 12px; }
  .wallet { display: none; }
  .transaction-card, .reward-card { flex: 1 1 100%; width: auto; min-width: 0; }
  .flow-arrow { width: 14px; margin: 0 auto; transform: rotate(90deg); }
  .payment-zone { padding-left: 0; border-left: 0; padding-top: 12px; border-top: 1px solid #e1e8e3; justify-content: space-between; }
  .bonus-grid { margin-left: 0; }
  .bonus { flex: 1 1 100%; min-width: 0; }
  .payment-card { grid-template-columns: 1fr; }
  .shield { display: none; }
  .expired-head { align-items: flex-start; padding-right: 18px; }
  .paypal-branded-button { width: 100%; min-width: 0; margin-left: 0; }
}

.main-grid {
  display: grid;
  grid-template-columns: 1.02fr 0.98fr;
  gap: 14px;
  margin-bottom: 14px;
  align-items: stretch;
}

.snack-card {
  overflow: hidden;
}

.favorite-section,
.activity-summary {
  margin-bottom: 14px;
}

.recommendations-section {
  background: rgba(255, 255, 255, 0.86);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 14px;
  padding: 20px;
}

.recommendations-heading {
  align-items: end;
  display: flex;
  gap: 20px;
  justify-content: space-between;
  margin-bottom: 16px;
}

.recommendations-heading h2 {
  color: var(--green-dark);
  font-size: 23px;
  font-weight: 500;
  margin: 0;
}

.recommendations-heading p {
  color: var(--muted);
  font-size: 13px;
  margin: 0;
  text-align: right;
}

.recommendations-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.recommendation-card {
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: grid;
  gap: 12px;
  grid-template-columns: 82px minmax(0, 1fr);
  min-height: 112px;
  padding: 12px;
  position: relative;
}

.recommendation-photo {
  align-items: center;
  background: #f5faf4;
  border-radius: 10px;
  display: flex;
  height: 82px;
  justify-content: center;
  overflow: hidden;
}

.recommendation-photo img {
  height: 100%;
  object-fit: contain;
  width: 100%;
}

.recommendation-card h3 {
  color: var(--green-dark);
  font-size: 14px;
  font-weight: 500;
  margin: 0 20px 4px 0;
}

.recommendation-card p {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
  margin: 0 0 5px;
}

.recommendation-card > div:last-child > span {
  color: var(--green);
  font-size: 13px;
  font-weight: 600;
}

.recommendation-dismiss {
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--muted);
  font-size: 18px;
  height: 28px;
  line-height: 1;
  padding: 0;
  position: absolute;
  right: 7px;
  top: 7px;
  width: 28px;
}

.favorite-card {
  display: grid;
  grid-template-columns: minmax(260px, 0.9fr) minmax(0, 1.1fr);
}

.favorite-card .snack-image {
  border-right: 1px solid var(--line);
  border-bottom: 0;
}

.snack-image {
  background: rgba(255, 255, 255, 0.68);
  padding: 14px;
  border-bottom: 1px solid var(--line);
}

/* cover (not contain) so the favorite background photo fills its box
   edge to edge instead of floating with empty space around it on a
   landscape upload; object-position keeps the crop centered either way. */
.snack-image img {
  width: 100%;
  height: 246px;
  object-fit: cover;
  object-position: center;
  display: block;
}

.snack-body,
.card {
  padding: 16px;
}

.snack-name {
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -0.03em;
  margin-bottom: 5px;
  color: var(--green-dark);
}

.snack-label {
  color: var(--green-dark);
  font-size: 13px;
  margin-bottom: 5px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.summary-grid,
.mini-grid {
  display: grid;
  gap: 10px;
}

.summary-grid {
  grid-template-columns: repeat(2, 1fr);
}

.activity-summary .summary-grid {
  grid-template-columns: repeat(3, 1fr);
}

.mini-grid {
  grid-template-columns: repeat(2, 1fr);
  margin-top: 14px;
}

.mini-card {
  background: rgba(255,255,255,0.80);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px;
}

.soft-green {
  background: rgba(234, 245, 232, 0.94);
}

.soft-blue {
  background: rgba(241, 247, 250, 0.94);
}

.muted-small {
  color: var(--muted);
  font-size: 13px;
  margin-top: 4px;
}

.section-card {
  padding: 18px;
  margin-bottom: 14px;
}

.note {
  background: rgba(244, 250, 243, 0.98);
  border: 1px solid rgba(24, 79, 42, 0.12);
  border-radius: var(--radius-sm);
  padding: 11px 12px;
  color: var(--muted);
  margin-bottom: 12px;
  font-size: 14px;
}

details {
  margin-bottom: 10px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.86);
}

summary {
  cursor: pointer;
  list-style: none;
  /* 10px + the details 1px border = the same 11px inset as the table,
     so the percentage lanes land on identical x positions */
  padding: 16px 10px;
  font-size: 15px;
  background: linear-gradient(90deg, rgba(244,250,243,0.94), rgba(255,255,255,0.82));
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 17% 17% 22%;
  column-gap: 0;
  align-items: center;
}

summary > span:nth-child(2),
summary .month-total {
  padding: 0 9px;
}

/* month names stay put (left) so nothing shifts when a section expands;
   the snack-days annotation simply appears to the right of the name */
summary > span:nth-child(2) {
  text-align: left;
}

.log-head > span:nth-child(2) {
  text-align: left;
}

/* column headings above the collapsible periods; same lanes as the summary
   rows and the tables inside (details border 1px + summary padding = inset) */
.log-head {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) 17% 17% 22%;
  column-gap: 0;
  align-items: center;
  /* no border here, so 11px does the whole inset */
  padding: 4px 11px 8px;
  color: var(--muted);
  font-size: 13px;
}

.log-head > span {
  padding: 0 9px;
}

summary::-webkit-details-marker { display: none; }

.toggle {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  background: rgba(255,255,255,0.86);
  color: var(--green-dark);
  display: inline-grid;
  place-items: center;
  font-size: 17px;
  line-height: 1;
  border: 1px solid rgba(24,79,42,0.12);
}

.toggle::before { content: "+"; }
/* > summary child scoping matters: with a bare descendant selector, a
   COLLAPSED day group nested inside an open month still matched
   details[open] (the month) and kept showing the collapse "-" instead
   of reverting to "+". */
details[open] > summary .toggle::before { content: "\2013"; }

.month-name { font-weight: 600; color: var(--green-dark); }

/* a period holding a listing flagged for review shows red even collapsed */
.month-name.needs-review {
  color: #8a2f2f;
}

.month-note {
  color: var(--muted);
  font-size: 13px;
  margin-top: 2px;
}

.month-total {
  color: var(--blue-dark);
  font-size: 14px;
  white-space: nowrap;
  font-weight: 500;
}

.tracker-table,
.bottom-summary {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0 6px;
  font-size: 14px;
  table-layout: fixed;
}

/* one column system top to bottom:
   lead (day+snack+qty) = 44%, then Tab/Value 17%, Paid/Payment 17%, Remaining 22% */
.col-day { width: 10%; }
.col-snack { width: 26%; }
.col-count { width: 8%; }
.col-value { width: 17%; }
.col-payment { width: 17%; }
.col-fill { width: 22%; } /* the Remaining lane; tables leave it empty */

/* bottom summary continues the same lanes: value under Tab, paid under Paid,
   balance under Remaining */
.bs-label { width: 44%; }
.bs-a { width: 17%; }
.bs-b { width: 17%; }
.bs-c { width: 22%; }

.tracker-table th,
.tracker-table td {
  overflow-wrap: anywhere;
}

/* numbers, money, and dates never break mid-value */
.tracker-table th,
.tracker-table .num,
.tracker-table .num-money,
.tracker-table td:first-child,
.bottom-summary .num-money {
  white-space: nowrap;
  overflow-wrap: normal;
}

.tracker-table {
  padding: 0 10px 10px;
}

/* titles and counts center; currency stays left-aligned at the J$ */
.tracker-table th {
  text-align: center;
  color: var(--muted);
  font-weight: 500;
  padding: 8px 9px 4px;
  font-size: 13px;
}

.tracker-table td {
  text-align: center;
}

.tracker-table .num {
  text-align: center;
}

.log-head > span {
  text-align: center;
}

.section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.section-head h2 {
  margin: 0;
}

.log-controls {
  display: flex;
  gap: 8px;
}

/* the period detail (e.g. "5 snack day(s)") rides the month's line,
   but only while the section is expanded */
.month-note {
  display: none;
}

/* > summary child scoping for the same reason as the toggle icon above -
   a collapsed nested day group must not inherit its open parent's state */
details[open] > summary .month-note {
  display: inline;
}

.tracker-table td,
.bottom-summary td {
  background: rgba(255, 255, 255, 0.84);
  padding: 10px 9px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.tracker-table td:first-child,
.bottom-summary td:first-child {
  border-left: 1px solid var(--line);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}

.tracker-table td:last-child,
.bottom-summary td:last-child {
  border-right: 1px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* money cells: an invisible fixed-width box is centered in the lane and the
   value is left-aligned inside it, so all prices start on the same line */
.num-money {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.mv {
  /* fixed-width box centered in the lane; the value is left-aligned inside
     and starts where the centered title's text starts. Long values overflow
     to the right without moving the shared start line. */
  display: inline-block;
  width: 2.8em;
  text-align: left;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* same idea for snack names: a fixed-width invisible box centered in the
   lane, names left-aligned inside so they all start on the same line */
.nv {
  display: inline-block;
  width: 150px;
  text-align: left;
  white-space: nowrap;
}


.snack-pill {
  display: inline-block;
  background: var(--green-light);
  color: var(--green-dark);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  font-size: 13px;
  font-weight: 400;
}

.other-pill {
  background: #f2f7f8;
  color: var(--blue-dark);
}

.pay-pill {
  background: #eef4f8;
  color: var(--blue-dark);
}

.month-line td {
  background: rgba(244, 250, 243, 0.96);
  color: var(--green);
  font-weight: 500;
}

/* Each date within a month collapses on its own, nested inside the
   month's own <details> - same horizontal inset the flat tracker-table
   would have used, so nesting doesn't shift anything sideways. */
.log-day-groups {
  padding: 0 10px 10px;
}

.log-day-group {
  margin-bottom: 8px;
}

.log-day-group:last-child {
  margin-bottom: 0;
}

/* ---- flat report presentation for the transaction logs ---- */
/* #log (the customer Snack Log) and #txn-groups (the admin transactions
   page): the collapsible groups read as one continuous, minimalist
   report - no card border/shadow/blur wrapped around every group, no
   gradient header strips, no pill-box row borders. Hairline separators
   carry the structure instead, and the modular expand/collapse (+/- per
   group, Expand all/Collapse all) keeps working on top of the flat
   view. Scoped to these two containers so FAQ accordions and other
   <details> around the site keep their card look. */
#log details,
#txn-groups details {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin-bottom: 0;
}

#log summary,
#txn-groups summary {
  background: transparent;
  border-top: 1px solid var(--line);
}

/* nested day rows inside a month get a fainter separator than the
   month-level ones, so the hierarchy still reads at a glance */
#log .log-day-group > summary {
  border-top: 1px solid rgba(24, 79, 42, 0.07);
}

/* Expand all -> flat monthly report (.all-expanded is set by the Expand
   all button, cleared by Collapse all): with everything open, the per-day
   summary rows, per-day total lines, and repeated per-day table headers
   are redundant - every item row already carries its date - so they drop
   out and each month reads as a single continuous run of item rows under
   ONE set of general column headers (the first day-table's header per
   month stays as that general header). */
#log.all-expanded .log-day-group > summary,
#log.all-expanded .log-day-group .tracker-table .month-line,
#log.all-expanded .log-day-group:not(:first-child) .tracker-table thead {
  display: none;
}

#log .toggle,
#txn-groups .toggle {
  background: transparent;
  border: 0;
}

#log .tracker-table,
#txn-groups .admin-table {
  border-spacing: 0;
}

#log .tracker-table td,
#txn-groups .admin-table td {
  background: transparent;
  border: 0;
  border-bottom: 1px solid rgba(24, 79, 42, 0.08);
  border-radius: 0;
}

/* Both flat overrides above are ID-scoped, so they beat tr.disputed's
   plain background rule regardless of source order (an id always
   outranks a class, however the two selectors are arranged) - caught
   live: transactions.html's disputed-row red tint was silently rendering
   as fully transparent. Restored here at matching specificity so the
   "this needs attention" row tint survives the flat table style instead
   of being erased by it. */
#txn-groups .admin-table tr.disputed td {
  background: rgba(250, 240, 239, 0.9);
}

/* User accounting used to carry the same flat-report treatment as
   #txn-groups above (zero row spacing, hairline-only borders) - fine when
   every row looked the same, but once rows could be individually
   highlighted (resolved/green, unresolved/red - see admin.html's UI
   settings), a highlighted row's own border+radius sitting right against
   a spacing-less flat neighbor produced inconsistent gaps between
   different row pairs, and a flat row's bottom-hairline-only border
   would've read as a half-open box once real spacing existed at all.
   Simplest fix: every row here - highlighted or not - shares one full
   bordered, rounded-corner treatment with real spacing between rows, so
   the gap is identical everywhere and only the FILL color (plus the
   healthy/unresolved classes below) ever changes. */
#accounting-list .admin-table {
  border-spacing: 0 6px;
}

#accounting-list .admin-table td {
  background: rgba(255, 255, 255, 0.84);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

#accounting-list .admin-table td:first-child {
  border-left: 1px solid var(--line);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}

#accounting-list .admin-table td:last-child {
  border-right: 1px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* The name inputs/VIP select go semi-transparent on a highlighted row
   instead of their usual solid white, so the row's tint shows through
   them too - one consistent colored surface per row, not a color fill
   fighting its own white form controls. */
#accounting-list .admin-table tr.healthy-account td {
  background: var(--green-light);
  border-color: rgba(24, 79, 42, 0.18);
}

#accounting-list .admin-table tr.healthy-account input,
#accounting-list .admin-table tr.healthy-account select {
  background: rgba(255, 255, 255, 0.6);
}

#accounting-list .admin-table tr.unresolved-account td {
  background: rgba(250, 240, 239, 0.9);
  border-color: rgba(138, 47, 47, 0.18);
}

#accounting-list .admin-table tr.unresolved-account input,
#accounting-list .admin-table tr.unresolved-account select {
  background: rgba(255, 255, 255, 0.6);
}

.bottom-summary {
  /* match the horizontal inset of tables inside <details>
     (1px border + 10px table padding) so columns line up */
  margin: 8px 11px 0;
  width: calc(100% - 22px);
}

.bottom-summary td:first-child {
  color: var(--muted);
}

.balance-row td {
  background: rgba(234, 245, 232, 0.98);
  color: var(--green-dark);
  font-weight: 500;
}

footer {
  margin-top: 12px;
  color: #5d665d;
  font-size: 13px;
  padding: 4px 2px 18px;
}

.footer-link {
  background: none;
  border: none;
  padding: 0;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* ---- Forms / admin / bins ---- */

.admin-login-shell {
  display: grid;
  min-height: min(570px, calc(100vh - 205px));
  padding: clamp(34px, 7vw, 76px) 20px;
  place-items: start center;
}

.admin-login-card {
  background: rgba(255, 255, 255, 0.96);
  border: 1px solid #d8e2da;
  border-radius: 16px;
  box-shadow: 0 18px 45px rgba(20, 58, 34, 0.1);
  padding: clamp(24px, 4vw, 38px);
  width: min(590px, 100%);
}

.admin-login-heading {
  align-items: flex-start;
  display: flex;
  gap: 16px;
  margin-bottom: 28px;
}

.admin-login-heading h2 {
  color: var(--green-dark);
  font-size: clamp(26px, 4vw, 32px);
  letter-spacing: -0.025em;
  margin: 1px 0 7px;
}

.admin-login-heading p {
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
}

.admin-login-eyebrow {
  color: var(--green) !important;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.admin-login-mark {
  align-items: center;
  background: #edf5ed;
  border-radius: 12px;
  display: flex;
  flex: 0 0 48px;
  height: 48px;
  justify-content: center;
}

.admin-login-mark svg {
  fill: var(--green-dark);
  height: 27px;
  width: 27px;
}

.admin-sso-actions {
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr 1fr;
}

.admin-sso-button {
  align-items: center;
  background: #fff;
  border: 1px solid #cbd7cd;
  border-radius: 9px;
  color: #213b29;
  display: flex;
  font-weight: 500;
  gap: 10px;
  justify-content: center;
  min-height: 46px;
}

.admin-sso-button:hover {
  background: #f5f8f5;
  border-color: #9fb3a3;
}

.admin-sso-button:disabled {
  background: #f1f3f1;
  border-color: #d9ddda;
  color: #929a94;
  cursor: not-allowed;
  opacity: 1;
}

.admin-sso-button:disabled .microsoft-icon {
  filter: grayscale(1);
  opacity: 0.55;
}

.admin-provider-icon {
  align-items: center;
  display: inline-flex;
  flex: 0 0 18px;
  height: 18px;
  justify-content: center;
  width: 18px;
}

.google-icon {
  color: #3569d4;
  font-family: Arial, sans-serif;
  font-size: 17px;
  font-weight: 700;
}

.microsoft-icon {
  display: grid;
  gap: 2px;
  grid-template-columns: 1fr 1fr;
}

.microsoft-icon i:nth-child(1) { background: #f35325; }
.microsoft-icon i:nth-child(2) { background: #81bc06; }
.microsoft-icon i:nth-child(3) { background: #05a6f0; }
.microsoft-icon i:nth-child(4) { background: #ffba08; }

.microsoft-icon i {
  display: block;
  height: 8px;
  width: 8px;
}

.admin-login-divider {
  align-items: center;
  color: #748078;
  display: flex;
  font-size: 12px;
  gap: 13px;
  margin: 25px 0 20px;
  text-transform: uppercase;
}

.admin-login-divider::before,
.admin-login-divider::after {
  background: #dfe6e0;
  content: "";
  flex: 1;
  height: 1px;
}

.admin-email-login {
  display: grid;
  gap: 15px;
}

.admin-email-login .field label {
  color: #33483a;
  font-weight: 500;
  margin-bottom: 7px;
}

.admin-email-login input {
  background: #fff;
  min-height: 46px;
}

.admin-email-submit {
  margin-top: 3px;
  min-height: 46px;
  width: 100%;
}

.admin-login-card .status:not(:empty) {
  margin-top: 14px;
}

.admin-login-restriction {
  align-items: center;
  border-top: 1px solid #e3e9e4;
  color: #6a756d;
  display: flex;
  font-size: 12px;
  gap: 8px;
  justify-content: center;
  margin: 24px 0 0;
  padding-top: 18px;
}

.admin-login-restriction svg {
  fill: #6a756d;
  height: 15px;
  width: 15px;
}

.admin-page-footer {
  text-align: center;
}

@media (max-width: 620px) {
  .admin-login-shell {
    min-height: auto;
    padding: 18px 0 28px;
  }

  .admin-login-card {
    border-radius: 12px;
    padding: 24px 18px;
  }

  .admin-sso-actions {
    grid-template-columns: 1fr;
  }

  .admin-login-heading {
    gap: 12px;
  }

  .admin-login-mark {
    flex-basis: 42px;
    height: 42px;
  }
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  align-items: end;
}

.field label {
  display: block;
  color: var(--muted);
  font-size: 13px;
  margin-bottom: 4px;
}

input,
select,
textarea {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-family: inherit;
  background: rgba(255,255,255,0.92);
  color: var(--text);
}

textarea {
  resize: vertical;
  min-height: 100px;
}

input:focus,
select:focus,
textarea:focus {
  outline: 2px solid rgba(47, 122, 68, 0.35);
  border-color: var(--green);
}

/* Date fields get their own themed treatment instead of the flat grey box
   every other field uses - it's the only field type that also opens extra
   native UI, so it should read as more clearly interactive at a glance.
   color-scheme:light stops the OS's dark mode from handing back a
   dark-on-dark picker popup with an invisible icon. */
input[type="date"] {
  border: 1.5px solid var(--line);
  background: var(--green-soft);
  cursor: pointer;
  accent-color: var(--green);
  color-scheme: light;
  transition: border-color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
}

/* Hovering anywhere in the field - not just the calendar icon - should read
   as clickable, since js/date-inputs.js opens the picker on a click anywhere
   in the field, not only on the icon. */
input[type="date"]:hover {
  border-color: var(--green);
  background: var(--white);
  box-shadow: var(--shadow);
}

input[type="date"]:focus {
  background: var(--white);
}

/* Recolors the native calendar-icon button to the brand green instead of
   Chrome/Edge's default flat grey glyph. Firefox/Safari don't expose this
   pseudo-element, so they fall back to their own native icon untouched. */
input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  width: 18px;
  height: 18px;
  padding: 3px;
  border-radius: 5px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 16px;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="%232f7a44" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="3"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>');
}

input[type="date"]::-webkit-calendar-picker-indicator:hover {
  background-color: var(--green-light);
}

.field.wide {
  grid-column: 1 / -1;
}

/* ---- feedback page ---- */

.feedback-video-banner {
  position: relative;
  width: 100%;
  min-width: 0;
  aspect-ratio: 60 / 13;
  min-height: 150px;
  max-height: 260px;
  overflow: hidden;
  margin-bottom: 14px;
  border-radius: 14px;
  background: #1d5c31;
  box-shadow: var(--shadow);
}

.feedback-video-banner .feedback-banner-inner {
  min-height: 100%;
  align-items: flex-start;
}

.feedback-video-banner video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.feedback-hero {
  display: block;
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow);
  margin-bottom: 14px;
  line-height: 0;
}

.feedback-hero img {
  display: block;
  width: 100%;
  height: auto;
}

.feedback-hero-mobile {
  display: none;
}

.profile-prompt {
  margin-top: 14px;
  background: rgba(234, 245, 232, 0.94);
  border: 1px solid rgba(47, 122, 68, 0.18);
  border-radius: var(--radius-sm);
  padding: 14px;
}

.profile-prompt p {
  color: var(--text);
  margin-bottom: 10px;
}

.profile-prompt .row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

/* ---- quick-request cards ---- */

.request-cards {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
}

.feedback-requests {
  padding: 28px 30px 30px;
  border-radius: 14px;
}

.feedback-requests > h2 {
  margin-bottom: 12px;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(26px, 3vw, 34px);
  line-height: 1.1;
}

.request-note {
  margin-bottom: 18px;
  padding: 0;
  color: var(--text);
  font-size: 14px;
}

.request-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  text-align: center;
  min-height: 230px;
  background: rgba(255, 255, 255, 0.86);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 18px 16px 14px;
  cursor: pointer;
  font-family: inherit;
  white-space: normal;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}

.request-card:hover {
  transform: translateY(-3px);
  border-color: rgba(47, 122, 68, 0.4);
  box-shadow: var(--shadow);
}

.request-card:active {
  transform: translateY(-1px);
}

.request-card.active {
  border-color: var(--green);
  background: var(--green-light);
  box-shadow: 0 0 0 2px rgba(47, 122, 68, 0.25);
}

.request-icon {
  width: 104px;
  height: 104px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(234, 245, 232, 0.9);
  color: var(--green-dark);
  flex-shrink: 0;
  padding: 8px;
  overflow: hidden;
  transition: background 0.18s ease;
}

.request-card:hover .request-icon {
  background: #dcefd9;
}

.request-icon img {
  width: 120%;
  height: auto;
  aspect-ratio: 1;
  max-width: none;
  object-fit: contain;
  transition: transform 0.2s ease;
  transform: translateY(-3px);
  transform-origin: center;
}

.request-card:hover .request-icon img,
.request-card:focus-visible .request-icon img {
  transform: translateY(-3px) scale(1.2);
}

.request-title {
  font-family: Georgia, "Times New Roman", serif;
  font-weight: 700;
  color: var(--green-dark);
  font-size: 18px;
  line-height: 1.2;
}

.request-desc {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
  max-width: 170px;
}

.request-card[data-category="credit"]:not(:disabled) .request-credit-pilot,
.request-card[data-category="credit"]:disabled .request-credit-live {
  display: none;
}

.request-card[data-category="credit"]:disabled {
  cursor: not-allowed;
  border-color: #d4d8d5;
  background: #f0f2f0;
  box-shadow: none;
  filter: grayscale(1);
  opacity: 0.68;
  transform: none;
}

.request-card[data-category="credit"]:disabled .request-credit-pilot {
  display: block;
  color: #767d78;
  font-weight: 700;
  letter-spacing: 0.03em;
}

.request-card[data-category="credit"]:disabled .request-icon {
  background: #dfe3df;
}

.request-card[data-category="credit"]:disabled .request-icon img {
  opacity: 0.58;
}

.request-card[data-category="credit"]:disabled .request-title {
  color: #666d68;
}

.request-card[data-category="credit"]:disabled .request-arrow {
  border-color: #aeb4af;
  color: #777e79;
}

.request-arrow {
  position: relative;
  display: block;
  width: 30px;
  height: 30px;
  margin-top: auto;
  border: 1px solid rgba(47, 122, 68, 0.22);
  border-radius: 50%;
  color: var(--green-dark);
}

.request-arrow::before {
  content: "";
  position: absolute;
  width: 7px;
  height: 7px;
  left: 50%;
  top: 50%;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  transform: translate(-65%, -50%) rotate(45deg);
}

.feedback-footer {
  display: flex;
  justify-content: space-between;
  gap: 8px 24px;
  flex-wrap: wrap;
  padding: 16px 20px;
  border-radius: 12px;
  background: linear-gradient(100deg, #050505, #061528);
  box-shadow: inset 4px 0 #00b4d8, inset -4px 0 #1e4788;
  color: #d9f8ff;
}

.feedback-footer a,
.request-modal-technology a {
  color: #00b4d8;
}

.feedback-footer a:hover,
.feedback-footer a:focus-visible,
.request-modal-technology a:hover,
.request-modal-technology a:focus-visible {
  color: #fff;
  text-decoration-color: #1e4788;
}

@media (max-width: 900px) {
  .request-cards {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 560px) {
  .feedback-video-banner {
    aspect-ratio: auto;
    height: 112px;
    min-height: 112px;
  }

  .feedback-hero img {
    display: none;
  }

  .feedback-hero-mobile {
    display: block;
    padding: 20px 18px 22px;
    line-height: 1.45;
  }

  .feedback-hero-kicker {
    display: inline-flex;
    align-items: center;
    margin-bottom: 12px;
    padding: 6px 11px;
    border-radius: 999px;
    background: var(--green-dark);
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }

  .feedback-hero-mobile h1 {
    margin: 0 0 10px;
    color: var(--green-dark);
    font-family: Georgia, "Times New Roman", serif;
    font-size: clamp(27px, 8vw, 34px);
    line-height: 1.05;
  }

  .feedback-hero-mobile p {
    margin: 0;
    color: var(--text);
    font-size: 13.5px;
  }

  .feedback-hero-mobile p + p {
    margin-top: 6px;
  }

  .feedback-requests {
    padding: 20px 14px;
  }

  .feedback-requests > h2 {
    margin-bottom: 8px;
    font-size: 28px;
  }

  .request-note {
    margin-bottom: 14px;
    font-size: 13.5px;
  }

  .request-cards {
    grid-template-columns: 1fr;
    gap: 10px;
  }

  .request-card {
    display: grid;
    grid-template-columns: 72px minmax(0, 1fr) 28px;
    grid-template-rows: auto auto;
    gap: 4px 12px;
    min-height: 112px;
    padding: 12px;
    text-align: left;
  }

  .request-icon {
    grid-column: 1;
    grid-row: 1 / 3;
    width: 72px;
    height: 72px;
    padding: 6px;
  }

  .request-title {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    font-size: 16px;
  }

  .request-desc {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    max-width: none;
    font-size: 12px;
    line-height: 1.35;
  }

  .request-arrow {
    grid-column: 3;
    grid-row: 1 / 3;
    align-self: center;
    width: 28px;
    height: 28px;
    margin: 0;
  }

  .feedback-footer {
    padding: 14px 16px;
    font-size: 13px;
  }
}

/* ---- cell number: area-code dropdown + local number ---- */

.phone-row {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: 8px;
}

.phone-row select {
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23184f2a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: 43px center;
  background-size: 10px 6px;
  padding-left: 8px;
  padding-right: 18px;
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 13.5px;
  color: var(--muted);
  cursor: pointer;
}

.checkbox-row input {
  width: 16px;
  height: 16px;
  accent-color: var(--green-dark);
  cursor: pointer;
}

/* ---- amount: currency prefix + numeric input ---- */

.amount-row {
  display: flex;
  align-items: stretch;
}

.amount-prefix {
  display: inline-flex;
  align-items: center;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-right: none;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  background: rgba(234, 245, 232, 0.75);
  color: var(--green-dark);
  font-weight: 600;
  font-size: 14px;
  white-space: nowrap;
}

.amount-row input {
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.req-mark {
  color: #8a2f2f;
}

/* ---- quick-request modal ---- */

.request-form-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 18px;
  align-items: start;
}

.request-form-grid .field {
  min-width: 0;
}

.request-form-grid .field > label {
  margin-bottom: 0;
}

.feedback-request-modal {
  width: min(680px, 100%);
  max-height: calc(100vh - 24px);
  padding: 20px 24px;
}

.feedback-request-modal h2 {
  margin-bottom: 8px;
}

.request-modal-desc {
  max-width: 58ch;
  margin: 0 0 16px;
  line-height: 1.55;
}

.feedback-request-modal .field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.feedback-request-modal input,
.feedback-request-modal select {
  min-height: 40px;
}

.feedback-request-modal textarea {
  min-height: 76px;
  line-height: 1.45;
}

.feedback-request-modal .status:not(:empty) {
  margin-top: 20px;
}

.request-modal-footer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.feedback-request-modal .modal-actions {
  margin: 0;
  flex-wrap: nowrap;
}

.request-modal-technology {
  margin-top: 14px;
  padding: 12px 16px;
  border-radius: 9px;
  background: linear-gradient(100deg, #050505, #061528);
  box-shadow: inset 4px 0 #00b4d8, inset -4px 0 #1e4788;
  color: #d9f8ff;
  font-size: 12.5px;
  line-height: 1.4;
  text-align: center;
}

.request-modal-technology a {
  font-weight: 700;
  text-decoration: none;
}

.request-modal-technology a:hover,
.request-modal-technology a:focus-visible {
  text-decoration: underline;
}

.request-contact-consent {
  margin: 0;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: var(--green-soft);
  color: var(--text);
  line-height: 1.3;
  gap: 10px;
}

.feedback-request-modal .amount-row {
  width: min(285px, 100%);
}

.request-contact-consent input {
  flex: 0 0 16px;
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(18, 36, 24, 0.55);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 70;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, visibility 0.2s ease;
}

.modal-backdrop.show {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.modal {
  position: relative;
  width: min(640px, 100%);
  max-height: 90vh;
  overflow-y: auto;
  background: var(--white);
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  padding: 24px;
  transform: translateY(14px) scale(0.97);
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.3, 1);
}

.modal-backdrop.show .modal {
  transform: none;
}

.modal h2 {
  margin: 0 0 6px;
  padding-right: 28px;
}

.modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 50%;
  font-size: 18px;
  line-height: 1;
  transition: background 0.15s ease, transform 0.15s ease;
}

.modal-close:hover {
  transform: rotate(90deg);
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

.confirm-modal {
  width: min(360px, 100%);
}

.confirm-modal p {
  margin-top: 4px;
}

.transaction-review-modal {
  width: min(440px, 100%);
  border-top: 4px solid #a34c45;
}

.transaction-review-modal h2 {
  margin-top: 12px;
}

.transaction-review-icon {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #faefed;
  color: #8a2f2f;
  font-size: 22px;
  font-weight: 600;
}

.transaction-review-modal .muted-small {
  margin-top: 10px;
  line-height: 1.5;
}

/* Remove vs review choice - only shown when the customer added this item
   themselves (never for one an admin logged, which only ever offers
   review). Review defaults active since it's the safer/reversible choice. */
.modal-mode-toggle {
  display: flex;
  gap: 8px;
}

.modal-mode-btn {
  flex: 1;
  padding: 9px 8px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius-sm);
  background: #fff;
  color: var(--muted);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.modal-mode-btn[data-mode="review"].active {
  border-color: var(--green);
  background: var(--green-light);
  color: var(--green-dark);
}

.modal-mode-btn[data-mode="removal"].active {
  border-color: #a34c45;
  background: #faefed;
  color: #8a2f2f;
}

.name-capture-modal {
  width: min(680px, 100%);
}

/* Promo-landing offer popup (e.g. the Hydrate QR campaign) - a pure image
   banner, no chrome besides the close button, edge-to-edge in the modal
   card. See PROMO_OFFER_IMAGES in index.html's boot script. */
.promo-offer-modal { width: min(560px, 100%); padding: 0; }
.promo-offer-modal .modal-close { background: rgba(255, 255, 255, 0.88); }
.promo-offer-image { display: block; width: 100%; height: auto; }

.invitee-name-modal {
  width: min(420px, 100%);
}

.profile-info-modal {
  width: min(620px, 100%);
}

.profile-info-fields {
  display: grid;
  gap: 14px;
  grid-template-columns: minmax(0, 1.35fr) minmax(170px, 0.65fr);
  margin-top: 18px;
}

.profile-display-field {
  grid-column: auto;
}

.profile-status-field input[readonly] {
  cursor: default;
  font-weight: 600;
}

.profile-status-field input[data-profile-tone="guest"] {
  background: #edf6ee;
  border-color: #afcbb5;
  color: #245f35;
}

.profile-status-field input[data-profile-tone="vip"] {
  background: #fbf4df;
  border-color: #d9bf72;
  color: #725313;
}

.profile-status-field input[data-profile-tone="admin"] {
  background: #edf4fb;
  border-color: #a8c4df;
  color: #24577f;
}

.name-capture-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* Red asterisk follows the input's live `required` state rather than a
   hand-toggled span - required/not-required flips in JS (self opening a
   tab vs. an invitee joining someone else's already-accountable tab just
   editing shared profile fields), and the mark should always match it
   without a second place to keep in sync. */
.name-capture-fields .field label:has(+ input:required)::after,
.profile-info-fields .field label:has(+ input:required)::after {
  content: " *";
  color: #b3261e;
}

.ncm-section {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.ncm-section h3 {
  margin: 0 0 4px;
}

/* Horizontal analogue of .bins-cart-items-wrap/.bins-cart-fade (this same
   file, ~3508-3551): overflow-y -> overflow-x, top/bottom fades -> left/right. */
.ncm-strip-wrap {
  position: relative;
}

.ncm-strip {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding-bottom: 4px;
}

.ncm-strip::-webkit-scrollbar {
  display: none;
}

.ncm-strip-fade {
  bottom: 4px;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  top: 0;
  transition: opacity 180ms ease;
  width: 32px;
}

.ncm-strip-fade-left {
  background: linear-gradient(to right, rgba(255,255,255,0.95), rgba(255,255,255,0));
  left: 0;
}

.ncm-strip-fade-right {
  background: linear-gradient(to left, rgba(255,255,255,0.95), rgba(255,255,255,0));
  right: 0;
}

.ncm-strip-wrap.has-more-left .ncm-strip-fade-left,
.ncm-strip-wrap.has-more-right .ncm-strip-fade-right {
  opacity: 1;
}

/* Card sized for a horizontal strip, adapted from .bin-card/.bin-photo. */
.ncm-card {
  background: rgba(255,255,255,0.88);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 22px rgba(24, 79, 42, 0.07);
  flex: 0 0 150px;
  overflow: hidden;
}

.ncm-card-photo {
  background: linear-gradient(160deg, rgba(244,250,243,0.9), rgba(227,239,225,0.9));
  border-bottom: 1px solid var(--line);
  height: 100px;
  overflow: hidden;
  position: relative;
}

.ncm-card-photo img {
  background: #fff;
  box-sizing: border-box;
  height: 100%;
  inset: 0;
  object-fit: contain;
  padding: 4px;
  position: absolute;
  width: 100%;
}

.ncm-card-body {
  padding: 8px 10px 10px;
}

.ncm-card-name {
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ncm-card-meta {
  color: var(--muted);
  font-size: 12px;
  margin: 3px 0 8px;
}

@media (max-width: 480px) {
  .name-capture-fields,
  .profile-info-fields {
    grid-template-columns: 1fr;
  }

  .profile-display-field {
    grid-column: auto;
  }
}

/* "Added to basket" toasts (desktop only - mobile just flashes the cart
   directly, see showCartToast() in the page script). Anchored to the
   bottom of the viewport (fixed, so it stays put regardless of scroll)
   but its left/width are set inline via JS to line up under the cart
   well's own column - including out in the right margin on very wide
   screens - rather than centering on the whole page. New toasts enter at
   the bottom and stack upward above any still visible. After a moment
   each toast individually flies up to the well's actual on-screen
   position (computed fresh per toast, since scrolling can move it within
   its sticky range) and fades out along the way, triggering a brief
   flash on the well (.basket-attention, defined further down) once it
   arrives. Every toast is a fixed size (matching the well's width) with
   the message itself truncated with an ellipsis rather than the toast
   growing to fit longer text. */
/* Anchored to the bottom-RIGHT corner of the viewport (no longer
   JS-positioned under the basket column): each toast slides in from the
   right like a ticker/ad strip and, when done, slides back off the right
   edge of the screen - see .leave below and showCartToast(). */
.cart-toast-stack {
  bottom: 28px;
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
  pointer-events: none;
  position: fixed;
  right: 18px;
  width: min(340px, calc(100vw - 36px));
  z-index: 80;
}

.cart-toast {
  align-items: center;
  background: var(--green-dark);
  border-radius: var(--radius);
  box-shadow: 0 18px 44px rgba(24, 79, 42, 0.35);
  box-sizing: border-box;
  color: #fff;
  display: flex;
  font-size: 14px;
  font-weight: 500;
  gap: 10px;
  opacity: 0;
  overflow: hidden;
  padding: 14px 22px;
  transform: translateX(60vw);
  transition: transform 320ms cubic-bezier(0.2, 0.8, 0.3, 1), opacity 220ms ease;
  width: 100%;
}

/* The ticker viewport: a hard clipping zone for the moving text, kept
   strictly separate from the check icon on its left and the Undo action
   on its right - the scrolling text lives entirely inside this box and
   can never slide across its neighbors. The mask fades the text out at
   both clip edges so it enters/exits softly like a real ticker strip.
   padding-left nudges the text clear of that left-edge fade zone - without
   it, even a short message that fits with no scrolling needed still reads
   as clipped, since its first couple of pixels sit right under the fade. */
.cart-toast .cart-toast-text-clip {
  flex: 1;
  mask-image: linear-gradient(90deg, transparent 0, #000 10px, #000 calc(100% - 10px), transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 10px, #000 calc(100% - 10px), transparent 100%);
  min-width: 0;
  overflow: hidden;
  padding-left: 4px;
}

.cart-toast .cart-toast-text {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: bottom;
  white-space: nowrap;
}

/* Long snack names scroll once from start to end inside the clip zone
   like a ticker-tape ad instead of truncating - only applied (by JS) when
   the text actually overflows, so short messages stay put. --ticker-shift
   is set by JS to exactly (clip content width - text width), so the far
   end of the text lines up with the clip's edge at the finish. Runs once
   (not infinite/alternate) - JS holds the toast on screen until this
   finishes, then sends it off-screen, so the full name is always read
   before it leaves; --ticker-duration is admin-configurable (UI settings
   panel) so the scroll speed can be tuned. */
.cart-toast .cart-toast-text.ticker {
  animation: cart-toast-ticker var(--ticker-duration, 1s) linear 1 forwards;
  max-width: none;
  overflow: visible;
  text-overflow: clip;
}

@keyframes cart-toast-ticker {
  0%, 8% { transform: translateX(0); }
  92%, 100% { transform: translateX(var(--ticker-shift, -40%)); }
}

.cart-toast svg {
  flex: none;
  height: 20px;
  width: 20px;
}

.cart-toast .cart-toast-undo {
  background: none;
  border: 0;
  color: #fff;
  cursor: pointer;
  flex: none;
  font: inherit;
  font-weight: 600;
  margin-left: auto;
  padding: 0 2px;
  pointer-events: auto;
  text-decoration: underline;
}

.cart-toast.show {
  opacity: 1;
  transform: translateX(0);
}

/* Exit: straight off the right edge of the screen, ad-strip style - no
   upward fly-away. */
.cart-toast.leave {
  opacity: 0;
  transform: translateX(110vw);
  transition: transform 650ms ease-in, opacity 600ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .cart-toast .cart-toast-text.ticker {
    animation: none;
  }
}

@media (max-width: 620px) {
  .modal-backdrop {
    padding: 8px;
  }

  .request-form-grid {
    grid-template-columns: 1fr;
  }

  .request-form-grid .field.wide {
    grid-column: auto;
  }

  .modal {
    max-height: calc(100dvh - 16px);
    padding: 18px 14px;
  }

  .feedback-request-modal {
    padding: 18px 14px;
  }

  .request-form-grid {
    gap: 14px;
  }

  .request-modal-desc {
    margin-bottom: 12px;
    font-size: 13.5px;
    line-height: 1.45;
  }

  .feedback-request-modal .field {
    gap: 6px;
  }

  .request-modal-footer {
    gap: 10px;
    margin-top: 12px;
    padding-top: 10px;
  }

  .request-contact-consent {
    padding-inline: 10px;
    font-size: 13px;
  }

  .request-modal-technology {
    margin-top: 10px;
    padding-block: 10px;
  }
}

.status {
  margin-top: 10px;
  font-size: 14px;
  min-height: 20px;
}

.status.ok { color: var(--green); }
.status.err { color: #8a2f2f; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

/* ---- invite card (compact, no-scroll, branded) ---- */

/* viewport-relative (not a fixed px cap) so the whole card — and every
   cqw-sized piece of text/icon inside it — scales up on large screens and
   down on small ones instead of sitting at one fixed size everywhere */
.invite-modal {
  width: clamp(260px, 34vw, 460px);
  padding: 16px;
}

.invite-card {
  position: relative;
  container-type: inline-size;
  aspect-ratio: 1086 / 1448;
  background-image: url("invite-card-frame.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 20px;
  box-shadow: var(--shadow);
}

/* all overlays below sit at the exact fractional bounds of the panels
   baked into invite-card-frame.jpg (QR box, Close button, Copy button),
   measured directly from the source image. The open gap between the QR
   box and the buttons has no baked-in guides, so name/note/label/link
   are freely positioned within it. Font/icon sizes use cqw (% of
   .invite-card's own width) so they shrink along with the card on narrow
   screens instead of overflowing past the card/modal/viewport edge.
   transform: translateY(2px) nudges text down slightly everywhere —
   percentage-based vertical centering left it sitting a touch too high. */
.invite-card-x-close {
  position: absolute;
  top: 2%;
  right: 2%;
  background: #fff;
  color: var(--green-dark);
  z-index: 2;
}

.invite-card-qr-area {
  position: absolute;
  left: 18.78%;
  top: 19.55%;
  width: 61.78%;
  height: 46.89%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.invite-card-qr-area canvas {
  display: block;
  width: 58cqw;
  max-width: 195px;
  height: auto;
}

/* everything between the QR box and the buttons is one flex column that
   self-distributes its content evenly across the open gap, rather than
   relying on separately hand-tuned top percentages per line (which is
   fragile whenever spacing needs to change) */
.invite-card-gap {
  position: absolute;
  left: 10%;
  right: 10%;
  top: 66.44%;
  bottom: 10.57%;
  display: flex;
  flex-direction: column;
  justify-content: space-evenly;
  align-items: center;
}

.invite-card-name {
  margin: 0;
  text-align: center;
  color: var(--green-dark);
  font-weight: 700;
  font-size: 4.6cqw;
  white-space: nowrap;
}

.invite-card-name .whisker {
  height: 3.6cqw;
  width: auto;
  vertical-align: middle;
  margin: 0 2px;
}

.invite-card-note {
  margin: 0;
  text-align: center;
  color: var(--muted);
  font-size: 3.3cqw;
  white-space: nowrap;
}

.invite-card-link-group {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 1cqw;
}

.invite-card-link-label {
  text-align: left;
  color: var(--green-dark);
  font-weight: 700;
  font-size: 3.4cqw;
}

.invite-card-link-wrap {
  position: relative;
  width: 100%;
  height: 8cqw;
  min-height: 28px;
  display: flex;
  align-items: center;
  border: 1.5px solid var(--green);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.7);
}

.invite-card-link-wrap input {
  flex: 1;
  height: 100%;
  border: none;
  background: transparent;
  color: var(--green-dark);
  font-size: 3cqw;
  padding: 2px 8cqw 0 3cqw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
}

.invite-card-link-copy-icon {
  position: absolute;
  right: 2%;
  top: 50%;
  transform: translateY(-50%);
  width: 6cqw;
  height: 6cqw;
  min-width: 20px;
  min-height: 20px;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--green);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.invite-card-link-copy-icon svg {
  width: 70%;
  height: 70%;
}

.invite-card-close-btn {
  position: absolute;
  left: 35.08%;
  top: 89.43%;
  width: 19.61%;
  height: 7.05%;
  transform: translateY(2px);
  border: none;
  background: transparent;
  color: var(--green-dark);
  font-weight: 700;
  font-size: 3.6cqw;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  font-family: Arial, Helvetica, sans-serif;
}

.invite-card-copy-btn {
  position: absolute;
  left: 54.97%;
  top: 89.3%;
  width: 36.46%;
  height: 7.05%;
  transform: translateY(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.5cqw;
  border: none;
  background: transparent;
  color: #fff;
  font-weight: 700;
  font-size: 3.6cqw;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  font-family: Arial, Helvetica, sans-serif;
}

.invite-card-copy-btn svg {
  width: 3.8cqw;
  height: 3.8cqw;
  max-width: 15px;
  max-height: 15px;
  flex-shrink: 0;
}

.invite-state-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.invite-state-row .invite-state-label.deactivated {
  color: #8a2f2f;
  font-weight: 600;
}

/* diagnostic-only: add ?debug=1 to the URL to outline every overlay
   element on the invite card in dashed red with a floating label, so
   misalignment against the background image is easy to see and measure. */
.invite-card-debug [data-diag] {
  outline: 1px dashed #ff2d2d;
  outline-offset: -1px;
  /* no position property here — the [data-diag] elements that are already
     position:absolute (QR area, Close/Copy buttons) must keep that; adding
     position:relative in this same rule previously overrode it and knocked
     them into normal flow. The name/note/link elements are now plain flex
     children (no position of their own), so they get position:relative
     specifically below, just enough to anchor their ::before label. */
}

.invite-card-debug .invite-card-name,
.invite-card-debug .invite-card-note,
.invite-card-debug .invite-card-link-label,
.invite-card-debug .invite-card-link-wrap {
  position: relative;
}

.invite-card-debug [data-diag]::before {
  content: attr(data-diag);
  position: absolute;
  top: -1.4em;
  left: 0;
  font-size: 9px;
  font-family: monospace;
  background: #ff2d2d;
  color: #fff;
  padding: 1px 4px;
  border-radius: 2px;
  white-space: nowrap;
  z-index: 5;
  pointer-events: none;
}

/* ---- linked devices list ---- */

.device-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--green-soft);
  margin-bottom: 8px;
}

.device-row-label {
  font-weight: 500;
  color: var(--green-dark);
  font-size: 14px;
}

.device-row-details {
  min-width: 0;
}

.device-row-context {
  margin-top: 2px;
  color: var(--text);
  font-size: 13px;
}

.device-row-id {
  margin-top: 3px;
  color: var(--muted);
  font-size: 12px;
  overflow-wrap: anywhere;
}

.device-row-id code {
  color: var(--text);
  font-size: 11px;
}

.admin-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0 6px;
  font-size: 14px;
}

/* .num/.num-money on a plain <td> already align right/center via their own
   rules further down, but on a <th> that same class was losing to this
   selector's higher specificity (class+element beats a lone class) and
   silently rendering left-aligned regardless - headers and their values
   pointed different ways in every .admin-table (Qty/Value columns across
   transactions.html, accounting.html, edit-tab.html, catalog.html,
   inventory.html). Restated here with matching specificity so the header
   actually follows the same alignment as the column's own values. */
.admin-table th.num { text-align: right; }
.admin-table th.num-money { text-align: center; }

.admin-table th {
  text-align: left;
  color: var(--muted);
  font-weight: 500;
  padding: 4px 9px;
  font-size: 13px;
}

.admin-table td {
  background: rgba(255, 255, 255, 0.84);
  padding: 8px 9px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.admin-table td:first-child {
  border-left: 1px solid var(--line);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}

.admin-table td:last-child {
  border-right: 1px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.admin-table input {
  padding: 6px 8px;
  font-size: 13px;
}

/* accounting.html's name cell - separate First/Last inputs (matching the
   customer-facing name-capture form's own fields) rather than one free-text
   box, so what an admin types here is always a specific person's real name,
   not a placeholder like "VIP Customer" typed into a generic Name field. */
.name-fields {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  gap: 4px;
  min-width: 130px;
}

.name-fields input {
  flex: 1 1 0;
  min-width: 0;
}

/* Side by side is too cramped on a phone-width screen, so only there do
   First/Last stack top to bottom instead. */
@media (max-width: 640px) {
  .name-fields {
    flex-direction: column;
  }
  .name-fields input {
    width: 100%;
  }
}

.permission-presets {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 14px 0 10px;
}

.permission-table {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 4px 18px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.permission-row {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 6px 0;
  font-size: 13px;
  line-height: 1.35;
  cursor: pointer;
}

.permission-row input {
  width: 16px;
  height: 16px;
  margin: 1px 0 0;
  flex: 0 0 auto;
}

.permission-row.critical {
  color: #8a2f2f;
}

.admin-user-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 4px;
  border-bottom: 1px solid var(--line);
}

.admin-user-row:last-child {
  border-bottom: none;
}

.admin-user-row.inactive {
  opacity: 0.55;
}

.admin-user-identity strong {
  display: block;
}

.admin-user-perms {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.admin-user-perms .pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--green-light);
  color: var(--green-dark);
  font-size: 11px;
  font-weight: 600;
}

.transaction-diffuser {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 10px 0 4px;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.35;
}

.transaction-diffuser input {
  width: 16px;
  height: 16px;
  margin: 1px 0 0;
  flex: 0 0 auto;
}

.transaction-draggable {
  cursor: grab;
}

.transaction-draggable:active {
  cursor: grabbing;
}

.transaction-draggable.is-dragging {
  opacity: 0.45;
}

.transaction-draggable.is-drop-target td {
  border-top-color: var(--green);
  border-bottom-color: var(--green);
  background: var(--green-light);
}

#accounting-list,
#transaction-list,
#adjustment-list {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  -webkit-overflow-scrolling: touch;
}

@media (max-width: 720px) {
  #accounting-list .admin-table {
    min-width: 1120px;
  }

  #transaction-list .admin-table {
    min-width: 760px;
  }

  #adjustment-list .admin-table {
    min-width: 560px;
  }

  #accounting-list,
  #transaction-list,
  #adjustment-list {
    margin-inline: -10px;
    padding: 0 10px 8px;
    scrollbar-width: thin;
  }

  .transaction-diffuser {
    padding-block: 12px 7px;
  }

  .admin-table td:last-child {
    white-space: normal !important;
  }

  .admin-table td:last-child button {
    margin: 2px;
  }
}

/* Snack catalog row view keeps only the fields used during routine edits.
   Photo and facts IDs are preserved as hidden values so the useful columns
   can take the full width. */
.catalog-table {
  table-layout: fixed;
}
.cc-name { width: 32%; }
.cc-price { width: 13%; }
.cc-stock { width: 12%; }
.cc-active { width: 13%; }
.cc-calories { width: 9%; }
.cc-actions { width: 21%; }

/* "Not tracked" was rendering at the browser's default placeholder color,
   which reads as almost invisible against the field's white background at
   this font size - a real, legible muted color instead. */
.catalog-table input::placeholder,
.catalog-card input::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* Calories can only ever be a small number (snack calories are under 1000,
   enforced by the max=999 attribute plus a matching input clamp) - it
   doesn't need to share the same width as Price/Stock. */
.cc-calories-input {
  max-width: 80px;
}

.catalog-table td input {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* table-layout:fixed's percentage columns just proportionally shrink on a
   narrow screen instead of reflowing - Price/Stock/Calories collapse to
   an unreadable sliver and Status's own dropdown can visually collide
   with the Saved-label/action icons next to it. Same fix already used
   for accounting.html/transactions.html's wide tables: let the columns
   keep their real, readable widths and scroll horizontally instead of
   squeezing everything below usable width. #catalog-list also holds the
   Gallery view's card grid, which never needs to scroll on its own, so
   this is harmless there. */
@media (max-width: 720px) {
  #catalog-list {
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Fixed px per column here (not the desktop percentages) specifically
     to give Name half its desktop-proportional width on mobile - Price/
     Stock/Status/Calories/Actions keep the same pixel width they'd get
     at the 800px reference size, only Name (256px there) drops to 128px,
     and the table's own min-width shrinks by exactly that difference. */
  #catalog-list .catalog-table {
    min-width: 672px;
  }
  #catalog-list .cc-name { width: 128px; }
  #catalog-list .cc-price { width: 104px; }
  #catalog-list .cc-stock { width: 96px; }
  #catalog-list .cc-active { width: 104px; }
  #catalog-list .cc-calories { width: 72px; }
  #catalog-list .cc-actions { width: 168px; }
}

/* Shown while an admin page checks whether the browser already has a
   signed-in session, so the login form never flashes on screen first for
   an admin who's already authenticated - see FS.admin.gateAuth. */
.auth-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 70px 20px; color: var(--muted); font-size: 14px; }
.auth-loading .spinner { width: 18px; height: 18px; border: 2px solid var(--line); border-top-color: var(--green); border-radius: 50%; animation: auth-spin .7s linear infinite; }
@keyframes auth-spin { to { transform: rotate(360deg); } }

/* Native number-input spinner arrows eat into the visible text area, which
   in a narrow Price/Calories column clips the digits themselves. */
.catalog-table input[type="number"] {
  -moz-appearance: textfield;
}
.catalog-table input[type="number"]::-webkit-inner-spin-button,
.catalog-table input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.catalog-table td:last-child button:not(.icon-button) {
  padding: 6px 8px;
  font-size: 12.5px;
}

.catalog-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 12px;
}

.catalog-upload-harness {
  display: grid;
  grid-template-columns: minmax(180px, .7fr) repeat(2, minmax(220px, 1fr));
  gap: 20px;
  align-items: start;
  margin-top: 18px;
}

/* justify-self keeps the preview+input pair centered in its grid cell
   instead of hugging the left edge once the cell grows past the 460px cap. */
.artwork-upload-item {
  display: grid;
  gap: 12px;
  max-width: 460px;
  justify-self: center;
  width: 100%;
}

/* Same side-by-side treatment for the per-card upload modal
   (js/admin-modals.js) - a dedicated class rather than reusing .form-grid,
   which stays a generic multi-column layout used by unrelated forms
   elsewhere. */
.artwork-upload-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(200px, 1fr));
  gap: 18px;
}

/* Side by side is too cramped on a phone-width screen, so only there do the
   items read top to bottom instead. */
@media (max-width: 640px) {
  .catalog-upload-harness,
  .artwork-upload-grid {
    grid-template-columns: 1fr;
  }
}

/* A fixed 150px height read as a thin strip regardless of how wide its
   grid column stretched - aspect-ratio keeps both previews (catalog photo
   and favorite background alike) a real square that grows with the
   column, so uploads are actually easy to inspect. */
.artwork-preview {
  position: relative;
  aspect-ratio: 1 / 1;
  height: auto;
  display: grid;
  place-items: center;
  overflow: hidden;
  border: 1px solid #d9e5dc;
  border-radius: 14px;
  background: #f6faf7;
  color: #68756b;
}

/* grid-area 1/1 on both stacks them in the same cell (instead of the
   default one-per-row auto-placement now that the box holds an empty-state
   span AND an img at once) so whichever isn't [hidden] fills the box on
   its own. */
.artwork-preview-empty,
.artwork-preview-img {
  grid-area: 1 / 1;
}

/* contain (not cover) so the whole photo is always visible at rest instead
   of being cropped to fill the square - js/admin-modals.js then layers
   drag-to-pan and scroll/pinch-to-zoom (via transform) on top so admins can
   inspect any part of a non-square upload at full resolution. */
.artwork-preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  transform-origin: center center;
  touch-action: none;
  user-select: none;
  -webkit-user-drag: none;
}

.artwork-preview-img[hidden] {
  display: none;
}

.artwork-preview-empty {
  font-size: 0.9rem;
}

/* Only shown once zoomed past 1x (toggled in JS) - lets an admin snap back
   to the full-image view without hunting for the right scroll/drag amount. */
.artwork-preview-reset {
  position: absolute;
  right: 8px;
  bottom: 8px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: rgba(24, 79, 42, 0.75);
  color: #fff;
  cursor: pointer;
  padding: 0;
}

.artwork-preview-reset .ui-icon {
  width: 15px;
  height: 15px;
}

.artwork-preview-reset:hover {
  background: rgba(24, 79, 42, 0.92);
}

.catalog-upload-harness input[type="file"] {
  padding: 9px;
  background: #fff;
}

.catalog-toolbar h2,
.catalog-toolbar p {
  margin-bottom: 0;
}

.catalog-view-toggle {
  display: inline-flex;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
}

.catalog-view-toggle button {
  padding: 6px 12px;
  border: 0;
  border-radius: 999px;
  background: transparent;
}

.catalog-view-toggle button[aria-pressed="true"] {
  background: var(--green);
  color: #fff;
}

.catalog-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 14px;
}

.catalog-card {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow);
}

.catalog-card[draggable="true"] {
  position: relative;
}

.catalog-card.dragging {
  opacity: .48;
  outline: 2px dashed #087a46;
}

.catalog-drag-handle {
  position: absolute;
  z-index: 2;
  top: 10px;
  right: 10px;
  padding: 7px 9px;
  border: 1px solid #cbdccf;
  border-radius: 999px;
  background: rgba(255, 255, 255, .94);
  color: #0b633c;
  font-weight: 800;
  line-height: 1;
  cursor: grab;
  user-select: none;
}

/* Opens the artwork upload modal for this card's own two images, right on
   the photo - bottom-right so it doesn't collide with the drag handle. */
.catalog-photo-upload {
  position: absolute;
  z-index: 2;
  bottom: 10px;
  right: 10px;
  border: 1px solid #cbdccf;
  background: rgba(255, 255, 255, .94);
  color: #0b633c;
}

.catalog-card.dragging .catalog-drag-handle {
  cursor: grabbing;
}

.section-heading-row,
.bin-editor-actions,
.bin-location-heading,
.bin-location-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

.bin-page-shell {
  position: relative;
}

.bin-page-content {
  min-width: 0;
}

.bin-layout-well {
  position: fixed;
  z-index: 30;
  top: 0;
  bottom: 0;
  left: 0;
  width: min(300px, calc(100vw - 54px));
  overflow: auto;
  padding: 18px;
  border: 1px solid #cddfd2;
  border-width: 0 1px 0 0;
  border-radius: 0 18px 18px 0;
  background: #f4f9f5;
  box-shadow: 0 12px 30px rgba(10, 70, 40, .09);
  transform: translateX(calc(-100% - 12px));
  transition: transform .22s ease;
}

.bin-layout-well.is-open {
  transform: translateX(0);
}

.bin-layout-toggle {
  position: fixed;
  z-index: 31;
  top: 104px;
  left: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 10px 13px;
  border-radius: 0 12px 12px 0;
  box-shadow: 0 8px 22px rgba(10, 70, 40, .16);
  transition: left .22s ease;
}

.bin-layout-toggle.drawer-open {
  left: min(300px, calc(100vw - 54px));
}

.bin-layout-well-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

.bin-layout-well-heading > div {
  display: grid;
  gap: 2px;
}

.bin-layout-close {
  min-width: 34px;
  padding: 2px 8px;
  border: 0;
  background: transparent;
  color: #075b35;
  font-size: 1.7rem;
  line-height: 1;
}

.bin-layout-well-heading strong {
  color: #075b35;
  font-size: 1.08rem;
  font-weight: 600;
}

.bin-layout-well-heading span {
  color: #647269;
  font-size: .78rem;
}

.layout-floor + .layout-floor {
  margin-top: 15px;
}

.layout-floor-name {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
  color: #284e39;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  cursor: grab;
}

.layout-floor.dragging-floor,
.bin-floor-group.dragging-floor {
  opacity: .48;
  outline: 2px dashed #087a46;
  outline-offset: 4px;
}

.floor-drag-grip {
  color: #087a46;
  cursor: grab;
  font-weight: 500;
  letter-spacing: -.18em;
}

.layout-bin-list {
  display: grid;
  gap: 6px;
}

.layout-bin-item {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: center;
  gap: 6px;
  padding: 8px;
  border: 1px solid #d8e5db;
  border-radius: 10px;
  background: #fff;
  color: #294d38;
  cursor: grab;
  font-size: .86rem;
}

.layout-bin-item.dragging {
  opacity: .45;
  outline: 2px dashed #087a46;
}

.layout-grip {
  color: #087a46;
  font-weight: 500;
}

.section-heading-row h2,
.section-heading-row p,
.bin-location-heading h4 {
  margin-bottom: 0;
}

.bin-form-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  margin: 20px 0;
}

.bin-item-editor {
  max-height: 308px;
  overflow: auto;
  border: 1px solid #dce7df;
  border-radius: 14px;
}

.bin-snack-search {
  max-width: 520px;
  margin-bottom: 12px;
}

.bin-item-editor thead th,
.bin-card-table-scroll thead th {
  position: sticky;
  z-index: 1;
  top: 0;
  background: #f4f9f5;
}

.bin-quantity-table {
  margin: 0;
}

.bin-quantity-table tr,
.bin-location-card .compact-table tr {
  height: 44px;
}

.bin-quantity-table input[type="number"] {
  width: 86px;
  text-align: center;
}

.bin-editor-actions {
  justify-content: flex-end;
  margin-top: 16px;
}

.bin-editor-total {
  margin-right: auto;
  font-size: 1.05rem;
}

.bin-floor-group + .bin-floor-group {
  margin-top: 28px;
}

.bin-floor-group { position: relative; }

.bin-floor-group h3 {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 12px;
}

.bin-floor-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  margin-bottom: 12px;
  padding: 14px 16px;
  border: 1px solid #cadbce;
  border-left: 5px solid #087a46;
  border-radius: 14px;
  background: linear-gradient(135deg, #f3f9f5, #fff);
}

.bin-floor-heading h3 {
  margin-bottom: 0;
}

.inline-floor-wrap {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

.inline-floor-name,
.inline-bin-name {
  border: 1px solid transparent;
  background: transparent;
  color: #075b35;
  font-family: inherit;
  font-weight: 600;
}

.inline-name-display,
.inline-value-display {
  min-height: 0;
  padding: 4px 6px;
  border: 1px solid transparent;
  background: transparent;
  color: #075b35;
  font-family: inherit;
  font-weight: 600;
  text-align: left;
}

.inline-name-display:hover,
.inline-name-display:focus,
.inline-value-display:hover,
.inline-value-display:focus {
  border-color: #99bda6;
  background: #fff;
}

.floor-name-display {
  font-size: 1.28rem;
}

.bin-name-display {
  font-size: 1.05rem;
}

.inline-value-display {
  min-width: 54px;
  font-weight: 500;
  text-align: center;
}

.inline-floor-name {
  width: min(280px, 55vw);
  padding: 5px 7px;
  font-size: 1.28rem;
}

.inline-bin-name {
  width: min(220px, 45vw);
  padding: 3px 5px;
  font-size: 1.05rem;
}

.inline-floor-name:hover,
.inline-floor-name:focus,
.inline-bin-name:hover,
.inline-bin-name:focus {
  border-color: #99bda6;
  background: #fff;
  outline: none;
}

.bin-floor-actions {
  display: flex;
  gap: 8px;
}

.action-button {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-weight: 500;
}

.bin-location-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

.bin-location-card {
  position: relative;
  overflow: hidden;
  border: 1px solid #dce7df;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 8px 22px rgba(19, 69, 44, .07);
}

.bin-location-card.dragging {
  opacity: .5;
  outline: 2px dashed #087a46;
}

.bin-location-drag {
  position: absolute;
  z-index: 3;
  top: 10px;
  right: 10px;
  padding: 7px 9px;
  border: 1px solid #cbdccf;
  border-radius: 999px;
  background: rgba(255, 255, 255, .95);
  color: #0b633c;
  line-height: 1;
  cursor: grab;
  user-select: none;
}

.bin-location-card.dragging .bin-location-drag {
  cursor: grabbing;
}

.bin-location-card .bin-location-heading {
  padding-right: 58px;
}

.bin-location-heading,
.bin-location-actions {
  padding: 14px 16px;
}

.bin-location-heading {
  background: #f4f9f5;
}

.bin-location-heading strong {
  color: #075b35;
  font-weight: 500;
  white-space: nowrap;
}

.bin-title-row {
  display: flex;
  align-items: center;
  gap: 5px;
}

.bin-title-row > .ui-icon {
  flex: 0 0 auto;
  color: #4c705b;
}

.bin-location-card .compact-table {
  margin: 0;
  border-radius: 0;
  box-shadow: none;
}

.bin-card-table-scroll {
  max-height: 308px;
  overflow: auto;
  scrollbar-gutter: stable;
}

.compact-table th,
.compact-table td {
  padding: 8px 10px;
}

.bin-location-actions {
  justify-content: flex-end;
  border-top: 1px solid #edf2ee;
}

.bin-location-actions span {
  margin-right: auto;
  color: #5c6b61;
}

.icon-button {
  display: inline-grid;
  place-items: center;
  min-width: 40px;
  padding: 8px 10px;
}

.ui-icon {
  width: 19px;
  height: 19px;
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.bin-location-drag .ui-icon {
  width: 18px;
  height: 18px;
  fill: currentColor;
  stroke: none;
}

.inline-qty-input {
  width: 70px;
  min-height: 36px;
  padding: 5px 6px;
  text-align: center;
  font-weight: 500;
}

.floor-analysis {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.floor-analysis-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.floor-analysis-title {
  color: var(--green-dark);
  font-size: 1rem;
  font-weight: 500;
}

.floor-analysis-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 9px;
}

.floor-analysis-card {
  display: grid;
  gap: 8px;
  padding: 11px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(255, 255, 255, .68);
}

.floor-analysis-name {
  color: var(--green-dark);
  font-size: .92rem;
  font-weight: 500;
}

.floor-analysis-value {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  font-size: 1rem;
  font-weight: 500;
}

.floor-analysis-value span,
.floor-analysis-meta {
  color: var(--muted);
  font-size: .76rem;
}

.floor-analysis-meta {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}

.floor-banner-totals {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  margin-left: auto;
  color: var(--green-dark);
}

.floor-banner-totals > span {
  display: grid;
  gap: 1px;
  min-width: 86px;
  font-size: 1rem;
  font-weight: 500;
}

.floor-banner-totals small {
  color: var(--muted);
  font-size: .7rem;
  font-weight: 400;
  letter-spacing: .02em;
}

.bin-add-cutout {
  width: clamp(150px, calc((100% - 1040px) / 2 - 32px), 210px);
  min-height: 0;
  aspect-ratio: 1.18 / 1;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 6px;
  margin: 0;
  padding: 18px 14px;
  border: 2px dashed #9bbda5;
  border-radius: 10px;
  background: rgba(244, 249, 245, .7);
  color: #0b633c;
  text-align: center;
}

.bin-add-cutout:hover,
.bin-add-cutout:focus {
  border-color: #087a46;
  background: #edf8f1;
  transform: translateY(-2px);
}

.bin-add-cutout.drop-ready {
  border-color: #087a46;
  background: #dff3e7;
  box-shadow: inset 0 0 0 3px rgba(8, 122, 70, .12);
  transform: scale(1.01);
}

.bin-add-symbol {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: #087a46;
  color: #fff;
  line-height: 1;
}

.bin-add-symbol .ui-icon {
  width: 25px;
  height: 25px;
  stroke-width: 1.8;
}

.bin-add-title {
  font-size: 1.08rem;
  font-weight: 500;
}

.bin-add-cutout small {
  color: #637268;
  font-size: .82rem;
  line-height: 1.25;
}

.bin-add-floating {
  position: fixed;
  z-index: 24;
  top: clamp(170px, 50vh, calc(100vh - 170px));
  right: max(16px, calc((100% - 1460px) / 4));
  transform: translateY(-50%);
  box-shadow: 0 10px 24px rgba(10, 70, 40, .12);
}

.bin-add-floating:hover,
.bin-add-floating:focus {
  transform: translateY(calc(-50% - 2px));
}

.bin-add-floating.drop-ready {
  transform: translateY(-50%) scale(1.03);
}

@media (max-width: 1390px) {
  #global-add-bin.bin-add-floating {
    display: none !important;
    visibility: hidden;
    pointer-events: none;
  }
}

.bin-copy-dialog {
  width: min(520px, calc(100vw - 28px));
  padding: 24px;
  border: 0;
  border-radius: 18px;
  box-shadow: 0 24px 70px rgba(0, 34, 18, .28);
}

.bin-copy-dialog::backdrop {
  background: rgba(0, 18, 10, .58);
}

.bin-copy-dialog form {
  position: relative;
  display: grid;
  gap: 16px;
}

/* Customer transaction and payment ledger */
.user-ledger-overview {
  display: grid;
  grid-template-columns: repeat(3, minmax(130px, 1fr)) auto;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  background: rgba(246, 250, 247, .78);
}

.user-ledger-overview > div {
  display: grid;
  gap: 2px;
  color: var(--text);
  font-size: .96rem;
  font-weight: 500;
}

.user-ledger-overview > div span {
  color: var(--muted);
  font-size: .72rem;
  font-weight: 400;
}

.user-ledger-overview .credit { color: #087a46; }
.user-ledger-overview .due { color: #8a4d16; }

.collapsed-ledger-stats {
  display: grid;
  grid-column: 3 / 6;
  grid-template-columns: repeat(3, minmax(84px, auto));
  gap: 8px;
  text-align: right;
}

.collapsed-ledger-stats > span {
  display: grid;
  gap: 1px;
  color: var(--ink);
  font-size: .9rem;
  font-variant-numeric: tabular-nums;
}

.collapsed-ledger-stats small {
  color: var(--muted);
  font-size: .67rem;
  font-weight: 500;
  letter-spacing: .03em;
  text-transform: uppercase;
}

.collapsed-ledger-stats .credit { color: #087a46; }
.collapsed-ledger-stats .due { color: #8a4d16; }

/* <summary> stays visible whether its <details> is open or closed, so its
   Purchases/Payments/Balance stats used to sit right on top of the same
   three numbers restated in .user-ledger-overview just below, once
   expanded. Hide the summary's copy once open - the full one (which also
   has Record payment) is the one that matters then. */
details[open] > summary .collapsed-ledger-stats {
  display: none;
}

.professional-action-icon svg { stroke-width: 1.8; }
.professional-action-icon.danger { color: #a63b35; }
.professional-action-icon.payment-action-icon {
  color: #17623a;
  background: #eef7f0;
  border-color: #b9d2c0;
}
.credit-balance { color: #087a46; font-weight: 500; }

/* A zero balance means snacks and payments already net out exactly - a
   healthy account, worth a quiet visual "nothing to do here" cue across
   the whole row rather than just the one Balance cell. */
tr.healthy-account td {
  background: var(--green-light);
}

.ledger-type,
.ledger-status {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 3px 7px;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 500;
  line-height: 1;
}

.ledger-type {
  margin-right: 5px;
  border: 1px solid #d4e2d7;
  color: #52645a;
  background: #f7faf8;
}

.ledger-type.payment,
.ledger-status.paid {
  border-color: #b9d8c2;
  color: #075b35;
  background: #eaf6ed;
}

/* Distinct from Purchase (neutral grey) and Payment (green) - adjustments
   moved here from their own accounting.html column, so they need to read
   as their own kind of ledger entry at a glance, not blend into either. */
.ledger-type.adjustment {
  border-color: #cfc6ea;
  color: #4a3aa7;
  background: #f1eefa;
}

.ledger-status.neutral {
  color: #66736a;
  background: #eef2ef;
}

.ledger-status.approved {
  color: #29566f;
  background: #e9f2f7;
}

.payment-row { background: rgba(234, 246, 237, .42); }
.payment-value { color: #087a46; }
.ledger-actions { white-space: nowrap; }
.ledger-actions button + button { margin-left: 4px; }
/* Right-justified so the ⋮ menu lands in the same spot whether or not a
   Mark paid button also occupies this cell (see renderTxnActions/
   renderPaymentActions in transactions.html). */
.ledger-actions-row { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.ledger-actions-row button + button,
.ledger-actions-row .row-menu + button,
.ledger-actions-row button + .row-menu { margin-left: 0; }
.permanent-note { color: var(--muted); font-size: .78rem; }

.payment-modal { width: min(540px, calc(100vw - 28px)); }
.payment-form { display: grid; gap: 14px; margin-top: 16px; }

.payment-ledger-link-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
}

.payment-ledger-link-card h2,
.payment-ledger-link-card p { margin-bottom: 0; }

.admin-test-profile-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 18px;
}

.admin-test-profile-card h2,
.admin-test-profile-card p { margin-bottom: 0; }
.admin-test-profile-card .status { grid-column: 1 / -1; }

.admin-test-profile-controls {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.admin-test-profile-controls select,
.admin-test-profile-controls input {
  min-width: 190px;
}

.app-health-alert {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  border-color: #d9a3a0;
  background: #fff6f5;
  color: #7f2924;
}

.app-health-alert > div { display: grid; gap: 4px; }
.app-health-alert span { color: #6f4542; font-size: .88rem; }
.app-health-alert button { flex: 0 0 auto; }

@media (max-width: 760px) {
  .user-ledger-overview {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .user-ledger-overview > button { width: 100%; }
  .collapsed-ledger-stats { grid-column: 1 / -1; width: 100%; grid-template-columns: repeat(3, 1fr); padding-top: 6px; text-align: left; }
  #txn-groups details > .admin-table { display: block; overflow-x: auto; }
  .payment-ledger-link-card { align-items: stretch; flex-direction: column; }
  .admin-test-profile-card { grid-template-columns: 1fr; }
  .admin-test-profile-controls { flex-direction: column; align-items: stretch; }
  .app-health-alert { align-items: stretch; flex-direction: column; }
  .app-health-alert button { width: 100%; }
}

.bin-copy-dialog h2,
.bin-copy-dialog p {
  margin-bottom: 0;
}

.dialog-close {
  position: absolute;
  top: -12px;
  right: -10px;
  min-width: 38px;
  padding: 5px;
  border: 0;
  background: transparent;
  font-size: 1.7rem;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

@media (max-width: 780px) {
  .bin-form-grid,
  .bin-location-grid {
    grid-template-columns: 1fr;
  }

  .section-heading-row,
  .bin-editor-actions {
    align-items: stretch;
    flex-direction: column;
  }

  .bin-floor-heading {
    align-items: stretch;
    flex-direction: column;
  }

  .floor-banner-totals {
    justify-content: flex-start;
    margin-left: 0;
  }

  .bin-floor-actions button {
    flex: 1;
  }

  .inline-floor-wrap {
    align-items: stretch;
    flex-direction: column;
    gap: 3px;
  }

  .inline-floor-name,
  .inline-bin-name {
    width: 100%;
  }

  .bin-editor-total {
    margin-right: 0;
  }
}

.catalog-card-photo {
  position: relative;
  height: 190px;
  overflow: hidden;
  background: linear-gradient(160deg, rgba(244,250,243,0.9), rgba(227,239,225,0.9));
  border-bottom: 1px solid var(--line);
}

.catalog-card-photo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
  padding: 8px;
  box-sizing: border-box;
}

.catalog-card-body {
  padding: 14px;
}

.catalog-card-fields {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  margin-top: 10px;
}

/* Calories doesn't drive any admin decision here (it's purely a fact shown
   to customers on their own snack cards) - demoted below the fields that
   actually matter day-to-day (price, stock, active) instead of sharing
   equal billing with them, and kept small since it can only ever be a
   3-digit number. */
.catalog-card-secondary {
  margin-top: 8px;
}

.catalog-card-secondary label {
  font-weight: 400;
}

.catalog-card-cal input {
  width: 80px;
}

.catalog-card input,
.catalog-card select {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* Replaces the old Save/Deactivate button row - every field writes on its
   own three-second debounce now, so all that's left here is a quiet status
   word and the one remaining destructive action (permanent delete).
   Active/Inactive in the fields grid above is the only status control -
   a separate Deactivate button was redundant with it. */
.catalog-card-footer {
  align-items: center;
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
}

.catalog-save-status {
  color: var(--muted);
  font-size: 12.5px;
}

.catalog-save-status.saving {
  color: #8a6d1a;
}

.catalog-save-status.err {
  color: #a63b35;
  font-weight: 600;
}

.catalog-row-actions {
  align-items: center;
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

@media (max-width: 560px) {
  .catalog-toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .catalog-view-toggle {
    align-self: flex-start;
  }

  .catalog-gallery {
    grid-template-columns: 1fr;
  }
}

.nowrap {
  white-space: nowrap;
}

/* Row options menu: one icon button instead of a crowded row of buttons -
   frees up column width elsewhere in the table (e.g. Last activity no
   longer gets squeezed into wrapping) and reads as a single clean action
   rather than a button-group. */
.row-menu {
  position: relative;
  display: inline-block;
}

.row-menu-toggle {
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 50%;
  font-size: 18px;
  line-height: 1;
  display: inline-grid;
  place-items: center;
}

/* position:fixed, not absolute - several of this dropdown's containers
   (accounting.html's #accounting-list, transactions.html's #txn-groups,
   edit-tab.html's #transaction-list/#adjustment-list) scroll horizontally,
   which clips an absolutely-positioned dropdown opening below the LAST
   row in the list (nowhere left inside the container to render into).
   Fixed positioning escapes any ancestor's overflow entirely; its top/
   left are computed and set by each page's own positionRowMenuDropdown()
   when it opens, rather than the `top: calc(100% + 4px); right: 0` an
   absolutely-positioned version could rely on. */
.row-menu-dropdown {
  position: fixed;
  min-width: 170px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14);
  z-index: 30;
  overflow: hidden;
  display: none;
}

.row-menu.open .row-menu-dropdown {
  display: block;
}

.row-menu-dropdown button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  border: none;
  border-radius: 0;
  background: none;
  font-size: 13.5px;
  color: var(--text);
}

.row-menu-dropdown button:hover {
  background: rgba(47, 122, 68, 0.08);
}

.row-menu-dropdown button.danger {
  color: #b3261e;
}

.row-menu-divider {
  height: 1px;
  background: var(--line);
  margin: 4px 0;
}

/* A fixed repeat(3, 1fr) kept forcing 3 columns into whatever room was
   left beside the basket well's fixed 360px width even as the viewport
   narrowed toward the 900px breakpoint (see .bins-layout above),
   squeezing each card down as far as ~134px wide there. auto-fill/
   minmax fixed the squeeze but introduced a different bug: auto-fill
   still creates as many column TRACKS as geometrically fit (sharing the
   row's width evenly across all of them, filled or not) - with a small
   real catalog (as few as 3 snacks), that left extra empty tracks
   eating a large visible gap next to the basket.
   A plain viewport-width @media breakpoint isn't right either, because
   how much room the GALLERY actually has depends on more than the
   viewport - a visitor (no basket well to share space with, see
   .bins-layout's :has(#basket-panel.hidden) rule above) gets the full
   .bins-layout width to itself, while a customer with an active tab
   only gets what's left after the well's fixed 360px - two very
   different available widths at the exact same viewport size. A
   container query keyed off .bins-gallery-col's own rendered width
   (see container-type below) reads the space this grid ACTUALLY has,
   regardless of why it has it, so both cases size correctly without
   two separate breakpoints to keep in sync by hand. 800px is chosen
   because that's ~.page-wide's max width (1280px) minus the basket
   well (360px + gap) minus the section's own padding - i.e. the most
   room a customer-with-a-tab's gallery ever has, so 3 columns become
   available there too, not just for a visitor's full-width gallery. */
.bins-gallery-col {
  container-type: inline-size;
}

.bin-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

@container (min-width: 800px) {
  .bin-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.snack-shop-section {
  padding: 28px;
}

.snack-shop-heading {
  text-align: center;
  margin: 2px auto 28px;
}

.snack-shop-kicker {
  color: var(--muted);
  display: block;
  font-size: 12px;
  letter-spacing: 0.12em;
  margin-bottom: 7px;
  text-transform: uppercase;
}

.snack-shop-heading h2 {
  color: var(--green-dark);
  font-size: clamp(28px, 3.4vw, 42px);
  font-weight: 500;
  letter-spacing: -0.035em;
  margin: 0;
}

.snack-shop-heading h2::after {
  background: var(--green);
  border-radius: 99px;
  content: "";
  display: block;
  height: 3px;
  margin: 13px auto 14px;
  width: 72px;
}

.snack-shop-heading p {
  color: var(--muted);
  margin: 0;
}

/* Gallery + persistent cart well, side by side on desktop/tablet - the well
   stays in view via position:sticky rather than being hidden behind a
   floating button, so a shopper can always see what they've picked while
   browsing the rest of the gallery. align-items stays at its grid default
   (stretch) rather than "start" so .bins-cart-col (the grid item) stretches
   to match the gallery column's full height - but the sticky positioning
   itself lives on .bins-cart-sticky (the inner card), NOT on .bins-cart-col.
   A sticky element only has room to float for as much extra space as its
   OWN box is smaller than its containing block; putting sticky directly on
   .bins-cart-col, which is stretched to be exactly as tall as its own
   containing block (the grid row), leaves it zero slack to move within -
   it just scrolls away with the page instead of pinning. Keeping
   .bins-cart-col a plain stretched (tall) grid item and making the
   naturally-small .bins-cart-sticky inside it the sticky element gives
   sticky the room it actually needs.
   Below the 900px breakpoint there's no room for gallery + well side by
   side, so it switches over to the floating button + slide-in drawer
   pattern instead (see that breakpoint below). */
.bins-layout {
  display: grid;
  gap: 24px;
  grid-template-columns: minmax(0, 1fr) 360px;
}

.bins-layout:has(#basket-panel.hidden) {
  grid-template-columns: minmax(0, 1fr);
}

/* index.html and index-static-basket.html: #basket-panel isn't inside
   .bins-layout on these pages (it lives up in .recommendations-row), so
   .bins-gallery-col is .bins-layout's only child - but without this
   override the grid still reserves the second 360px track for a basket
   column that's never there, leaving the gallery/cards stuck ~360px+gap
   narrower than the section. edit-tab.html (no #snack-shop id) keeps the
   two-column grid for its own side-by-side admin basket. */
body:has(#snack-shop) .bins-layout,
body:has(#snack-shop-static) .bins-layout {
  grid-template-columns: minmax(0, 1fr);
}

/* index-static-basket.html only: the "Keep basket static" evaluation
   toggle. Fixed regardless of scroll (so it's reachable at any point on
   the page, including once the basket itself is floating over the same
   top area of the screen) and given a very high z-index so it always
   sits above the floating basket dropdown, not under it. Right-anchored,
   flush against the edge (right:1px) per request - top:60px (not the
   more typical 10-12px) specifically to clear #cashback-notifications-
   bell, which is fixed at top:10/right:10 and would otherwise sit
   directly underneath/overlapping this on any account with a live cash
   back notification. */
.basket-static-toggle {
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 4px 14px rgba(24, 79, 42, 0.14);
  color: var(--green-dark);
  cursor: pointer;
  display: flex;
  font-size: 13px;
  font-weight: 500;
  gap: 8px;
  padding: 9px 16px 9px 14px;
  position: fixed;
  right: 1px;
  top: 60px;
  user-select: none;
  z-index: 100;
}

.basket-static-toggle input {
  accent-color: var(--green);
  height: 15px;
  margin: 0;
  width: 15px;
}

/* index-static-basket.html only: places the basket at the end of the
   "Three snacks to consider" row instead of as a full-width block of its
   own - see the HTML comment above .recommendations-row for why the
   basket is a flex sibling of #recommendations-section rather than nested
   inside it. align-items:stretch (below) makes #basket-dock-home exactly
   as tall as #recommendations-section, its row sibling - which means
   .bins-cart-sticky's own position:sticky (unscoped, shared with
   edit-tab.html) now DOES have real slack to float within, unlike the
   old flex-start layout - explicitly killed off in the height-matching
   block below, since this page needs it fully static regardless. */
.recommendations-row {
  align-items: stretch;
  display: flex;
  gap: 18px;
  margin-bottom: 14px;
}

.recommendations-row #recommendations-section {
  flex: 3 3 0;
  margin-bottom: 0;
  min-width: 0;
}

/* #basket-dock-home (not #basket-panel itself) carries the reserved width,
   since #basket-panel gets reparented out to <body> while floating (see
   updateBasketFloatState) - keeping the empty home slot at the same width
   means the row never resizes at the moment it leaves or returns.
   flex-basis:0 with a 1:3 ratio against #recommendations-section (a plain
   fraction, not a fixed px width) makes this settle at roughly the same
   width as one of the three recommendation cards inside that section,
   at any viewport width - a fixed px value would only happen to match at
   one specific width and visibly drift out of proportion at others. */
.recommendations-row #basket-dock-home {
  flex: 1 1 0;
}

@media (max-width: 900px) {
  .recommendations-row {
    flex-direction: column;
  }

  .recommendations-row #basket-dock-home {
    flex-basis: auto;
  }
}

/* Match the basket's default (docked) height to #recommendations-section's
   own height exactly, per explicit request - rather than compacting
   padding to only approximate a recommendation card's height. The stretch
   above only sizes #basket-dock-home itself; height:100% carries that
   down through the plain (non-flex) .bins-cart-col/.bins-cart-sticky
   elements, which otherwise default to their own content height and
   ignore a stretched parent. .bins-cart-sticky is then made a column flex
   container so .bins-cart-body can flex:1 to fill whatever extra room
   that leaves - the empty-state message/item list gets vertically
   centered space instead of leaving dead blank area below the buttons.
   position:static neutralizes .bins-cart-sticky's own position:sticky,
   which would otherwise float within the real slack the match now gives
   it - the opposite of what this "static regular element" page wants. */
/* #basket-dock-home itself is NOT listed here on purpose - it's the
   actual flex item align-items:stretch sizes, and giving it an explicit
   height:100% (against .recommendations-row, whose own height is auto/
   intrinsic - determined BY its stretched children, not the other way
   round) resolves as an indefinite percentage and silently defeats
   stretch instead of reinforcing it. Only its descendants need the
   height:100% chain, since plain block elements don't inherit a
   stretched ancestor's height on their own. */
.recommendations-row #basket-dock-home .bins-cart-col,
.recommendations-row #basket-dock-home .bins-cart-sticky {
  height: 100%;
}

.recommendations-row #basket-dock-home .bins-cart-sticky {
  display: flex;
  flex-direction: column;
  padding: 12px 14px;
  position: static;
  top: auto;
}

.recommendations-row #basket-dock-home .bins-cart-header {
  font-size: 15px;
  margin-bottom: 6px;
  padding-bottom: 6px;
}

.recommendations-row #basket-dock-home .bins-cart-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

/* Per explicit request: the docked basket shouldn't expand in place as
   snacks are added (an itemized list grows taller with every addition,
   which would either need internal scrolling in this already-small card
   or break its height match to #recommendations-section) - so it shows
   #bins-cart-summary-line's single aggregate line ("3 snacks selected")
   instead of the full itemized #bins-cart-items-wrap list. #basket-panel
   lives inside #basket-dock-home permanently now (see its own HTML
   comment - it no longer gets reparented anywhere), so this is simply
   its one, permanent look; the full itemized breakdown still exists, but
   only inside #basket-float-shell's tray - a wholly separate element,
   see its own CSS/HTML comments further down. */
.recommendations-row #basket-dock-home .bins-cart-items-wrap {
  display: none;
}

.recommendations-row #basket-dock-home .bins-cart-summary-line {
  align-items: center;
  display: flex;
  flex: 1;
  margin-bottom: 6px;
}

.recommendations-row #basket-dock-home .bins-cart-total {
  margin-bottom: 8px;
}

/* index-static-basket.html only: #basket-float-shell is the floating
   "pull tab" shown once you scroll past the basket's docked spot
   (#basket-dock-home above) - ported from a standalone design reference
   (a decorative SVG-framed header with a notch/tab you click to pull a
   tray open below it) into a live widget, wired to the same real
   picked-items data as the docked basket via syncFloatShell(). It's a
   wholly separate element from #basket-panel, not a repositioned copy of
   it - the compact docked card and this decorative pull-tab diverge
   enough visually that sharing one DOM node stopped making sense; see
   the HTML comment above #basket-float-shell for the fuller history.

   .visible is added the moment you scroll past the trigger point;
   .is-floating-in is added a frame later (double requestAnimationFrame,
   same reasoning as before) so the opacity/transform transition has a
   real "from" state to animate away from instead of landing in the same
   paint as .visible and skipping the transition. */
.basket-float-shell {
  color: var(--green);
  /* Hidden outright by default, not just via opacity - whatever the JS
     scroll/layout logic gets wrong on a given load, an un-displayed
     element can't flash on screen or intercept a tap. .visible flips
     this to display:block first; opacity/transform only animate once
     .is-floating-in lands a frame later (see updateBasketFloatState). */
  display: none;
  left: 50%;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  top: 10px;
  transform: translateX(-50%) translateY(-10px) scale(0.96);
  transition: transform 240ms cubic-bezier(0.2, 0.8, 0.3, 1), opacity 200ms ease;
  /* ~20% wider than a gallery card (.bin-card runs ~400px at common
     desktop widths), rather than dominating the screen. */
  width: min(94vw, 480px);
  z-index: 40;
}

.basket-float-shell.visible {
  display: block;
  pointer-events: auto;
}

.basket-float-shell.visible.is-floating-in {
  opacity: 1;
  transform: translateX(-50%) translateY(0) scale(1);
}

/* The rounded outline is a plain CSS border, not part of the SVG - see
   the HTML comment above .basket-float-notch for why (the reference
   file's stretched full-frame SVG distorted its corner arcs into
   ellipses that could never sit flush against the white card).
   Translucent frosted-glass background per request - the backdrop blur
   is what keeps gallery content scrolling beneath it from reading as
   smudged text (a plain alpha background without blur did). */
.basket-float-header {
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  background: rgba(255, 255, 255, 0.86);
  border: 2px solid var(--green);
  border-radius: 18px;
  box-shadow: 0 16px 36px rgba(21, 68, 31, 0.16), 0 2px 8px rgba(21, 68, 31, 0.07);
  height: 72px;
  /* the site's generic `header` element rule adds margin-bottom:14px and
     padding - both would wedge a gap between this and the joined tray */
  margin: 0;
  overflow: visible;
  padding: 0;
  position: relative;
}

/* The pull-tab notch - anchored to the bottom of the whole SHELL (it's a
   direct child of .basket-float-shell, not of the header), so it hangs
   under the mini tab while closed and rides down to the bottom of the
   extended basket while the tray is open. bottom:-18px with a 19px-tall
   notch leaves its top edge overlapping the shell's last border line
   (green over green) by 1px - attached with no hairline gap. Lined up
   exactly with .basket-float-tab-hit, the actual click target. */
.basket-float-notch {
  bottom: -18px;
  display: block;
  height: 19px;
  left: 50%;
  pointer-events: none;
  position: absolute;
  transform: translateX(-50%);
  width: 90px;
  z-index: 1;
}

/* inset:0 (against the relative header's padding box) rather than
   height:100% - measured live, the percentage was resolving to a
   collapsed 8px box that pushed the text cluster down over the bottom
   border; absolutely filling the header sidesteps percentage-height
   resolution entirely and pins the grid to exactly the box it should
   center within. */
.basket-float-content {
  align-items: center;
  display: grid;
  gap: 14px;
  grid-template-columns: 1fr auto auto;
  inset: 0;
  padding: 0 16px 0 18px;
  position: absolute;
  z-index: 2;
}

.basket-float-summary {
  align-items: center;
  display: flex;
  gap: 11px;
  min-width: 0;
}

.basket-float-icon {
  color: var(--green-dark);
  display: grid;
  flex: 0 0 auto;
  height: 34px;
  place-items: center;
  width: 34px;
}

.basket-float-icon svg {
  display: block;
  height: 28px;
  overflow: visible;
  width: 28px;
}

.basket-float-labels {
  min-width: 0;
}

.basket-float-title {
  color: var(--green-dark);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1;
  margin: 0;
  white-space: nowrap;
}

.basket-float-sub {
  color: var(--muted);
  font-size: 12px;
  line-height: 1.1;
  margin: 3px 0 0;
  white-space: nowrap;
}

.basket-float-mini-total {
  text-align: right;
  white-space: nowrap;
}

.basket-float-mini-total-label {
  color: var(--muted);
  font-size: 11px;
  margin: 0;
}

.basket-float-mini-total-value {
  color: var(--green-dark);
  font-size: 17px;
  font-weight: 750;
  letter-spacing: -0.02em;
  line-height: 1;
  margin: 3px 0 0;
}

.basket-float-action {
  background: var(--green);
  border: 0;
  border-radius: 10px;
  color: #fff;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  height: 34px;
  padding: 0 14px;
  white-space: nowrap;
}

.basket-float-tab-hit {
  background: transparent;
  border: 0;
  bottom: -21px;
  cursor: pointer;
  height: 26px;
  left: 50%;
  padding: 0;
  position: absolute;
  transform: translateX(-50%);
  width: 90px;
  z-index: 6;
}

.basket-float-tab-hit:focus-visible,
.basket-float-action:focus-visible,
.basket-float-btn:focus-visible {
  outline: 3px solid rgba(47, 122, 68, 0.24);
  outline-offset: 3px;
}

/* Joined directly to the mini tab when open - same width, no gap, green
   border continuing straight down from the header's (whose bottom border
   stays put as the divider between them), rounded only at the bottom.
   Closed, it must contribute exactly ZERO height to the shell (margin 0
   AND border-width 0, not just max-height 0) so the bottom-anchored
   notch/tab-hit sit flush under the header rather than a few px below
   it. */
.basket-float-tray {
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  background: rgba(255, 255, 255, 0.93);
  border: 0 solid var(--green);
  border-radius: 0 0 18px 18px;
  border-top: 0;
  box-shadow: 0 16px 32px rgba(18, 72, 29, 0.13);
  margin: 0 auto;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  pointer-events: none;
  transform: translateY(-6px);
  transition: max-height 380ms ease, opacity 320ms ease, transform 340ms ease;
  width: 100%;
}

.basket-float-shell.open .basket-float-tray {
  border-width: 2px;
  border-top: 0;
  max-height: min(70vh, 520px);
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

/* While open the header is the top half of one continuous panel - square
   off its bottom corners so the tray continues it seamlessly, and thin
   the divider between them down to a hairline: keeping the full 2px
   green border there still read as two stacked carts rather than one
   panel with a separator. */
.basket-float-shell.open .basket-float-header {
  border-bottom: 1px solid var(--line);
  border-radius: 18px 18px 0 0;
}

.basket-float-tray-inner {
  /* transparent - the tray's own frosted rgba background is the surface;
     a solid fill here would defeat it */
  background: transparent;
  padding: 16px 18px 18px;
}

/* No duplicate "Your basket" header in here - the collapsed tab above
   already says that, and repeating it read as two carts stacked on
   screen. The tray holds the real itemized list (the actual
   #bins-cart-items-wrap node, reparented in while open - see
   setFloatTrayOpen), then the total and actions. */
.basket-float-tray-section {
  padding-top: 0;
}

.basket-float-tray-row {
  align-items: baseline;
  border-top: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding-top: 12px;
}

.basket-float-tray-row h3 {
  color: var(--text);
  font-size: 15px;
  font-weight: 700;
  margin: 0;
}

.basket-float-tray-amount {
  color: var(--green-dark);
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
}

.basket-float-tray-actions {
  display: grid;
  gap: 10px;
  grid-template-columns: 96px 1fr;
  margin-top: 14px;
}

.basket-float-btn {
  background: #f7faf7;
  border: 1.5px solid #bfd1c0;
  border-radius: 11px;
  color: var(--green-dark);
  cursor: pointer;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  height: 42px;
}

.basket-float-btn.primary {
  background: var(--green);
  border-color: var(--green-dark);
  color: #fff;
  font-weight: 700;
}

.basket-float-btn:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* These three are meant to hold their own established look on hover -
   unchanged, per the comment at the top of the sitewide button system -
   but the generic ghost-button hover fill (button:not(:disabled):hover)
   turns out to have just enough specificity (a plain type+2-pseudo-class
   selector) to beat their own plain-class rest styles anyway, so without
   this they were silently flashing solid green on hover: the tab-hit's
   invisible curved hit-area turning into a visible green rectangle (the
   "square" shape-shift), and both tray buttons losing their own
   background/gradient. button.<class>:hover matches that same
   specificity and wins on source order (this block comes later). */
button.basket-float-tab-hit:hover {
  background: transparent;
  border-color: transparent;
}

button.basket-float-btn:hover {
  background: #f7faf7;
  border-color: #bfd1c0;
  color: var(--green-dark);
}

button.basket-float-btn.primary:hover {
  background: var(--green-dark);
  border-color: var(--green-dark);
  color: #fff;
}

@media (max-width: 620px) {
  .basket-float-content {
    grid-template-columns: 1fr auto;
    padding: 0 12px;
  }

  .basket-float-action {
    display: none;
  }

  /* top comes from the base rule's calc(100% + 1px) either way - only
     the notch/hit width narrows on a small screen. */
  .basket-float-notch,
  .basket-float-tab-hit {
    width: 70px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .basket-float-shell,
  .basket-float-tray {
    transition: none;
  }
}


.visitor-intro p {
  margin: 8px 0 16px;
  max-width: 60ch;
}

.basket-overlay {
  display: none;
}

.bins-cart-sticky {
  position: sticky;
  top: 16px;
  background: rgba(255,255,255,0.9);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
  box-shadow: 0 10px 28px rgba(24, 79, 42, 0.08);
}

.bins-cart-header {
  align-items: center;
  background: transparent;
  border: 0;
  border-bottom: 2px solid rgba(47, 122, 68, 0.18);
  border-radius: 0;
  font-size: 18px;
  font-weight: 500;
  color: var(--green-dark);
  margin-bottom: 14px;
  padding-bottom: 12px;
  display: flex;
  justify-content: space-between;
  text-align: left;
  width: 100%;
}

.bins-cart-header > span {
  align-items: center;
  display: flex;
}

.basket-panel-close {
  /* The well is now always visible (persistent on desktop, inline as the
     third gallery card on mobile) rather than a dismissible drawer, so
     there's nothing left for a close button to do. */
  display: none;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--muted);
  font-size: 22px;
  height: 30px;
  line-height: 1;
  padding: 0;
  width: 30px;
}

.bins-cart-icon {
  height: 20px;
  margin-right: 7px;
  width: 20px;
}

.mobile-basket-count,
.mobile-basket-chevron {
  display: none;
}

.bins-cart-body {
  min-width: 0;
}

/* The cart list scrolls, but a visible scrollbar felt heavy in this small
   well - instead a soft "bloom" gradient fades in at whichever edge still
   has more items past it (like a horizon), and the native scrollbar is
   hidden while the element stays fully scrollable by wheel/touch/drag. */
.bins-cart-items-wrap {
  margin-bottom: 12px;
  position: relative;
}

/* Hidden everywhere except index-static-basket.html's docked position
   (see .recommendations-row #basket-dock-home below, which shows this
   and hides .bins-cart-items-wrap instead) - the itemized list is what
   every other context (index.html, edit-tab.html, and this page's own
   floating/expanded tab) shows. */
.bins-cart-summary-line {
  display: none;
  margin: 0 0 12px;
}

.bins-cart-items {
  max-height: 320px;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.bins-cart-items::-webkit-scrollbar {
  display: none;
}

.bins-cart-fade {
  height: 32px;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  right: 0;
  transition: opacity 180ms ease;
}

.bins-cart-fade-top {
  background: linear-gradient(to bottom, rgba(255,255,255,0.95), rgba(255,255,255,0));
  top: 0;
}

.bins-cart-fade-bottom {
  background: linear-gradient(to top, rgba(255,255,255,0.95), rgba(255,255,255,0));
  bottom: 0;
}

.bins-cart-items-wrap.has-more-above .bins-cart-fade-top,
.bins-cart-items-wrap.has-more-below .bins-cart-fade-bottom {
  opacity: 1;
}

.bins-cart-empty {
  margin: 0;
}

.bins-cart-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "name subtotal"
    "stepper remove";
  align-items: center;
  gap: 10px 12px;
  padding: 14px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 14.5px;
}

.bins-cart-item:last-child {
  border-bottom: none;
}

.bins-cart-item-name {
  grid-area: name;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-weight: 500;
}

.bins-cart-item-subtotal {
  grid-area: subtotal;
  font-weight: 600;
  color: var(--green-dark);
  font-variant-numeric: tabular-nums;
}

.bins-cart-item-remove {
  align-items: center;
  align-self: center;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: #8a2f2f;
  display: inline-flex;
  grid-area: remove;
  height: 32px;
  justify-content: center;
  padding: 0;
  width: 32px;
}

.bins-cart-item-remove svg {
  height: 18px;
  width: 18px;
}

.bins-cart-item-remove:hover {
  background: rgba(138, 47, 47, 0.1);
}

.bins-cart-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: 600;
  margin-bottom: 12px;
}

.bins-cart-total .amount {
  color: var(--green-dark);
  font-size: 18px;
}

.bins-cart-actions {
  display: flex;
  gap: 8px;
}

.bins-cart-actions #bins-submit {
  flex: 1;
}


.bin-card {
  background: rgba(255,255,255,0.88);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0;
  overflow: hidden;
  text-align: left;
  box-shadow: 0 8px 22px rgba(24, 79, 42, 0.07);
  transition: transform 160ms ease, box-shadow 160ms ease;
  display: flex;
  flex-direction: column;
}

.basket-item-stepper {
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  display: inline-grid;
  grid-area: stepper;
  grid-template-columns: 30px minmax(32px, auto) 30px;
  justify-self: start;
  overflow: hidden;
}

.basket-item-stepper button {
  background: #f4faf3;
  border: 0;
  border-radius: 0;
  color: var(--green-dark);
  height: 30px;
  padding: 0;
  width: 30px;
}

.basket-item-stepper span {
  color: var(--green-dark);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.bin-card:hover {
  box-shadow: 0 12px 28px rgba(24, 79, 42, 0.12);
  transform: translateY(-2px);
}

.bin-photo {
  height: 190px;
  background: linear-gradient(160deg, rgba(244,250,243,0.9), rgba(227,239,225,0.9));
  border-bottom: 1px solid var(--line);
  position: relative;
  overflow: hidden;
}

/* Stock set to exactly 0 on the catalog page (not deactivated, not
   removed - a bin's own preset amounts are a completely separate concept
   and are never touched by this) shows this diagonal band across the
   photo and disables adding it, without hiding the snack from the
   gallery entirely. */
.bin-card.sold-out .bin-photo {
  filter: grayscale(0.6);
  opacity: 0.75;
}

.sold-out-ribbon {
  background: rgba(138, 47, 47, 0.92);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  left: -34px;
  letter-spacing: 0.04em;
  padding: 4px 40px;
  position: absolute;
  text-align: center;
  text-transform: uppercase;
  top: 16px;
  transform: rotate(-38deg);
  z-index: 2;
}

/* absolute + inset so the photo always fills exactly this box regardless
   of its own aspect ratio — a grid/flex child with center alignment sizes
   itself to its own content instead of stretching, which let tall product
   photos blow past the fixed-height box and overlap the name/price below */
.bin-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
  padding: 6px;
  box-sizing: border-box;
  display: block;
}

.bin-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 46px;
  opacity: 0.5;
}

.bin-body {
  padding: 16px 17px 18px;
  display: flex;
  flex: 1;
  flex-direction: column;
}

/* two-line clamp keeps every card's name block the same height, so price
   and the +/- stepper start at the same row across a grid line regardless
   of whether a given snack's name is short ("Oreo") or long ("Butterkist
   Sandwich Cookies - Chocolate") */
.bin-card .bin-name {
  font-size: 17px;
  font-weight: 500;
  line-height: 1.28;
  color: var(--green-dark);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.56em;
}

.bin-card .bin-price {
  color: var(--green);
  font-size: 19px;
  font-weight: 600;
  margin: 7px 0 3px;
}

.bin-card .bin-meta {
  color: var(--muted);
  font-size: 12.5px;
  margin-bottom: 10px;
}

.bin-add-button {
  margin-top: auto;
  width: 100%;
}

/* Replaces the Add to basket button/stepper outright (not just a disabled
   version of it) for a sold-out or delisted (inactive) snack - the ribbon
   on the photo already carries the "why", this is just a quiet placeholder
   where the control used to be, not a control itself. */
.bin-unavailable-label {
  color: #8a2f2f;
  font-size: 13px;
  font-weight: 600;
  margin-top: auto;
  text-align: center;
  width: 100%;
}

.basket-notification {
  align-items: center;
  background: var(--green-dark);
  border: 0;
  border-radius: 50%;
  box-shadow: 0 8px 20px rgba(24, 79, 42, 0.24);
  color: #fff;
  display: none;
  height: 54px;
  justify-content: center;
  padding: 0;
  position: fixed;
  right: 18px;
  top: 18px;
  width: 54px;
  z-index: 42;
}

.basket-notification:active {
  box-shadow: 0 3px 8px rgba(24, 79, 42, 0.24);
  transform: translateY(2px);
}

.basket-notification svg {
  height: 26px;
  width: 26px;
}

.basket-notification span {
  align-items: center;
  background: #fff;
  border: 2px solid var(--green-dark);
  border-radius: 999px;
  color: var(--green-dark);
  display: flex;
  font-size: 11px;
  font-weight: 600;
  height: 21px;
  justify-content: center;
  min-width: 21px;
  padding: 0 4px;
  position: absolute;
  right: -4px;
  top: -4px;
}

.bins-cart-sticky {
  outline: 3px solid rgba(47, 122, 68, 0);
  outline-offset: 2px;
  transition: outline-color 200ms ease, box-shadow 200ms ease;
}

/* Stays lit continuously across repeated additions (JS only adds/removes
   this class, never toggling it off and back on) so it never restarts
   mid-glow and blinks - it just holds a bright border until the JS timer
   clears it 2s after the most recent addition. */
.bins-cart-sticky.basket-attention {
  outline-color: rgba(47, 122, 68, 0.9);
  box-shadow: 0 10px 28px rgba(24, 79, 42, 0.08), 0 0 0 4px rgba(47, 122, 68, 0.22);
}

/* one encapsulated control: [ − | qty | + ] */
.stepper {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: 999px;
  overflow: hidden;
  background: #fff;
  width: 100%;
}

.stepper button {
  width: 42px;
  height: 38px;
  padding: 0;
  border: none;
  border-radius: 0;
  font-size: 18px;
  line-height: 1;
  background: linear-gradient(180deg, #ffffff, #eff7ee);
  color: var(--green-dark);
}

.stepper button:first-child {
  border-right: 1px solid var(--line);
}

.stepper button:last-child {
  border-left: 1px solid var(--line);
}

.stepper .qty {
  min-width: 46px;
  display: grid;
  place-items: center;
  font-size: 16px;
  font-weight: 600;
  color: var(--green-dark);
  font-variant-numeric: tabular-nums;
  flex: 1;
}

/* Tapping a product photo opens its preview. Quantities are adjusted only in
   the basket so the gallery remains clean and product-led. */
.bin-photo {
  cursor: pointer;
}

/* The photo IS the modal - it fills the whole card edge-to-edge, zoomed in
   via object-fit: cover (rather than the gallery card's "contain", which
   just shows the source photo's own baked-in whitespace at a smaller size).
   Name/price/stepper/Done float as a translucent overlay bar at the photo's
   bottom instead of living in their own section underneath. */
.snack-modal {
  padding: 0;
  overflow: hidden;
  width: min(420px, 100%);
}

/* index.html and index-static-basket.html: the add-to-basket (snack
   quantity) modal opens anchored to the bottom-right corner instead of
   centered - #snack-modal-backdrop specifically, not the shared
   .modal-backdrop class, so confirm-remove/transaction-review/etc. keep
   their normal centered behavior.
   padding-right overrides just that one side of the shared 20px padding -
   #snack-modal-backdrop is itself position:fixed/inset:0 (real viewport
   edges, not some ancestor's box), so this places the modal a true 3px
   from the actual right edge of the screen at any width. */
body:has(#snack-shop) #snack-modal-backdrop,
body:has(#snack-shop-static) #snack-modal-backdrop {
  place-items: end;
  padding-right: 3px;
}

.snack-modal-photo {
  position: relative;
  height: 68vh;
  max-height: 520px;
  min-height: min(380px, calc(100vh - 40px));
  background: linear-gradient(160deg, rgba(244,250,243,0.9), rgba(227,239,225,0.9));
  overflow: hidden;
}

.snack-modal-photo img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.22);
  transform-origin: center;
  display: block;
}

.snack-modal-photo .bin-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 120px;
}

.snack-modal .modal-close {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

.snack-modal-overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 22px 20px 20px;
  background: linear-gradient(to top, rgba(9, 28, 15, 0.88) 0%, rgba(9, 28, 15, 0.6) 50%, rgba(9, 28, 15, 0) 100%);
  color: #fff;
  text-align: center;
}

.snack-modal-overlay h2 {
  margin: 0 0 2px;
  color: #fff;
}

.snack-modal-overlay p {
  margin: 0 0 14px;
  color: rgba(255, 255, 255, 0.85);
  font-size: 13.5px;
}

.snack-modal-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}

.snack-modal-add {
  min-width: 180px;
}

.snack-modal-stepper {
  background: rgba(255, 255, 255, 0.95);
}

.snack-modal-stepper button {
  width: 48px;
  height: 46px;
  font-size: 21px;
}

.snack-modal-stepper .qty {
  min-width: 52px;
  font-size: 18px;
}

@media (max-width: 360px) {
  .snack-modal-overlay {
    padding-inline: 12px;
  }

  .snack-modal-controls {
    flex-direction: column;
    gap: 8px;
  }

  .snack-modal-controls > .primary {
    width: 100%;
  }
}

@media (max-height: 520px) {
  .modal-backdrop {
    padding: 12px;
  }

  .snack-modal-photo {
    height: calc(100vh - 24px);
    min-height: 0;
  }

  .snack-modal-overlay {
    padding-top: 38px;
    padding-bottom: 12px;
  }

  .snack-modal-overlay p {
    margin-bottom: 8px;
  }
}

.bin-total {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding: 14px;
  background: rgba(234, 245, 232, 0.94);
  border: 1px solid rgba(47, 122, 68, 0.18);
  border-radius: var(--radius-sm);
}

.bin-total .amount {
  font-size: 24px;
  font-weight: 600;
  color: var(--green-dark);
}

.hidden { display: none !important; }

/* ---- Hamburger + left drawer ---- */

.header-lead {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.hamburger {
  font-size: 18px;
  line-height: 1;
  padding: 9px 12px;
}

.profile-btn {
  padding: 5px;
}

.profile-btn img {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: block;
  border: 1px solid rgba(47, 122, 68, 0.25);
}

.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(18, 36, 24, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
  z-index: 55;
}

.drawer-backdrop.show {
  opacity: 1;
  pointer-events: auto;
}

.drawer {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 258px;
  background: linear-gradient(160deg, rgba(255,255,255,0.98), rgba(244,250,243,0.98));
  border-right: 1px solid var(--line);
  box-shadow: 14px 0 34px rgba(24, 79, 42, 0.14);
  padding: 18px 14px;
  transform: translateX(-102%);
  transition: transform 0.3s ease;
  z-index: 60;
}

.drawer.open {
  transform: none;
}

.drawer-brand {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--green-dark);
  text-transform: uppercase;
  padding: 6px 14px 14px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
}

.drawer-section-label {
  padding: 14px 14px 4px;
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.drawer-section-label:first-of-type {
  padding-top: 2px;
}

.drawer-link {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 12px 14px;
  margin: 2px 0;
  border-radius: var(--radius-sm);
  color: var(--green-dark);
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
}

.drawer-link:hover {
  background: var(--green-light);
}

.drawer-link.active {
  background: var(--green-light);
}

/* right-side well (user settings) */
.drawer.right {
  left: auto;
  right: 0;
  width: 300px;
  border-right: none;
  border-left: 1px solid var(--line);
  box-shadow: -14px 0 34px rgba(24, 79, 42, 0.14);
  transform: translateX(102%);
  overflow-y: auto;
}

.drawer.right.open {
  transform: none;
}

.drawer .field {
  margin: 10px 2px;
}

.drawer .drawer-note {
  color: var(--muted);
  font-size: 13px;
  margin: 10px 2px;
}

.link-status {
  background: rgba(234, 245, 232, 0.6);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  margin: 8px 2px 14px;
}

.link-status .drawer-note {
  margin: 0 0 8px;
}

.link-invite-card .row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.drawer button.primary {
  width: 100%;
  margin-top: 6px;
}

/* ---- Nutrition facts slide door (over the favorite snack photo) ---- */

.snack-image {
  position: relative;
  overflow: hidden;
}

.facts-door {
  position: absolute;
  inset: 0;
  background: #fff;
  display: grid;
  place-items: center;
  padding: 10px;
  transform: translateX(-102%);
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  box-shadow: 10px 0 22px rgba(0, 0, 0, 0.14);
  cursor: pointer;
}

.facts-door img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.facts-door.open {
  transform: none;
}

/* ---- listing verdict (thin check / cross) ---- */

.verdict-cell {
  text-align: center;
}

/* Anchors .entry-remove-btn to this cell's own top-right corner rather than
   the row/card as a whole - simpler and just as visually "corner-of-the-
   entry" without fighting table layout (a <tr> is a bad position:relative
   anchor). Also the mobile card layout's own actions cell, so this alone
   covers both. */
.tracker-actions {
  position: relative;
}

/* Small, borderless, red "x" that floats in the corner instead of sitting
   inline beside Mark as Paid like a second equal-weight button - it reads
   as a dismiss/flag affordance, not a competing action. */
.entry-remove-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 14px;
  height: 14px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: #a34c45;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
}

.entry-remove-btn:hover {
  color: #8a2f2f;
}

.entry-date-control {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  border-bottom: 1px dashed var(--green);
  cursor: pointer;
  white-space: nowrap;
}

.entry-date-control::after {
  content: "\270E";
  color: var(--green);
  font-size: 11px;
}

.entry-date-control:hover {
  border-bottom-color: var(--green-dark);
}

.entry-date-control:hover::after {
  color: var(--green-dark);
}

.entry-date-control input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}

.row-verdict {
  display: inline-flex;
  gap: 6px;
  vertical-align: middle;
}

.verdict-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  font-size: 13px;
  font-weight: 300;
  line-height: 1;
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.9);
  color: var(--muted);
  cursor: pointer;
}

.verdict-btn.agree.on {
  border-color: var(--green);
  background: var(--green-light);
  color: var(--green-dark);
}

.verdict-btn.dispute.on {
  border-color: rgba(138, 47, 47, 0.5);
  background: #faf0ef;
  color: #8a2f2f;
}

tr.disputed td {
  background: rgba(250, 240, 239, 0.9);
}

.verdict-badge {
  display: inline-block;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 600;
}

.verdict-badge.agreed {
  background: var(--green-light);
  color: var(--green-dark);
}

.verdict-badge.disputed {
  background: #faf0ef;
  color: #8a2f2f;
}

.verdict-badge.needs-review {
  background: #fdf3e2;
  color: #8a6412;
}

.verdict-badge.activity {
  background: #eaf1fb;
  color: #2c5a8a;
}


.notifications-modal {
  width: min(560px, 100%);
}

#admin-notifications-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-color: var(--green) transparent;
  scrollbar-width: thin;
}

#admin-notifications-list::-webkit-scrollbar {
  width: 8px;
}

#admin-notifications-list::-webkit-scrollbar-track {
  background: transparent;
}

#admin-notifications-list::-webkit-scrollbar-thumb {
  background: var(--green);
  border-radius: 999px;
}

#admin-notifications-list::-webkit-scrollbar-thumb:hover {
  background: var(--green-dark);
}

.notif-item {
  align-items: center;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: 0 1px 2px rgba(24, 79, 42, 0.04);
  cursor: pointer;
  display: flex;
  gap: 14px;
  padding: 14px 16px;
  text-align: left;
}

.notif-item:hover {
  background: var(--green-soft);
}

.notif-item.disputed {
  background: rgba(250, 240, 239, 0.9);
}

.notif-item-photo {
  align-items: center;
  background: var(--green-light);
  border-radius: 14px;
  color: var(--green-dark);
  display: flex;
  flex: none;
  height: 52px;
  justify-content: center;
  overflow: hidden;
  position: relative;
  width: 52px;
}

.notif-item-photo svg {
  height: 26px;
  width: 26px;
}

.notif-item-photo.tone-blue {
  background: #eaf1fb;
  color: #2c5a8a;
}

.notif-item-photo.tone-amber {
  background: #fdf3e2;
  color: #8a6412;
}

.notif-item-photo.tone-red {
  background: #faf0ef;
  color: #8a2f2f;
}

.notif-item-photo img {
  background: #fff;
  height: 100%;
  object-fit: contain;
  position: absolute;
  inset: 0;
  width: 100%;
}

.notif-item-body {
  flex: 1;
  min-width: 0;
}

.notif-item-title {
  font-size: 16px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notif-item-subtitle {
  margin-top: 2px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.35;
}

.notif-item-meta {
  align-items: flex-end;
  display: flex;
  flex-direction: column;
  flex: none;
  gap: 6px;
}

.notif-item-status {
  margin-top: 6px;
}

.notif-item-actions {
  display: flex;
  gap: 6px;
  margin-top: 6px;
}

/* Inside the meta row (transaction cards put the Confirm/Approve button
   there, beside the date) the actions block is a plain flex sibling -
   no top nudge. */
.notif-item-meta .notif-item-actions {
  margin-top: 0;
}

.notif-item-actions button {
  font-size: 12.5px;
  padding: 4px 9px;
}

.notif-dismiss {
  align-items: center;
  background: transparent;
  border: 0;
  border-radius: 999px;
  box-shadow: none;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  flex: none;
  font-size: 20px;
  height: 26px;
  justify-content: center;
  line-height: 1;
  margin-left: 4px;
  padding: 0;
  width: 26px;
}

.notif-dismiss:hover,
.notif-dismiss:focus-visible {
  background: rgba(0,0,0,0.08);
  color: var(--text);
}

/* Narrow screens: the desktop row layout (photo | body | meta column | x)
   falls apart - .notif-item-meta is flex:none, so a long badge like
   "Customer reported payment" refuses to shrink and crushes the body to
   a sliver: names truncate after a few letters, one-line summaries wrap
   into four, and body action buttons (wider than the crushed body)
   overflow straight under the badge. Regroup into a grid: photo + text +
   dismiss on top, and the meta (date + badge) as its own full-width row
   underneath where nothing competes with it. Titles also wrap instead of
   ellipsizing - there's no column to protect anymore. */
@media (max-width: 620px) {
  .notif-item {
    align-items: start;
    display: grid;
    gap: 8px 10px;
    grid-template-areas:
      "photo body dismiss"
      "meta meta meta";
    grid-template-columns: 44px minmax(0, 1fr) 26px;
    padding: 12px 14px;
  }

  .notif-item-photo {
    grid-area: photo;
    height: 44px;
    width: 44px;
  }

  .notif-item-body {
    grid-area: body;
  }

  .notif-dismiss {
    grid-area: dismiss;
    margin-left: 0;
  }

  .notif-item-meta {
    align-items: center;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 6px 10px;
    grid-area: meta;
    justify-content: flex-start;
  }

  .notif-item-title {
    white-space: normal;
  }
}

/* ---- customer-facing cash back notifications bell ---- */
/* Same bell/panel design as .admin-notifications-bell/.notif-item above,
   reused for a customer-facing purpose - see renderCashbackNotifications()
   in index.html. .basket-notification is display:none at its base rule
   with nothing that ever un-hides it (effectively dead markup), so there's
   no real risk of the two fixed badges overlapping in practice.

   Pinned to the true top-right corner of the browser window itself
   (position:fixed, not .page-wide/.banner-inner-relative like before) and
   past the profile chip rather than beside it, at every width - so it
   stays put through a scroll and never drifts back toward the centered
   page content on a wide screen the way an absolutely-positioned version
   tied to .page-wide's (capped, centered) box would. */
.cashback-notifications-bell {
  position: fixed;
  right: 10px;
  top: 10px;
  transition: opacity 240ms ease;
}

/* While the floating basket tab is on screen it owns the top edge -
   especially on a phone, where the tab spans nearly the full width and
   the bell would sit right on top of it. The bell fades out of the way
   for as long as the tab is visible and returns once the user scrolls
   back up (.visible is managed by updateBasketFloatState; :has keys off
   it directly, so no extra JS is involved). pointer-events restores the
   taps that would otherwise land on an invisible button. */
body:has(.basket-float-shell.visible) .cashback-notifications-bell {
  opacity: 0;
  pointer-events: none;
}

#cashback-notifications-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 4px;
  scrollbar-color: var(--green) transparent;
  scrollbar-width: thin;
}

#cashback-notifications-list::-webkit-scrollbar { width: 8px; }
#cashback-notifications-list::-webkit-scrollbar-track { background: transparent; }
#cashback-notifications-list::-webkit-scrollbar-thumb { background: var(--green); border-radius: 999px; }
#cashback-notifications-list::-webkit-scrollbar-thumb:hover { background: var(--green-dark); }

.notifications-empty {
  color: var(--muted);
  font-size: 14px;
  margin: 14px 0 0;
}

.cashback-instance-stages {
  align-items: center;
  display: flex;
  gap: 4px;
  margin-top: 6px;
}

.cashback-instance-stage {
  background: #eef1ee;
  border-radius: 999px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  padding: 3px 8px;
}

.cashback-instance-stage.is-past { background: transparent; color: var(--green-dark); opacity: 0.6; }
.cashback-instance-stage.is-current { background: var(--green-light); color: var(--green-dark); box-shadow: inset 0 0 0 1px rgba(24, 79, 42, 0.15); }
.cashback-instance-stage.is-current.is-expired-stage { background: #faf0ef; color: #8a2f2f; box-shadow: inset 0 0 0 1px rgba(138, 47, 47, 0.2); }

.cashback-instance-arrow { color: var(--muted); font-size: 12px; opacity: 0.6; }

/* ---- snacks-taken macro chips ---- */

.macro-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.macro-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  background: var(--green-light);
  color: var(--green-dark);
  border: 1px solid rgba(47, 122, 68, 0.16);
  border-radius: 999px;
  padding: 7px 13px;
  font-size: 14px;
}

.macro-chip .macro-meta {
  color: var(--green);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* ---- QR join card ---- */

.qr-wrap {
  display: flex;
  gap: 20px;
  align-items: center;
  flex-wrap: wrap;
}

.qr-wrap #qr svg {
  display: block;
  width: 150px;
  height: 150px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px;
}

.qr-wrap > div:last-child {
  flex: 1;
  min-width: 220px;
}

/* ---- Nutrition facts ---- */

.has-facts {
  cursor: pointer;
  border-bottom: 1px dashed rgba(24, 79, 42, 0.45);
}

.bin-facts {
  margin-bottom: 12px;
  min-height: 18px;
}

.bin-facts a,
#fav-facts a {
  color: var(--blue-dark);
  font-size: 13px;
}

.bin-facts a {
  text-decoration: none;
}

.facts-overlay {
  position: fixed;
  inset: 0;
  background: rgba(18, 36, 24, 0.62);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 50;
  cursor: zoom-out;
}

.facts-overlay figure {
  margin: 0;
  text-align: center;
}

.facts-overlay img {
  max-width: min(720px, 100%);
  max-height: 84vh;
  background: #fff;
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}

.facts-overlay figcaption {
  color: rgba(255, 255, 255, 0.92);
  font-size: 14px;
  margin-top: 10px;
}

@media (max-width: 900px) {
  .activity-summary .summary-grid {
    grid-template-columns: 1fr;
  }

  .recommendations-heading {
    align-items: start;
    display: block;
  }

  .recommendations-heading p {
    margin-top: 7px;
    text-align: left;
  }

  .recommendations-grid {
    grid-template-columns: 1fr;
  }

  /* Below this width the gallery + persistent well no longer fit side by
     side. Rather than hiding the well behind a floating toggle button (or
     burying it a couple of cards down where you'd have to scroll back up
     to see it react), it's locked sticky above the gallery grid - always
     in view while adding snacks, without needing to reveal or scroll to
     it. .bins-layout switches to a simple column flex stack (order:-1 on
     the well moves it before the gallery without touching markup order,
     which stays gallery-then-well so the desktop grid columns don't need
     to change); the gallery keeps its own 2-column grid inside
     .bins-gallery-col exactly as before. Sticky here has real room to
     work because .bins-layout, in normal flow, is as tall as well+gallery
     combined - the well itself doesn't need to be artificially stretched
     the way it does in the desktop 2-column grid case above. */
  .bins-layout {
    display: flex;
    flex-direction: column;
    gap: 14px;
  }

  .bins-gallery-col {
    order: 1;
  }

  .bin-grid {
    display: grid;
    gap: 8px;
    grid-template-columns: repeat(2, 1fr);
  }

  .basket-notification,
  .basket-overlay {
    display: none;
  }

  .bins-cart-col {
    opacity: 1;
    order: -1;
    pointer-events: auto;
    position: sticky;
    top: 8px;
    transform: none;
    width: auto;
    z-index: 30;
  }

  /* The sticky rule above predates the recommendations-row basket design
     and is unscoped because edit-tab.html still depends on it. On
     index.html and the static comparison page the basket lives in
     .recommendations-row and should stay plainly static on mobile too -
     the floating pull tab handles "keep the basket reachable" there. */
  body:has(#snack-shop) .bins-cart-col,
  body:has(#snack-shop-static) .bins-cart-col {
    position: static;
  }

  .favorite-card {
    grid-template-columns: 1fr;
  }

  .favorite-card .snack-image {
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }

  .brand-grid,
  .main-grid,
  .brand-stats,
  .summary-grid,
  .mini-grid {
    grid-template-columns: 1fr;
  }

  .bins-cart-sticky {
    position: static;
    transform: none;
    background: rgba(255, 255, 255, 0.97);
    border-radius: 13px;
    overflow: hidden;
    padding: 0;
  }

  .bins-cart-header {
    border-bottom: 0;
    cursor: pointer;
    margin: 0;
    min-height: 54px;
    padding: 12px 15px;
  }

  .mobile-basket-count {
    align-items: center;
    background: var(--green-dark);
    border-radius: 999px;
    color: #fff;
    display: inline-flex;
    font-size: 12px;
    font-weight: 600;
    height: 24px;
    justify-content: center;
    margin-left: 9px;
    min-width: 24px;
    padding: 0 7px;
  }

  .mobile-basket-chevron {
    display: block;
    height: 22px;
    transition: transform 180ms ease;
    width: 22px;
  }

  .bins-cart-body {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    padding: 0 15px;
    pointer-events: none;
    transition: max-height 260ms ease, opacity 180ms ease, padding 260ms ease;
  }

  .bins-cart-col.is-open .bins-cart-header {
    border-bottom: 1px solid var(--line);
  }

  .bins-cart-col.is-open .mobile-basket-chevron {
    transform: rotate(180deg);
  }

  .bins-cart-col.is-open .bins-cart-body {
    max-height: min(68vh, 560px);
    opacity: 1;
    overflow: visible;
    padding: 14px 15px 16px;
    pointer-events: auto;
  }

  .bins-cart-items {
    max-height: min(34vh, 260px);
  }

  .bin-photo {
    height: 104px;
  }

  .bin-body {
    padding: 9px 10px 12px;
  }

  .bin-card .bin-name {
    font-size: 13.5px;
  }

  .bin-card .bin-price {
    font-size: 12px;
    margin: 2px 0 8px;
  }

  /* header stays one row: menu + logo + title left, profile button right */
  .header-row {
    align-items: center;
    gap: 10px;
  }

  header {
    padding: 14px 16px;
  }

  h1 {
    font-size: 22px;
    letter-spacing: -0.02em;
  }

  header p {
    font-size: 12.5px;
  }

  .header-logo {
    width: 30px;
    height: 30px;
  }

  .banner-inner {
    padding: 14px;
    gap: 10px;
    align-items: flex-start;
  }

  .banner-badge {
    width: 54px;
    margin-top: 2px;
  }

  .banner-track .ico {
    display: none;
  }

  .banner-tagline {
    font-size: 15px;
    margin-bottom: 8px;
  }

  .banner-track {
    font-size: 12.5px;
  }

  .banner-pill {
    font-size: 12.5px;
    padding: 6px 12px;
  }

  .hamburger {
    padding: 8px 10px;
  }

  .brand-card {
    padding: 14px;
  }

  .brand-title {
    font-size: 23px;
  }

  .brand-subtitle {
    font-size: 13px;
  }

  .avatar {
    width: 76px;
    height: 76px;
    border-width: 3px;
  }

  .brand-grid {
    grid-template-columns: auto 1fr;
    gap: 12px;
  }

  .brand-stats {
    grid-column: 1 / -1;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }

  .stat-value {
    font-size: 19px;
  }

  .balance-stat--hero {
    padding: 14px 16px;
  }

  .balance-stat--hero .stat-value {
    font-size: 21px;
  }

  .stat-box-icon {
    height: 38px;
    width: 38px;
  }

  .stat-box-icon img {
    height: 18px;
    width: 18px;
  }

  .page {
    width: calc(100% - 20px);
  }

  .snack-image img {
    height: 215px;
  }

  summary {
    grid-template-columns: 24px minmax(0, 1fr) 96px;
    column-gap: 12px;
    padding: 14px 10px;
  }

  .log-head {
    grid-template-columns: 24px minmax(0, 1fr) 96px;
    column-gap: 12px;
    padding-right: 11px;
  }

  /* small screens keep only the Remaining column */
  .sum-tab,
  .sum-paid,
  .head-tab,
  .head-paid {
    display: none;
  }

  .tracker-table {
    font-size: 13px;
    padding: 0 6px 8px;
  }

  .tracker-table th {
    font-size: 12px;
    padding: 8px 6px 4px;
  }

  .tracker-table td {
    padding: 9px 6px;
  }

  /* phones: keep a slim Remaining lane for the verdict buttons */
  .col-day { width: 13%; }
  .col-snack { width: 25%; }
  .col-count { width: 7%; }
  .col-value { width: 20%; }
  .col-payment { width: 21%; }
  .col-fill { width: 14%; }

  .verdict-btn {
    width: 20px;
    height: 20px;
    font-size: 11px;
  }

  .row-verdict {
    gap: 4px;
  }

  .nv {
    width: auto;
    white-space: normal;
  }

  .bs-label { width: 40%; }
  .bs-a { width: 20%; }
  .bs-b { width: 20%; }
  .bs-c { width: 20%; }

  .bottom-summary {
    font-size: 13px;
  }

  .bottom-summary td {
    padding: 9px 5px;
  }
}

.customer-final-status {
  display: inline-flex;
  min-height: 24px;
  align-items: center;
  padding: 3px 8px;
  border: 1px solid #ccd7ce;
  border-radius: 999px;
  background: #f2f5f2;
  color: #536157;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .02em;
  text-transform: uppercase;
}

.customer-final-status.confirmed {
  border-color: #b8d1bf;
  background: #edf6ef;
  color: #28613a;
}

.mark-paid-btn {
  display: inline-flex;
  min-height: 24px;
  align-items: center;
  padding: 3px 10px;
  border: 1px solid var(--green);
  border-radius: 999px;
  background: var(--green-light);
  color: var(--green-dark);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

.mark-paid-btn:hover {
  background: var(--green);
  color: #fff;
}

.mark-paid-btn:disabled {
  opacity: .55;
  cursor: not-allowed;
}

.entry-date-final {
  color: var(--text);
  cursor: default;
}

@media (max-width: 640px) {
  #snack-log .section-head {
    align-items: center;
    flex-wrap: nowrap;
  }

  #snack-log .section-head h2 {
    font-size: 22px;
    white-space: nowrap;
  }

  #snack-log .log-controls {
    gap: 6px;
    margin-left: auto;
  }

  #snack-log .log-controls button {
    font-size: 12px;
    min-height: 36px;
    padding: 7px 9px;
  }

  .tracker-table {
    display: block;
    padding: 4px 0 10px;
  }

  .tracker-table colgroup,
  .tracker-table thead {
    display: none;
  }

  .tracker-table tbody {
    display: grid;
    gap: 10px;
  }

  .tracker-table tr.tracker-entry {
    background: #fff;
    border: 1px solid #dbe4dd;
    border-radius: 12px;
    box-shadow: 0 4px 12px rgba(24, 79, 42, 0.045);
    display: grid;
    gap: 0;
    grid-template-areas:
      "snack value"
      "date qty"
      "payment actions";
    grid-template-columns: minmax(0, 1fr) auto;
    overflow: hidden;
    padding: 14px;
  }

  .tracker-table tr.tracker-entry td {
    background: transparent;
    border: 0;
    border-radius: 0;
    min-width: 0;
    padding: 0;
    text-align: left;
    white-space: normal;
  }

  .tracker-table tr.tracker-entry td::before {
    color: #7a857d;
    content: attr(data-label);
    display: block;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.065em;
    line-height: 1.2;
    margin-bottom: 4px;
    text-transform: uppercase;
  }

  .tracker-table .tracker-snack {
    grid-area: snack;
    padding-right: 12px !important;
  }

  .tracker-table .tracker-value {
    grid-area: value;
    text-align: right !important;
  }

  .tracker-table .tracker-date,
  .tracker-table .tracker-qty,
  .tracker-table .tracker-payment,
  .tracker-table .tracker-actions {
    border-top: 1px solid #edf1ee !important;
    margin-top: 12px;
    padding-top: 11px !important;
  }

  .tracker-table .tracker-date { grid-area: date; }
  .tracker-table .tracker-qty {
    grid-area: qty;
    min-width: 68px;
    text-align: right !important;
  }
  .tracker-table .tracker-payment { grid-area: payment; }
  .tracker-table .tracker-actions {
    align-items: flex-end;
    display: flex;
    flex-direction: column;
    grid-area: actions;
    min-width: 104px;
  }

  .tracker-table .tracker-actions::before {
    text-align: right;
  }

  .tracker-table .nv,
  .tracker-table .mv {
    display: block;
    max-width: 100%;
    width: auto;
  }

  .tracker-table .snack-pill {
    background: transparent;
    border-radius: 0;
    display: block;
    font-size: 15px;
    line-height: 1.3;
    overflow-wrap: normal;
    padding: 0;
    word-break: normal;
  }

  .tracker-table .entry-date-control {
    font-size: 13px;
  }

  .tracker-table .verdict-btn {
    height: 32px;
    width: 32px;
  }

  .tracker-table tr.month-line {
    background: #f3f8f3;
    border-radius: 10px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    margin-top: 2px;
    overflow: hidden;
  }

  .tracker-table tr.month-line td {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 11px 10px;
  }

  .tracker-table tr.month-line td:last-child {
    display: none;
  }

  .tracker-payment-entry .tracker-qty,
  .tracker-payment-entry .tracker-value,
  .tracker-payment-entry .tracker-actions {
    display: none !important;
  }

  .tracker-table tr.tracker-payment-entry {
    grid-template-areas:
      "snack payment"
      "date date";
  }

  .tracker-payment-entry .tracker-payment {
    border-top: 0 !important;
    margin-top: 0;
    padding-top: 0 !important;
    text-align: right !important;
  }
}

/* Admin sitemap and explicit inventory QR destinations */
.sitemap-tree { overflow: hidden; border: 1px solid var(--line); border-radius: 14px; background: #fff; }
.sitemap-group { padding: 12px 18px; color: var(--green-dark); background: var(--green-light); font-size: .82rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; }
.sitemap-row { display: grid; grid-template-columns: 20px minmax(0, 1fr) 42px; align-items: center; min-height: 58px; padding: 8px 14px; border-top: 1px solid var(--line); }
.sitemap-group + .sitemap-row,
.sitemap-floor:first-child > .sitemap-row:first-child { border-top: 0; }
.sitemap-row.level-1 { padding-left: 40px; }
.sitemap-row.level-2 { padding-left: 66px; }
.sitemap-branch { width: 12px; height: 18px; border-left: 1px solid #a7b9ad; border-bottom: 1px solid #a7b9ad; }
.sitemap-label { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.sitemap-label a { color: var(--ink); font-weight: 500; text-decoration: none; }
.sitemap-label a:hover { color: var(--green); text-decoration: underline; }
.sitemap-label span { color: var(--muted); font-size: .78rem; }
.sitemap-qr { justify-self: end; }
/* Promo rows carry a delete button alongside the QR button - one extra
   fixed-width grid column, same 42px as the QR slot. */
.sitemap-row.promo-row { grid-template-columns: 20px minmax(0, 1fr) 42px 42px; }
.sitemap-floor + .sitemap-floor { border-top: 8px solid #f3f7f4; }
.qr-target-highlight { outline: 3px solid rgba(0, 135, 81, .3); outline-offset: 4px; animation: qr-target-pulse 1.5s ease-out 2; }
@keyframes qr-target-pulse { 0%, 100% { outline-color: rgba(0, 135, 81, .22); } 50% { outline-color: rgba(0, 135, 81, .7); } }
@media (max-width: 640px) {
  .sitemap-row { padding-left: 10px; }
  .sitemap-row.level-1 { padding-left: 24px; }
  .sitemap-row.level-2 { padding-left: 38px; }
}

@media print {
  body { background: white; }
  body::before,
  body::after { display: none; }

  .page {
    width: 100%;
    margin: 0;
  }

  button, .btn,
  .drawer,
  .drawer-backdrop {
    display: none;
  }

  header,
  .brand-card,
  .card,
  .snack-card,
  details {
    box-shadow: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}

/* stats.html - deliberately plain, hand-drawn (no chart library): big
   number cards, a 14-day stacked bar chart, and a static reference grid of
   documented Firestore limits. */
.stats-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
  margin-top: 14px;
}

.stats-card {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.stats-card-label {
  color: var(--muted);
  font-size: 12px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.stats-card-value {
  color: #0b633c;
  font-size: 28px;
  font-weight: 800;
}

/* Fixed-width columns instead of flex:1 - fourteen stretching columns
   spread the chart across the entire card on a wide screen (huge dead
   gaps between skinny bars) and crush unusably on a phone. Columns hold
   a steady 44px each (~700px total), the chart itself caps at 720px, and
   anything narrower scrolls horizontally instead of squeezing. */
.stats-chart {
  align-items: flex-end;
  display: flex;
  gap: 6px;
  height: 178px;
  margin-top: 16px;
  max-width: 720px;
  overflow-x: auto;
  padding: 0 4px 6px;
  scrollbar-color: var(--green) transparent;
  scrollbar-width: thin;
}

.stats-bar-group {
  align-items: center;
  display: flex;
  flex: 0 0 44px;
  flex-direction: column;
  gap: 6px;
  height: 100%;
  justify-content: flex-end;
}

.stats-bar {
  background: rgba(24, 79, 42, 0.06);
  border-radius: 4px 4px 0 0;
  height: 130px;
  max-width: 26px;
  position: relative;
  width: 100%;
}

.stats-bar-segment {
  border-radius: 4px 4px 0 0;
  left: 0;
  position: absolute;
  right: 0;
}

.stats-bar-reads { background: #0b633c; }
.stats-bar-writes { background: #8a6d1a; }

.stats-bar-label {
  color: var(--muted);
  font-size: 10.5px;
  white-space: nowrap;
}

.stats-limits-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin-top: 14px;
}

.stats-limit-card {
  background: rgba(244, 250, 243, 0.8);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px;
  text-align: center;
}

.stats-limit-value {
  color: #0b633c;
  font-size: 18px;
  font-weight: 700;
}

.stats-limit-label {
  color: var(--muted);
  font-size: 11.5px;
  line-height: 1.3;
}

/* Daily sales report (daily-sales.html) - same hand-drawn, no-library
   approach as stats.html's chart, reusing .stats-cards/.stats-card for the
   KPI row. One filter row (.range-picker) scopes every card below it. */
.range-picker {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.range-btn.is-active {
  background: var(--green);
  border-color: var(--green);
  color: #fff;
}

.sales-chart-wrap {
  position: relative;
}

/* A real line/area chart, not a strip of fixed-width bars - always fills
   the card's width (no horizontal scrolling, no range where bars get
   unusably thin or need scrolling to reach), so every day in the range is
   simultaneously visible at a consistent scale. */
.sales-chart-svg {
  display: block;
  height: 280px;
  margin-top: 12px;
  width: 100%;
}

.sales-chart-gridline { stroke: var(--line); stroke-width: 1; }
.sales-chart-ylabel { fill: var(--muted); font-size: 11px; }

.sales-chart-area { fill: rgba(47, 122, 68, 0.14); stroke: none; }
.sales-chart-line {
  fill: none;
  stroke: var(--green);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.5;
}

/* One light tick per day (even where there's no room for a text label) so
   the eye can still count the full range at a glance - this is the direct
   fix for a chart that used to visually read as missing most of its days
   once a range's labels got sparse. */
.sales-chart-tick { stroke: var(--line); stroke-width: 1; }
.sales-chart-xlabel { fill: var(--muted); font-size: 11px; }

/* A dot on every day, including J$0 ones sitting right on the baseline -
   deliberately visible rather than an invisible zero-height bar, which is
   what used to make zero-revenue days look like gaps in the chart. */
.sales-chart-point {
  fill: #fff;
  stroke: var(--green);
  stroke-width: 2;
  transition: r 120ms ease, fill 120ms ease, stroke 120ms ease;
}
.sales-chart-point.is-zero { fill: var(--line); stroke: var(--muted); }
.sales-chart-point.is-active { fill: var(--green-dark); r: 5.5; stroke: var(--green-dark); }

.sales-chart-hit { cursor: pointer; fill: transparent; outline: none; }

/* Shared hover/focus tooltip for the chart's bars - one floating card,
   repositioned per bar rather than a native title attribute, so it can
   show more than one line and respond to keyboard focus too. */
.sales-tooltip {
  background: var(--green-dark);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  color: #fff;
  display: none;
  flex-direction: column;
  gap: 2px;
  padding: 8px 10px;
  pointer-events: none;
  position: fixed;
  width: max-content;
  max-width: 220px;
  z-index: 60;
}

.sales-tooltip.show {
  display: flex;
}

.sales-tooltip-date {
  color: rgba(255, 255, 255, 0.75);
  font-size: 11px;
}

.sales-tooltip-value {
  font-size: 16px;
  font-weight: 800;
}

.sales-tooltip-meta {
  color: rgba(255, 255, 255, 0.75);
  font-size: 11px;
}

.sales-table-wrap {
  margin-top: 14px;
  max-height: 320px;
  overflow: auto;
}

/* Top snacks - horizontal bars, single hue (this is a magnitude ranking,
   not a set of distinct identities), value labeled after the bar rather
   than inside it so it never needs to be measured against the fill width. */
.snack-bars {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
}

.snack-bar-row {
  align-items: center;
  display: grid;
  gap: 10px;
  grid-template-columns: minmax(90px, 230px) 1fr auto;
}

.snack-bar-name {
  color: var(--text);
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.snack-bar-track {
  background: rgba(24, 79, 42, 0.06);
  border-radius: 999px;
  display: block;
  height: 16px;
  overflow: hidden;
}

.snack-bar-fill {
  background: var(--green);
  border-radius: 999px;
  display: block;
  height: 100%;
}

.snack-bar-value {
  color: var(--muted);
  font-size: 12px;
  white-space: nowrap;
}

/* Payment sources - genuinely distinct categories (identity, not
   magnitude), so this is the one categorical chart on the page: a single
   segmented bar (part-to-whole) plus a legend, capped at three validated
   hues (brand green, blue, violet) with anything past that folded into a
   neutral "Other" - see the dataviz palette check run for this page. */
.payment-source-bar {
  border-radius: 999px;
  display: flex;
  gap: 2px;
  height: 26px;
  margin-top: 14px;
  overflow: hidden;
}

.payment-source-segment {
  height: 100%;
  min-width: 3px;
}

.payment-source-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 12px;
}

.payment-source-legend-item {
  align-items: center;
  display: flex;
  gap: 6px;
}

.payment-source-dot {
  border-radius: 50%;
  flex: none;
  height: 10px;
  width: 10px;
}

.payment-source-legend-label {
  color: var(--text);
  font-size: 12.5px;
}

.payment-source-legend-value {
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 700;
}

/* Pending invites (accounting.html) - a short list of not-yet-answered
   invites, kept visually and physically separate from the main accounting
   table below it (see accounting()'s own filter in shared.js, which
   excludes these from that table entirely while they're pending/expired). */
.pending-invite-row {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 14px;
  padding: 10px 0;
}

.pending-invite-row:last-child {
  border-bottom: none;
}

.pending-invite-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.pending-invite-status {
  color: var(--muted);
  flex: none;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}

.pending-invite-status.is-expired {
  color: #8a2f2f;
}

/* Docs page (docs.html) - sticky sidebar + a column of reference cards,
   reusing the site's existing card/table/badge language rather than
   inventing a separate visual style for one page. */
.docs-layout {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 28px;
  align-items: start;
}
.docs-sidebar {
  position: sticky;
  top: 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px;
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}
.docs-sidebar-title { font-weight: 700; color: var(--green-dark); margin-bottom: 6px; }
.docs-sidebar-group {
  margin-top: 14px;
  margin-bottom: 2px;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
.docs-sidebar a { padding: 6px 8px; border-radius: var(--radius-sm); color: var(--text); text-decoration: none; font-size: .92rem; }
.docs-sidebar a:hover { background: var(--green-soft); }
.docs-sidebar a.active { background: var(--green-light); color: var(--green-dark); font-weight: 600; }

.docs-content { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.docs-section {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px 24px;
  scroll-margin-top: 16px;
}
.docs-section h2 { margin-top: 0; color: var(--green-dark); }
.docs-section h3 { margin-bottom: 6px; }
.docs-section p { line-height: 1.55; }
.docs-section ul { line-height: 1.55; padding-left: 20px; }
.docs-section code { background: var(--green-soft); border-radius: 4px; padding: 1px 5px; font-size: .9em; }

.docs-callout {
  border-left: 3px solid var(--green);
  background: var(--green-soft);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  margin: 12px 0;
  font-size: .92rem;
  line-height: 1.5;
}
.docs-callout-info { border-left-color: #2c5a8a; background: #eaf1fb; }
.docs-callout-warn { border-left-color: #8a6412; background: #fdf3e2; }
.docs-callout-danger { border-left-color: #8a2f2f; background: #faf0ef; }

.docs-pill { display: inline-block; border-radius: 999px; padding: 2px 9px; font-size: .8rem; font-weight: 600; }
.docs-pill-green { background: var(--green-light); color: var(--green-dark); }
.docs-pill-amber { background: #fdf3e2; color: #8a6412; }
.docs-pill-red { background: #faf0ef; color: #8a2f2f; }
.docs-pill-blue { background: #eaf1fb; color: #2c5a8a; }

.docs-code { position: relative; margin: 10px 0; }
.docs-code pre {
  background: #1e2433;
  color: #e6ebf5;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  overflow-x: auto;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: .88rem;
  margin: 0;
}
.docs-copy {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(255, 255, 255, .12);
  color: #e6ebf5;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: var(--radius-sm);
  padding: 3px 9px;
  font-size: .75rem;
  cursor: pointer;
}
.docs-copy:hover { background: rgba(255, 255, 255, .2); }

@media (max-width: 860px) {
  .docs-layout { grid-template-columns: 1fr; }
  .docs-sidebar { position: static; max-height: none; }
}
