:root {
  --admin-green-dark: #163d29;
  --admin-green: #1f6b45;
  --admin-rose: #c8355d;
  --admin-rose-dark: #a32749;
  /* Stage 11E-D: named so the existing warm amber/gold tones already used
     by several status badges (out-of-stock, "contacted", "out for
     delivery" -- see .admin-badge-outofstock etc. below) can be reused
     consistently as an explicit "occasional floral accent" token,
     without inventing a new color scheme. */
  --admin-gold: #c8a24b;
  --admin-ink: #26221d;
  --admin-muted: #6f6a62;
  --admin-border: #e3ddd2;
  --admin-bg: #f7f3ec;
  --admin-white: #ffffff;
  --admin-error-bg: #fbe9ee;
  --admin-error-text: #a32749;
  --admin-radius: 16px;
  --admin-radius-sm: 10px;
  /* Stage 16: was 0 12px 30px / 0.12 -- a wide, floaty blur that read more
     like a marketing card than an operations console. Tighter blur/spread
     and a touch less opacity keeps the same soft green-tinted shadow
     language everywhere this token is already used (~16 rules) but
     restrained, matching a denser, more professional SaaS admin feel. */
  --admin-shadow: 0 2px 10px rgba(22, 61, 41, 0.08);
  --ff-heading: "Playfair Display", Georgia, "Times New Roman", serif;
  --ff-body: "Poppins", Arial, Helvetica, sans-serif;

  /* Stage 13 -- named aliases/additions for the florist design system, so
     new components reference tokens by role rather than scattering hex
     values. Deliberately does NOT rename --admin-bg/--admin-ink (used in
     ~370 existing rules) -- --admin-cream/--admin-text are aliases of the
     exact same values, not replacements, so this is purely additive. */
  --admin-cream: var(--admin-bg);
  --admin-text: var(--admin-ink);
  --admin-blush: #fbe9ee;
  --admin-blush-border: #f3d3dc;
  --admin-gold-bg: #f8f0dd;
  --admin-green-bg: #e9f3ec;
  /* Section 11: buttons/inputs use this moderate radius by default now
     instead of the old full-pill (100px) treatment -- see .admin-btn
     below. Kept as its own token (not reusing --admin-radius-sm) so it
     can be tuned for controls specifically without affecting cards. */
  --admin-radius-btn: 10px;
}

* { box-sizing: border-box; }

/* Stage 14 (checkpoint): investigated the reported "unexpected text
   caret/typing indicator when clicking around the Admin UI." Searched
   this entire project for every mechanism that legitimately produces an
   editable text caret -- contenteditable/-Editable, tabindex, autofocus,
   caret-color, execCommand/designMode, any <input> missing an explicit
   type= (which would default to type="text"), and any input positioned
   to invisibly overlay other content -- and found none outside the real,
   intentional form fields and the one small opacity:0 checkbox used for
   .admin-switch's toggle styling (a 46x27px hit target, not a text
   field). The actual cause is the ordinary browser default: plain UI
   chrome (buttons, nav links, badges, close/back controls) has no
   `user-select` override, so the browser applies its normal
   text-selection behavior to their labels -- on desktop that shows the
   I-beam text cursor on hover/click, and on mobile a tap-and-hold shows
   selection handles -- both of which read as "this looks editable" even
   though nothing is actually focused or contenteditable. Genuine content
   the admin might legitimately want to select/copy (product names,
   customer details, order numbers, activity text, table data) is
   deliberately left untouched below -- only non-editable, purely
   interactive chrome gets user-select:none. This does not touch cursor
   styling (buttons already use cursor:pointer elsewhere), disable any
   input, or affect keyboard navigation/focus-visible outlines at all. */
.admin-btn,
.admin-badge,
.admin-sidebar-link,
.admin-sidebar-brand,
.admin-bottom-nav a,
.admin-profile-btn,
.admin-profile-menu-item,
.admin-modal-close,
.admin-modal-back,
.admin-modal-backtotop,
.admin-backtotop,
.admin-backto-dashboard,
.admin-summary-label,
.admin-table th,
.admin-brand {
  -webkit-user-select: none;
  user-select: none;
}

/* The `hidden` attribute must always win over any component's own `display`
   rule (e.g. `.admin-topbar { display: flex }`), regardless of selector
   specificity elsewhere in this file. */
[hidden] { display: none !important; }

/* Visually hidden but still available to screen readers/labels -- e.g.
   the Dashboard's #dashboardRangeSelect <label>, whose text is redundant
   next to the select's own calendar icon for sighted users. */
.admin-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--ff-body);
  color: var(--admin-ink);
  background: var(--admin-bg);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

.admin-shell { min-height: 100vh; display: flex; flex-direction: column; }

.admin-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  /* Stage 14: reduced from 16px 24px -- a compact header, not a heavy one. */
  padding: 12px 18px;
  background: var(--admin-green-dark);
  color: var(--admin-white);
  position: relative;
  z-index: 45; /* above .admin-sidebar (40) so the profile menu never sits behind it */
}
/* Stage 14: single round avatar button replacing the old always-visible
   name + large "Log Out" button. */
.admin-profile-btn {
  width: 36px; height: 36px;
  border-radius: 100px;
  border: none;
  background: rgba(255,255,255,0.14);
  color: var(--admin-white);
  cursor: pointer;
  display: flex;
  flex: none;
}
.admin-profile-btn:hover { background: rgba(255,255,255,0.22); }
.admin-profile-avatar { margin: auto; font-size: 13px; font-weight: 700; letter-spacing: 0.02em; }
.admin-profile-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 18px;
  width: 220px;
  background: var(--admin-white);
  color: var(--admin-text);
  border-radius: var(--admin-radius-sm);
  border: 1px solid var(--admin-border);
  box-shadow: var(--admin-shadow);
  padding: 6px;
  z-index: 60;
}
.admin-profile-menu-head { padding: 10px 12px 8px; border-bottom: 1px solid var(--admin-border); margin-bottom: 6px; }
.admin-profile-menu-head strong { display: block; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-profile-menu-head span { display: block; font-size: 12px; color: var(--admin-muted); }
.admin-profile-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  border-radius: var(--admin-radius-sm);
  border: none;
  background: none;
  color: var(--admin-text);
  font-size: 13.5px;
  font-weight: 600;
  font-family: inherit;
  text-decoration: none;
  cursor: pointer;
}
.admin-profile-menu-item:hover { background: var(--admin-bg); }
.admin-profile-menu-item-danger { color: var(--admin-rose-dark); }
.admin-brand { font-family: var(--ff-heading); font-weight: 700; font-size: 15.5px; white-space: nowrap; }
@media (min-width: 400px) { .admin-brand { font-size: 17px; } }
@media (min-width: 480px) { .admin-brand { font-size: 19px; } }

.admin-main {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.admin-card {
  width: 100%;
  max-width: 380px;
  background: var(--admin-white);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
  padding: 36px 30px;
  text-align: center;
}
.admin-card-wide { max-width: 520px; text-align: left; }

.admin-card h1 {
  font-size: 26px;
  margin: 0 0 8px;
  color: var(--admin-green-dark);
}
.admin-subtext { color: var(--admin-muted); font-size: 14.5px; margin: 0 0 24px; }

.admin-field { text-align: left; margin-bottom: 18px; }
.admin-field label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 7px; }
.admin-field input {
  width: 100%;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 14.5px;
  background: #fbf9f5;
  transition: border-color 0.2s ease;
}
.admin-field input:focus { outline: none; border-color: var(--admin-rose); }

.admin-message {
  font-size: 13.5px;
  padding: 10px 14px;
  border-radius: var(--admin-radius-sm);
  margin-bottom: 18px;
  background: var(--admin-error-bg);
  color: var(--admin-error-text);
  text-align: left;
}
.admin-message-error { margin-bottom: 24px; }
/* Stage 4D: .admin-message defaults to the error (red) palette — there was
   no neutral/positive variant before this stage, since nothing previously
   needed to show a message that wasn't an error. Reuses the same green
   already used for .admin-badge-active/.admin-badge-enq-converted. */
.admin-message-success { background: #e6f4ea; color: #1f6b45; }

/* Stage 11E-A: plain text-style button for secondary/low-emphasis actions
   ("Forgot password?", "Back to Sign In") -- no prior generic link style
   existed in this file to reuse. */
.admin-inline-link {
  display: block;
  width: 100%;
  margin-top: 14px;
  border: none;
  background: none;
  color: var(--admin-muted);
  font-size: 13px;
  font-family: inherit;
  text-align: center;
  text-decoration: underline;
  cursor: pointer;
  padding: 6px;
}
.admin-inline-link:hover { color: var(--admin-green-dark); }

.admin-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 13px 24px;
  /* Stage 13, Section 11: was 100px (full pill) -- every button in the
     app looked like a decorative rounded pill regardless of purpose.
     Moderate radius reads as a proper application control instead. */
  border-radius: var(--admin-radius-btn);
  font-weight: 600;
  font-size: 14.5px;
  font-family: inherit;
  border: 2px solid transparent;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.admin-btn:active { transform: scale(0.97); }
.admin-btn:disabled { opacity: 0.65; cursor: not-allowed; }
.admin-btn-block { width: 100%; }
/* Stage 13, Section 11: PRIMARY is now filled botanical green (was rose)
   -- rose/pink is reserved for the ACCENT tier and DANGER actions only,
   per the explicit "do not make everything pink" direction. Save/Sign
   In/Add Product/Continue-style primary actions all use this class, so
   this one change re-balances the whole app's color weight without
   touching any markup. */
.admin-btn-primary { background: var(--admin-green); color: var(--admin-white); }
.admin-btn-primary:hover:not(:disabled) { background: var(--admin-green-dark); }
/* Stage 13, Section 11: ACCENT tier -- blush/rose, for the few actions
   that should stand out as "special" without being destructive (DANGER)
   or the page's single main action (PRIMARY). Not yet used everywhere;
   available for anywhere a future change wants it. */
.admin-btn-accent { background: var(--admin-rose); color: var(--admin-white); }
.admin-btn-accent:hover:not(:disabled) { background: var(--admin-rose-dark); }

.admin-topbar .admin-btn-outline { background: transparent; border-color: rgba(255,255,255,0.6); color: var(--admin-white); }
.admin-topbar .admin-btn-outline:hover { background: rgba(255,255,255,0.12); }

#backToLoginBtn.admin-btn-outline { background: transparent; border-color: var(--admin-green); color: var(--admin-green); }
#backToLoginBtn.admin-btn-outline:hover { background: var(--admin-green); color: var(--admin-white); }

@media (max-width: 480px) {
  .admin-card { padding: 28px 22px; }
  .admin-topbar { padding: 14px 18px; }
}

/* ==================== Phase 2: Product Management ====================
   Mobile-first: base rules below target small phones. Larger screens are
   progressively enhanced via min-width media queries further down. Sachin
   and Chetan will use this primarily on their phones, so the phone layout
   is the default, not an afterthought. */

.admin-topbar-right { display: flex; align-items: center; gap: 10px; min-width: 0; }
.admin-user {
  font-size: 12.5px;
  color: rgba(255, 255, 255, 0.85);
  max-width: 64px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
@media (min-width: 400px) { .admin-user { max-width: 90px; font-size: 13px; } }
.admin-brand { flex: none; }

.admin-main.is-dashboard { align-items: flex-start; justify-content: center; padding: 16px; }

/* Stage 20: the Stage 18A fix (raising max-width 1280->1760) assumed 1760
   would only bind on "genuinely huge monitors (2500px+)" -- verified
   false: 1760 + the 240px sidebar inset + 2*16px page padding already
   exceeds a plain 1920px monitor, so .admin-main's justify-content:center
   was STILL centering the dashboard block there, with the exact
   "growing empty margins" symptom Stage 18A itself describes (measured:
   an ~100px dead gutter between the sidebar and the content, plus an
   80px margin on the right edge, at 1920x1080). justify-content:center
   is still correct and needed for the LOGIN card (.admin-card,
   max-width:380px, genuinely should stay centered) -- so it's overridden
   to flex-start only once the fixed sidebar exists (900px+), not removed
   outright. */
@media (min-width: 900px) {
  .admin-main.is-dashboard { justify-content: flex-start; }
}

/* max-width raised 1760 -> 2400: keeps content from stretching edge-to-
   edge into unreadably long rows on genuinely huge/ultra-wide monitors,
   without binding at any of this project's tested desktop widths
   (1920px and below all stay unconstrained by this cap now). */
.admin-dashboard {
  width: 100%;
  max-width: 2400px;
  text-align: left;
}

/* ==================== Stage 13: shared sidebar + bottom navigation ====================
   Both <nav> elements are the FIRST children of #dashboardCard in every
   page that has one (see admin.html/admin-orders.html/
   admin-enquiries.html/admin-dashboard.html) -- nested there so
   #dashboardCard's own existing [hidden] auth-gating (js/admin.js) hides/
   shows them for free. .admin-content-area wraps everything else that was
   already inside #dashboardCard.

   Mobile-first: sidebar is not rendered at all below the desktop
   breakpoint; bottom-nav is the mobile navigation instead. */
.admin-sidebar { display: none; }

.admin-bottom-nav {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  display: flex;
  background: var(--admin-white);
  border-top: 1px solid var(--admin-border);
  box-shadow: 0 -4px 16px rgba(22, 61, 41, 0.08);
  padding: 6px max(6px, env(safe-area-inset-left)) max(6px, env(safe-area-inset-bottom)) max(6px, env(safe-area-inset-right));
}
.admin-bottom-nav a {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 2px;
  border-radius: var(--admin-radius-sm);
  color: var(--admin-muted);
  text-decoration: none;
  font-size: 10.5px;
  font-weight: 600;
}
.admin-bottom-nav a svg { width: 20px; height: 20px; }
.admin-bottom-nav a span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.admin-bottom-nav a.is-active { color: var(--admin-green-dark); background: var(--admin-green-bg); }

/* Content that used to be #dashboardCard's direct children -- now needs
   bottom clearance so the fixed bottom-nav never overlaps the last row of
   content (e.g. a table's last row, or a floating action button). */
.admin-content-area { padding-bottom: 76px; }

@media (min-width: 900px) {
  .admin-sidebar {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 220px;
    background: var(--admin-white);
    border-right: 1px solid var(--admin-border);
    padding: 20px 14px;
    z-index: 40;
    overflow-y: auto;
  }
  .admin-sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px 20px;
    font-family: var(--ff-heading);
    font-weight: 700;
    font-size: 16px;
    color: var(--admin-green-dark);
  }
  .admin-sidebar-leaf { font-size: 20px; }
  .admin-sidebar-brand-text small { display: block; font-family: var(--ff-body); font-weight: 600; font-size: 11px; color: var(--admin-muted); text-transform: uppercase; letter-spacing: 0.04em; }
  .admin-sidebar-link {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border-radius: var(--admin-radius-sm);
    color: var(--admin-muted);
    text-decoration: none;
    font-weight: 600;
    font-size: 14px;
    margin-bottom: 2px;
  }
  .admin-sidebar-link svg { width: 19px; height: 19px; flex: none; }
  .admin-sidebar-link:hover { background: var(--admin-bg); color: var(--admin-text); }
  /* The requested "subtle botanical-green background + clear indicator" --
     a left accent bar (not a filled pill) reads as an application nav
     state rather than another decorative button. */
  .admin-sidebar-link.is-active {
    background: var(--admin-green-bg);
    color: var(--admin-green-dark);
    box-shadow: inset 3px 0 0 var(--admin-green);
  }
  .admin-sidebar-spacer { flex: 1; }
  .admin-sidebar-divider { height: 1px; background: var(--admin-border); margin: 8px 10px 10px; }
  .admin-sidebar-link-secondary { font-size: 13px; }

  .admin-bottom-nav { display: none; }
  .admin-content-area { padding-bottom: 0; }

  /* Room for the fixed sidebar. #dashboardCard's own children (the
     sidebar/bottom-nav <nav> elements, plus .admin-content-area) are laid
     out by the existing .admin-main.is-dashboard flex/centering rules --
     this just insets the whole dashboard area from the left edge. */
  .admin-dashboard { padding-left: 240px; }
}

.admin-page-head {
  margin-bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.admin-page-head h1 {
  font-family: var(--ff-heading);
  font-size: 23px;
  margin: 0 0 6px;
  color: var(--admin-green-dark);
}
.admin-page-head .admin-subtext { margin: 0; font-size: 13.5px; }
/* Stage 13: dashboard-only greeting line, set by js/admin-dashboard.js's
   renderGreeting(). Plain body font/weight -- a greeting is operational
   copy, not a decorative heading. */
.admin-dash-greeting { margin: 0 0 2px; font-size: 13px; font-weight: 600; color: var(--admin-green-dark); }
/* Stage 14: was flex-direction:column + full-width buttons (a big green
   bar stacked on a big neutral bar) -- now a compact wrapping row at every
   size, sized to its own label rather than the full page width, matching
   "professional, compact primary action" instead of dominating the page. */
.admin-page-actions { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px; }

@media (min-width: 640px) {
  .admin-page-head { flex-direction: row; align-items: center; justify-content: space-between; }
  .admin-page-actions { flex-direction: row; }
  .admin-page-actions .admin-btn { width: auto; }
}

/* ---------- Summary cards ---------- */
.admin-summary-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 22px;
}
.admin-summary-card {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-top: 3px solid var(--admin-green);
  border-radius: var(--admin-radius-sm);
  box-shadow: var(--admin-shadow);
  padding: 14px 16px;
  min-width: 0;
}
/* Stage 13: "requires attention" KPIs (pending orders/payments, only
   applied when the count is actually > 0 -- see js/admin-dashboard.js)
   get a warm gold accent instead of the routine green one, plus a very
   light tinted background -- noticeable without being an alarming red. */
.admin-summary-card-attention { border-top-color: var(--admin-gold); background: var(--admin-gold-bg); }
/* Stage 14 (checkpoint): Dashboard KPI cards that drill down to an
   existing page (see js/admin-dashboard.js's kpiCardHtml `href` param)
   render as <a> instead of <div> -- reset link defaults so it still looks
   like a plain card, then add a restrained affordance (cursor, subtle
   lift, visible focus ring) so only these meaningfully-clickable cards
   read as interactive. Non-linked cards (Total Sales, Conversion Rate)
   are untouched. */
.admin-summary-card-link {
  display: block;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease;
}
.admin-summary-card-link:hover {
  border-color: var(--admin-green);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
  transform: translateY(-1px);
}
.admin-summary-card-link:focus-visible {
  outline: 2px solid var(--admin-green);
  outline-offset: 2px;
}
.admin-summary-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--admin-muted);
  margin-bottom: 6px;
}
.admin-summary-value {
  display: block;
  font-size: 22px;
  font-weight: 700;
  color: var(--admin-green-dark);
}

/* Stage 19: icon circle above the label on the Business Overview KPI
   cards (see KPI_ICONS in js/admin-dashboard.js) -- matches the reference
   dashboard's per-card icon treatment. Only ever rendered by
   renderExecKpis' kpiCardHtml calls; every other .admin-summary-card on
   this and other Admin pages simply never includes this span, so this is
   purely additive. */
.admin-summary-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: var(--admin-green-bg);
  color: var(--admin-green-dark);
  margin-bottom: 10px;
}
.admin-summary-icon svg { width: 17px; height: 17px; }
.admin-summary-icon-attention { background: var(--admin-gold-bg); color: #8a5a1f; }

/* ---------- Toolbar ---------- */
.admin-toolbar {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
  margin-bottom: 18px;
}
.admin-search { min-width: 0; }
.admin-search input {
  width: 100%;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 16px; /* 16px avoids iOS Safari auto-zoom on focus */
  background: var(--admin-white);
  min-height: 46px;
}
.admin-search input:focus { outline: none; border-color: var(--admin-rose); }
.admin-toolbar-filters { display: flex; gap: 10px; flex-wrap: wrap; }
.admin-toolbar-filters select {
  flex: 1 1 140px;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 12px;
  font-family: inherit;
  font-size: 14.5px;
  background: var(--admin-white);
  color: var(--admin-ink);
  min-height: 46px;
}

/* ---------- Shared list container ---------- */
.admin-table-wrap {
  background: transparent;
  border-radius: var(--admin-radius);
}
.admin-state {
  padding: 48px 20px;
  text-align: center;
  color: var(--admin-muted);
  font-size: 14.5px;
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
}
.admin-state-error { color: var(--admin-error-text); }

.admin-muted-text { color: var(--admin-muted); }

.admin-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.admin-badge-active { background: #e6f4ea; color: #1f6b45; }
.admin-badge-inactive { background: #f1ede6; color: #6f6a62; }
/* Stock is a separate axis from Active/Inactive, so it gets its own colors
   (blue/amber) rather than reusing green/gray, to avoid the two states
   being visually confused with each other. */
.admin-badge-instock { background: #e5f1fb; color: #1d5f8f; }
.admin-badge-outofstock { background: #fdecd2; color: #8a5a1f; }
/* Enquiry status — its own 5-tone set (Stage 3C), same .admin-badge shape.
   NEW reuses the "needs attention" blue already used for In Stock;
   CONTACTED reuses the amber already used for Out of Stock; CONVERTED
   reuses the existing green "Active" tone (a success state); CLOSED
   reuses the existing gray "Inactive" tone (an archived/neutral state).
   QUOTED is the one genuinely new tone needed, since none of the above
   fit "waiting on the customer to decide". */
.admin-badge-enq-new { background: #e5f1fb; color: #1d5f8f; }
.admin-badge-enq-contacted { background: #fdecd2; color: #8a5a1f; }
.admin-badge-enq-quoted { background: #f0e9f7; color: #6b3fa0; }
.admin-badge-enq-converted { background: #e6f4ea; color: #1f6b45; }
.admin-badge-enq-closed { background: #f1ede6; color: #6f6a62; }
/* Order status (Stage 4C) — reuses enquiry/product tones wherever the
   semantics already line up (NEW=blue, PREPARING=amber like CONTACTED,
   READY=purple like QUOTED, DELIVERED=green like Active, CANCELLED=gray
   like Inactive/Closed) so the 7-value order lifecycle doesn't need 7 new
   colors. CONFIRMED and OUT_FOR_DELIVERY are the only two genuinely new
   tones, since neither has an existing semantic match. Payment-status
   badges (Pending/Partial/Paid) reuse existing tones entirely — see
   js/admin-orders.js's PAYMENT_BADGE_CLASS, no new CSS needed there. */
.admin-badge-ord-confirmed { background: #e3f6f5; color: #1a7a72; }
.admin-badge-ord-outfordelivery { background: #fde8e0; color: #a85a2a; }

.admin-btn-sm { padding: 10px 16px; font-size: 12.5px; min-height: 40px; }
.admin-btn-disabled { background: var(--admin-bg); color: var(--admin-muted); border-color: var(--admin-border); cursor: not-allowed; }
.admin-btn-disabled:hover { background: var(--admin-bg); }

/* ---------- Mobile: stacked product cards (default view) ---------- */
.admin-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 14px;
}
.admin-pcard {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
  overflow: hidden;
  cursor: pointer;
  transition: box-shadow 0.15s ease;
}
.admin-pcard:hover { box-shadow: var(--admin-shadow); }
.admin-pcard-media { position: relative; width: 100%; aspect-ratio: 16 / 10; background: var(--admin-bg); }
.admin-pcard-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.admin-pcard-img-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #e9f3ec;
  color: var(--admin-green);
  font-weight: 700;
  font-size: 32px;
}
.admin-pcard-status { position: absolute; top: 10px; right: 10px; }
.admin-pcard-stock { position: absolute; top: 10px; left: 10px; }
.admin-pcard-body { padding: 14px 16px 16px; }
.admin-pcard-name {
  font-size: 16.5px;
  font-weight: 700;
  margin: 0 0 4px;
  color: var(--admin-ink);
  overflow-wrap: break-word;
}
.admin-pcard-meta { margin: 0 0 10px; font-size: 12.5px; color: var(--admin-muted); }
.admin-pcard-price-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
/* Stage 11E: dropped the decorative var(--ff-heading) serif override --
   the same "overly creative for a price figure" treatment reported on the
   customer site (see css/style.css's .cart-summary-total), present here
   too. Color/size/weight (Admin's own established brand green, kept
   as-is) still make it read as a price at a glance; only the font
   changes, to Admin's normal, highly-readable UI font. */
.admin-pcard-price { font-size: 18px; font-weight: 700; color: var(--admin-green-dark); }
.admin-pcard-discount { font-size: 12.5px; font-weight: 600; color: var(--admin-rose-dark); }
.admin-pcard-stats { display: flex; gap: 14px; font-size: 12px; color: var(--admin-muted); margin-bottom: 12px; }
.admin-pcard-actions { display: flex; gap: 8px; }
.admin-pcard-actions .admin-btn { flex: 1; }
.admin-row-actions { display: flex; gap: 8px; white-space: nowrap; }

/* ---------- Desktop: data table (hidden until min-width breakpoint) ---------- */
.admin-table { display: none; }

/* Locks background scroll while els.modalOverlay is open (see
   openModalOverlay()/closeModal() in js/admin-products.js) — a
   position:fixed overlay alone does not stop the page behind it from
   scrolling on mobile, and leaving it unlocked is also what made the first
   tap after opening the modal unresponsive on mobile until it was closed
   and reopened. */
body.admin-body-modal-open { overflow: hidden; }

/* ---------- Detail modal (mobile: bottom sheet) ---------- */
.admin-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(22, 61, 41, 0.45);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  z-index: 50;
}
/* Stage 14: the flex-column/pinned-header structure (close X + header row
   never scroll away, only the body content does) previously applied on
   mobile only -- now unconditional, since the same "X scrolls out of
   reach on a tall form" problem reported for the Edit Product modal
   happens identically on desktop. See the (former mobile-only) comment
   further down for the original root-cause writeup; nothing about that
   reasoning changes, it just no longer stops at 640px. */
.admin-modal {
  background: var(--admin-white);
  box-shadow: var(--admin-shadow);
  width: 100%;
  max-width: 100%;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  overflow: hidden; /* the outer box no longer scrolls -- only its content child does */
  /* Stage 14 (checkpoint): top padding nudged 24px -> 32px (mobile-first
     value, overridden to a uniform 30px on desktop below at 640px+ where
     the modal is centered rather than a near-full-height bottom sheet) --
     per fresh mobile screenshots, the Back/X row sat too close to the
     true top edge for comfortable safe-area/thumb clearance. Small nudge
     only, not a nav redesign. */
  padding: 32px 18px 28px;
  border-radius: 20px 20px 0 0;
  position: relative;
}
.admin-modal-headerbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex: none;
  margin-bottom: 10px;
}
.admin-modal-close {
  position: static;
  flex: none;
  width: 40px; height: 40px;
  border-radius: 100px;
  border: none;
  background: var(--admin-bg);
  color: var(--admin-ink);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  /* Stage 14 (checkpoint): .admin-modal-back is display:none outside the
     mobile media query below, which leaves .admin-modal-close as the
     ONLY flex item in .admin-modal-headerbar on desktop. A lone flex item
     under `justify-content: space-between` collapses to the container's
     main-start edge (left), not the end -- that's the actual root cause
     of the X reportedly appearing on the LEFT instead of top-right.
     margin-left: auto consumes all free space to its own left regardless
     of whether a sibling precedes it, so this pins it to the right edge
     in both the 1-child (desktop) and 2-child (mobile, Back + Close)
     cases -- space-between above becomes redundant on mobile but harmless. */
  margin-left: auto;
}
.admin-modal-close:hover { background: var(--admin-border); }
.admin-modal > *:not(.admin-modal-headerbar):not(#productModalActions) {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1 1 auto;
  min-height: 0; /* required so a flex child actually scrolls instead of overflowing its row */
}
/* Edit Product's pinned "Edit Product" action footer (Stage 11E) -- same
   reasoning as the headerbar above, now also unconditional. */
#productModalActions:not(:empty) {
  flex: none;
  margin-top: 0;
  padding-top: 12px;
  border-top: 1px solid var(--admin-border);
}

/* Stage 4H: wraps .admin-modal-close and (mobile-only) .admin-modal-back
   so both sit in one row -- see the real .admin-modal-headerbar flex
   rule near the top of the .admin-modal block above (Stage 14 made it
   apply at all sizes, not just mobile; see that rule's own comment). */
.admin-modal-back {
  display: none; /* re-enabled only inside the mobile block below — see ADMIN-07 */
}

/* ==================== Stage 4G: mobile modal close reachability ====================
   Root cause: on mobile, .admin-modal-overlay aligns the modal to the
   BOTTOM of the screen (align-items: flex-end, see below) and .admin-modal
   can be up to 92vh tall — a near-full-height bottom sheet. .admin-modal-
   close was `position: absolute` *inside that same scrolling box*, which
   meant two compounding problems: (1) it sat right at the top of a
   near-full-height sheet, close to the true top edge of the physical
   screen and outside comfortable one-handed thumb reach, and (2) because
   it was an absolutely-positioned descendant of the box that itself
   scrolls (.admin-modal has overflow-y: auto), it scrolled away entirely
   once the admin scrolled down into a long modal (e.g. the Order detail
   modal's 7 sections) — leaving no way to close it without scrolling all
   the way back up first.

   Fix: on mobile only, .admin-modal becomes a flex column. The close
   button becomes a normal (non-absolute) flex item pinned to the top of
   the sheet — it never scrolls away again — while the modal's actual
   content becomes its own independently-scrolling flex item beneath it.
   Visual position (top-right of the sheet) is unchanged; this is a pure
   reflow, no HTML/JS/id/behavior changes. Desktop (640px+) is completely
   untouched — the existing centered/padded layout there was never the
   reported problem.

   Selector note: every .admin-modal in this project has the same shape —
   .admin-modal-close followed by exactly one content div (id differs per
   modal: enquiryModalBody, orderModalBody, convertOrderModalBody,
   fieldSettingsModalBody, productModalBody, ...) — so
   `.admin-modal > *:not(.admin-modal-close)` reaches the content of every
   modal on every Admin page with this one rule, without adding a class to
   any of them.

   Known, accepted scope limit: the "Change Details" / product-history
   popup (#activityDetailOverlay, admin.html) reuses .admin-modal-close's
   styling but lives inside .admin-confirm-box, a differently-shaped,
   THREE-child, centered (not bottom-sheet) dialog — a title <p> sits
   between the close button and the body as separate siblings, so this
   same selector would also make the short title its own equal-height
   scroll region, visibly squeezing the real content. Extending this fix
   there would need a more bespoke selector for a secondary, lower-
   frequency, already-less-severe surface (centered+padded, not a
   near-full-height sheet) — left unchanged rather than risk it, per "do
   not redesign the entire Admin Panel".

   ---- Stage 4H addendum: distinct mobile Back affordance (ADMIN-07) ----
   Stage 4G made the X reachable, but the requirement explicitly asks for
   more than a repositioned X ("do not rely solely on... an extreme
   top-right X"). .admin-modal-close is now wrapped in
   .admin-modal-headerbar together with a new, adjacent .admin-modal-back
   button (mobile-only; markup is identical across every modal — see the
   selector note above, same reasoning applies here). Both sit in one
   flex row via the wrapper: Back at the start, Close at the end, purely
   from DOM order plus `justify-content: space-between` — no manual
   left/right offsets to fight over. Only .admin-modal-close's OWN rule
   changes below (align-self/margin-bottom move to the new wrapper, since
   .admin-modal-close is no longer a direct flex child of .admin-modal —
   it's nested one level deeper now); the content-scroll exclusion
   selector is updated from :not(.admin-modal-close) to
   :not(.admin-modal-headerbar) for the same reason — .admin-modal-close
   alone is no longer a direct child of .admin-modal, so the OLD selector
   would have wrongly also tried to scroll the wrapper itself. */
@media (max-width: 639.98px) {
  /* Stage 14: the flex/pinned-header/scroll-region/#productModalActions
     rules that used to live here are now unconditional (see the base
     .admin-modal rules above) -- only what's genuinely mobile-only
     (the Back button affordance) remains scoped to this query. */
  .admin-modal-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 40px;
    padding: 0 14px 0 12px;
    border-radius: 100px;
    border: none;
    background: var(--admin-bg);
    color: var(--admin-ink);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
  }
  .admin-modal-back:hover { background: var(--admin-border); }
  /* No longer needed once the button isn't overlapping the title (it now
     sits in its own row above) — matches the existing min-width:960px
     override of this same rule, just extended down to mobile too. */
  .admin-detail-title { margin-right: 0; }
}

/* ==================== Stage 4H: "Back to Top" controls ====================
   Two independent instances of the same idea, both position: fixed (so
   neither depends on figuring out which element is scrolling in order to
   just render in place):

   .admin-modal-backtotop — one per modal, always in the same on-screen
   corner. Its z-index (55) sits ABOVE .admin-modal-overlay's (50) so it's
   usable while that modal is open; its visibility/scroll-target IS aware
   of which element actually scrolls (.admin-modal itself at desktop vs.
   the separate content child at mobile, per the Stage 4G split above) —
   that part is necessarily handled in JS (see resolveModalScroller() /
   bindModalBackToTop() in each admin-*.js file), not CSS.

   .admin-backtotop — one per page (admin.html / admin-enquiries.html /
   admin-orders.html / admin-help.html), for the page's own document
   scroll. Its z-index (10) sits BELOW the modal overlay on purpose: while
   a modal is open the page is scroll-locked anyway (admin-body-modal-
   open), so it should be visually covered, not competing for the same
   corner. */
.admin-modal-backtotop,
.admin-backtotop {
  position: fixed;
  right: 20px;
  bottom: 20px;
  width: 44px;
  height: 44px;
  border-radius: 100px;
  border: none;
  background: var(--admin-ink);
  color: var(--admin-white);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--admin-shadow);
  display: flex;
  align-items: center;
  justify-content: center;
}
.admin-modal-backtotop:hover,
.admin-backtotop:hover { opacity: 0.9; }
.admin-modal-backtotop { z-index: 55; }
.admin-backtotop { z-index: 10; }

.admin-detail-image {
  width: 100%;
  max-height: 240px;
  object-fit: cover;
  border-radius: var(--admin-radius-sm);
  margin-bottom: 18px;
  background: var(--admin-bg);
}
.admin-detail-title {
  font-size: 20px;
  margin: 0 24px 4px 0;
  color: var(--admin-green-dark);
  overflow-wrap: break-word;
}
.admin-detail-category { color: var(--admin-muted); font-size: 13.5px; margin: 0 0 16px; }
.admin-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 18px; }
.admin-detail-item { min-width: 0; }
.admin-detail-item label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--admin-muted);
  margin-bottom: 4px;
  font-weight: 600;
}
.admin-detail-item span { font-size: 14px; font-weight: 600; overflow-wrap: break-word; }
.admin-detail-price-old { text-decoration: line-through; color: var(--admin-muted); font-weight: 400; margin-right: 8px; }

.admin-detail-section { margin-top: 20px; }
.admin-detail-section h3 { font-size: 15px; font-weight: 700; margin: 0 0 10px; color: var(--admin-green-dark); }
.admin-variant-row, .admin-image-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  margin-bottom: 8px;
  font-size: 13.5px;
  flex-wrap: wrap;
}
.admin-image-thumbs { display: flex; flex-wrap: wrap; gap: 10px; }
.admin-image-thumbs img { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; border: 1px solid var(--admin-border); }

/* ==================== Responsive breakpoints (mobile-first: min-width) ==================== */

/* Small phones -> larger phones */
@media (min-width: 400px) {
  .admin-summary-value { font-size: 24px; }
}

/* Tablets: two-column cards, row-based toolbar */
@media (min-width: 640px) {
  .admin-main.is-dashboard { padding: 24px; }
  .admin-page-head h1 { font-size: 26px; }
  .admin-toolbar { grid-template-columns: 1fr auto; align-items: center; }
  .admin-toolbar-filters { flex-wrap: nowrap; }
  .admin-toolbar-filters select { flex: 0 0 auto; width: 150px; }
  .admin-cards { grid-template-columns: repeat(2, 1fr); }
  .admin-modal-overlay { align-items: center; padding: 20px; }
  .admin-modal { max-width: 560px; max-height: 88vh; border-radius: var(--admin-radius); padding: 30px; }
  /* Stage 4C: the order detail modal has more sections (Customer/Order/
     Items/Delivery/Payment/Summary/Management) than the enquiry detail
     modal it's otherwise modeled on, so it gets a wider cap at this same
     breakpoint — still full-width and scrollable below it, same as every
     other modal.
     Stage 14 (checkpoint): 760px was still too narrow for the Edit
     Product modal's content (3-column image manager grid + AI multi-
     preview grid + form fields) at common desktop widths, per fresh
     screenshot evidence of horizontal scrolling. Switched to a fluid cap
     -- min(1040px, 94vw) -- reasoned from the 3-column image grid: at
     1040px, content width is roughly 1040 - 2*30 (padding) = 980px, so 3
     cards get ~318px each (comfortable for a square thumbnail + action
     buttons), while 94vw keeps a visible margin and never exceeds the
     viewport on a smaller desktop/tablet window (e.g. a 900px-wide
     browser gets a ~846px modal, not a fixed 1040px that would force the
     page itself to scroll horizontally). The order detail modal reuses
     this same class and simply gets more breathing room too. */
  .admin-modal-wide { max-width: min(1040px, 94vw); }
  .admin-user { max-width: 160px; font-size: 13.5px; }
  .admin-topbar-right { gap: 16px; }
}

/* Small laptops and up: switch from stacked cards to the full data table */
@media (min-width: 960px) {
  .admin-summary-grid { grid-template-columns: repeat(4, 1fr); gap: 18px; }
  .admin-summary-card { padding: 18px 20px; }
  .admin-summary-value { font-size: 28px; }
  .admin-page-head h1 { font-size: 28px; }

  .admin-cards { display: none; }
  .admin-table-wrap {
    background: var(--admin-white);
    border: 1px solid var(--admin-border);
    border-radius: var(--admin-radius);
    box-shadow: var(--admin-shadow);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
  .admin-state { border: none; box-shadow: none; border-radius: 0; }

  .admin-table { display: table; width: 100%; border-collapse: collapse; min-width: 900px; }
  .admin-table th {
    text-align: left;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--admin-muted);
    padding: 14px 16px;
    border-bottom: 1.5px solid var(--admin-border);
    background: #fbf9f5;
    white-space: nowrap;
  }
  .admin-table td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--admin-border);
    font-size: 14px;
    vertical-align: middle;
  }
  .admin-table tbody tr { cursor: pointer; transition: background 0.15s ease; }
  .admin-table tbody tr:hover { background: #fbf7f0; }
  .admin-table tbody tr:last-child td { border-bottom: none; }
  /* Stage 14: the Actions column (Edit/History) stays reachable even when
     the table needs its own horizontal scroll (min-width above, contained
     within .admin-table-wrap's own overflow-x -- the page itself never
     scrolls horizontally) -- sticky to the right edge with an opaque
     background so scrolled-under content doesn't show through, plus a
     subtle left border as a visual seam. */
  .admin-table th:last-child, .admin-table td.admin-row-actions {
    position: sticky;
    right: 0;
    background: var(--admin-white);
    border-left: 1px solid var(--admin-border);
  }
  .admin-table th:last-child { background: #fbf9f5; }
  .admin-table tbody tr:hover td.admin-row-actions { background: #fbf7f0; }

  .admin-product-cell { display: flex; align-items: center; gap: 12px; }
  .admin-thumb {
    width: 44px; height: 44px;
    border-radius: 8px;
    object-fit: cover;
    background: var(--admin-bg);
    flex: none;
    border: 1px solid var(--admin-border);
  }
  .admin-thumb-placeholder {
    width: 44px; height: 44px;
    border-radius: 8px;
    background: #e9f3ec;
    color: var(--admin-green);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 15px;
    flex: none;
    border: 1px solid var(--admin-border);
  }
  .admin-product-name { font-weight: 600; color: var(--admin-ink); }

  .admin-detail-title { margin-right: 0; }
}

/* ==================== Phase 2B: Edit Product form ==================== */

.admin-field select,
.admin-field textarea {
  width: 100%;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 16px; /* 16px avoids iOS Safari auto-zoom on focus */
  background: #fbf9f5;
  color: var(--admin-ink);
  transition: border-color 0.2s ease;
}
.admin-field select:focus,
.admin-field textarea:focus { outline: none; border-color: var(--admin-rose); }
.admin-field textarea { resize: vertical; min-height: 84px; }
.admin-field input:disabled,
.admin-field select:disabled,
.admin-field textarea:disabled { background: var(--admin-border); color: var(--admin-muted); cursor: not-allowed; }
.admin-field-hint { margin: 6px 0 0; font-size: 12px; color: var(--admin-muted); }
.admin-field-hint-warning { color: var(--admin-rose-dark); }
.admin-field-hint-success { color: var(--admin-green); font-weight: 600; }

.admin-field-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.admin-field-toggle label { margin-bottom: 0; }

.admin-switch { position: relative; display: inline-block; width: 46px; height: 27px; flex: none; }
.admin-switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.admin-switch-track {
  position: absolute;
  inset: 0;
  background: var(--admin-border);
  border-radius: 100px;
  transition: background 0.2s ease;
  pointer-events: none;
}
.admin-switch-track::after {
  content: "";
  position: absolute;
  top: 3px; left: 3px;
  width: 21px; height: 21px;
  background: var(--admin-white);
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: transform 0.2s ease;
}
.admin-switch input:checked + .admin-switch-track { background: var(--admin-green); }
.admin-switch input:checked + .admin-switch-track::after { transform: translateX(19px); }
.admin-switch input:focus-visible + .admin-switch-track { outline: 2px solid var(--admin-rose); outline-offset: 2px; }

/* Stage 13, Section 11: SECONDARY tier -- explicit white surface + subtle
   border (was transparent, relying entirely on whatever sat behind it). */
.admin-btn-outline-neutral { background: var(--admin-white); border-color: var(--admin-border); color: var(--admin-text); }
.admin-btn-outline-neutral:hover:not(:disabled) { background: var(--admin-bg); }

.admin-modal-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 22px;
}
.admin-modal-actions .admin-btn { width: 100%; }
/* Stage 11E: #productModalActions is empty (no visible footer/border) for
   every mode except the read-only product View -- see admin.html/
   admin-products.js. */
#productModalActions:empty { display: none; }

.admin-edit-thumb {
  width: 100%;
  max-height: 160px;
  object-fit: cover;
  border-radius: var(--admin-radius-sm);
  background: var(--admin-bg);
  border: 1px solid var(--admin-border);
}

.admin-toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  background: var(--admin-green-dark);
  color: var(--admin-white);
  padding: 14px 22px;
  border-radius: 100px;
  box-shadow: var(--admin-shadow);
  font-size: 14px;
  font-weight: 600;
  z-index: 100;
  max-width: calc(100vw - 40px);
  text-align: center;
}

@media (min-width: 480px) {
  .admin-modal-actions { flex-direction: row; justify-content: flex-end; }
  .admin-modal-actions .admin-btn { width: auto; min-width: 140px; }
}

/* ==================== Image picker (Add/Edit Product) ==================== */

.admin-image-picker { display: flex; flex-direction: column; gap: 10px; }
.admin-image-preview {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--admin-radius-sm);
  background: var(--admin-bg);
  border: 1.5px dashed var(--admin-border);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  color: var(--admin-muted);
  font-size: 13px;
  text-align: center;
  padding: 12px;
}
.admin-image-preview img { width: 100%; height: 100%; object-fit: cover; }
.admin-image-picker-btn { cursor: pointer; text-align: center; }
.admin-image-picker input[type="file"] { display: none; }

.admin-image-status {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 6px;
  padding: 10px 12px;
  border-radius: var(--admin-radius-sm);
  background: var(--admin-bg);
}
.admin-image-status .admin-field-hint { margin: 0; }

/* ---------- Multi-image manager (Edit Product) ---------- */
.admin-image-manager-grid {
  display: grid;
  /* Stage 14 (checkpoint): minmax(0, 1fr) instead of a bare 1fr -- a plain
     `1fr` track has an implicit content-based minimum size, so a card
     whose content (action-button row, badge/note text) needs more room
     than an even 1/N split can silently force this grid, and everything
     containing it (the modal, the page), wider than intended -- one of
     the real contributors to the reported horizontal-scroll bug. minmax
     (0, 1fr) removes that implicit minimum so cards actually shrink to
     fit instead of overflowing. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
.admin-image-card {
  background: var(--admin-bg);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  overflow: hidden;
}
.admin-image-card-media { position: relative; width: 100%; aspect-ratio: 1 / 1; background: var(--admin-border); }
.admin-image-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.admin-image-card-badge { position: absolute; top: 8px; left: 8px; }
.admin-image-card-actions { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px; }
/* Stage 11E-G: this reuses .admin-badge/.admin-badge-outofstock for its
   amber color only -- that base class is an inline-flex, fully-rounded
   PILL sized for a short word ("Out of Stock"), not a full sentence, which
   is why the instructional text overflowed/misaligned instead of wrapping
   inside the card. Overriding just the shape (display/radius/padding/
   width/white-space) here, after those rules in source order, so the
   color is kept and only the layout changes. */
.admin-image-card-pending-note {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border-radius: var(--admin-radius-sm);
  padding: 8px 10px;
  margin-bottom: 2px;
  font-size: 11.5px;
  line-height: 1.4;
  white-space: normal;
  text-align: left;
}
.admin-image-card-actions .admin-btn { flex: 1 1 auto; min-width: 0; padding: 8px 10px; font-size: 12px; }
.admin-image-card-actions label.admin-btn { display: flex; align-items: center; justify-content: center; margin: 0; }

.admin-btn-danger-outline {
  background: transparent;
  border-color: var(--admin-rose-dark);
  color: var(--admin-rose-dark);
}
.admin-btn-danger-outline:hover:not(:disabled) { background: var(--admin-error-bg); }
.admin-btn-danger { background: var(--admin-rose-dark); color: var(--admin-white); border-color: var(--admin-rose-dark); }
.admin-btn-danger:hover:not(:disabled) { background: #8a1f3c; }

.admin-image-manager-footer { margin-top: 12px; }
.admin-image-manager-footer .admin-btn { display: inline-flex; }

@media (min-width: 560px) {
  .admin-image-manager-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- Destructive-action confirmation dialog ---------- */
.admin-confirm-overlay {
  position: fixed;
  inset: 0;
  z-index: 70; /* above .admin-modal-overlay (50) — this confirms an action started inside that modal */
  background: rgba(38, 20, 20, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.admin-confirm-box {
  background: var(--admin-white);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
  max-width: 380px;
  width: 100%;
  padding: 22px 20px;
  border-top: 4px solid var(--admin-rose-dark);
}
.admin-confirm-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: 18px;
  color: var(--admin-rose-dark);
  margin: 0 0 10px;
}
.admin-confirm-message { color: var(--admin-muted); font-size: 14px; line-height: 1.5; margin: 0 0 20px; }
.admin-confirm-actions { display: flex; gap: 10px; }
.admin-confirm-actions .admin-btn { flex: 1; }

/* ==================== Recent Activity ==================== */

.admin-activity-panel {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
  padding: 18px 16px;
  margin-top: 22px;
}
.admin-activity-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.admin-activity-head h2 {
  font-size: 18px;
  margin: 0;
  color: var(--admin-ink);
}
.admin-activity-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.admin-activity-item {
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 14px;
  background: var(--admin-bg);
}
.admin-activity-item-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.admin-activity-actor { font-weight: 700; color: var(--admin-ink); }
.admin-activity-time { font-size: 12px; color: var(--admin-muted); white-space: nowrap; }
.admin-activity-summary { font-size: 13.5px; color: var(--admin-ink); margin: 4px 0 0; }
.admin-activity-summary strong { font-weight: 600; }
.admin-activity-diff {
  margin-top: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--admin-rose-dark);
}
.admin-activity-fields { font-size: 12.5px; color: var(--admin-muted); margin-top: 4px; }
.admin-activity-viewbtn { margin-top: 8px; }

/* ---------- Change Details / product history (reuses .admin-confirm-box) ---------- */
.admin-activity-detail-box {
  max-width: 480px;
  max-height: 84vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  position: relative;
  border-top-color: var(--admin-green);
  text-align: left;
}
.admin-activity-detail-title { color: var(--admin-green); padding-right: 34px; }
.admin-activity-detail-meta { color: var(--admin-muted); font-size: 13px; margin: 0 0 14px; }
.admin-activity-change-row { padding: 10px 0; border-top: 1px solid var(--admin-border); }
.admin-activity-change-row:first-of-type { border-top: none; }
.admin-activity-change-label { font-weight: 700; font-size: 13.5px; color: var(--admin-ink); margin-bottom: 4px; }
.admin-activity-change-values { display: flex; flex-direction: column; gap: 2px; font-size: 13.5px; }
.admin-activity-change-values .before { color: var(--admin-muted); }
.admin-activity-change-values .before::before { content: "Before: "; font-weight: 600; }
.admin-activity-change-values .after { color: var(--admin-green); }
.admin-activity-change-values .after::before { content: "After: "; font-weight: 600; }

.admin-history-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.admin-history-item { border-left: 3px solid var(--admin-green); padding: 4px 0 4px 12px; }
.admin-history-time { font-size: 12px; color: var(--admin-muted); }
.admin-history-actor { font-weight: 700; font-size: 13.5px; margin: 2px 0; }
.admin-history-summary { font-size: 13.5px; color: var(--admin-ink); }
.admin-history-diff { font-size: 13px; font-weight: 600; color: var(--admin-rose-dark); }

/* ---------- Image zoom/position/aspect/fit-fill adjustment (Stage 16) ----------
   .admin-image-adjust-box reuses .admin-activity-detail-box's sizing
   (max-width 480px, centered) -- plenty of room for a compact stage, not
   a full editor. The stage's on-screen box size/shape is set from JS
   (openImageAdjustDialog in js/admin-products.js, per the chosen aspect
   ratio) via an inline width + aspect-ratio, NOT a fixed height, so
   max-width:100% below still lets it shrink to fit a narrow phone screen
   without distorting; the canvas's internal drawing-buffer resolution is
   independent of this and stays crisp regardless. This file only styles
   the chrome. */
.admin-image-adjust-box { max-width: 420px; }
.admin-image-adjust-stage {
  max-width: 100%;
  margin: 12px auto;
  border-radius: var(--admin-radius-sm);
  overflow: hidden;
  background: #f7f5f3;
  border: 1px solid var(--admin-border);
  touch-action: none; /* dragging is handled entirely via pointer events -- prevent the browser's own scroll/zoom gestures from fighting it */
  cursor: grab;
}
.admin-image-adjust-stage:active { cursor: grabbing; }
.admin-image-adjust-stage canvas { display: block; width: 100%; height: 100%; }
.admin-image-adjust-toolbar {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}
.admin-image-adjust-group { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.admin-image-adjust-group .admin-btn {
  width: auto;
  padding: 7px 12px;
  font-size: 12.5px;
  min-height: 34px;
}
/* Stage 16: shared "currently selected" treatment for the aspect-ratio /
   fit-fill toggle groups -- same green-accent language
   .admin-summary-card-attention and drill-down links already use
   elsewhere, not a new color. */
.admin-image-adjust-group .admin-btn.is-active {
  background: var(--admin-green-bg);
  border-color: var(--admin-green);
  color: var(--admin-green-dark);
}
.admin-image-adjust-zoomrow {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 16px;
}
.admin-image-adjust-zoomrow .admin-btn { width: auto; min-width: 44px; }

/* ---------- Standard/AI processing choice + AI cost-warning dialogs ---------- */
.admin-processing-choice { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 18px; }
.admin-processing-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 12px 14px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.admin-processing-option:has(input:checked) { border-color: var(--admin-green); background: #f2f8f4; }
.admin-processing-option input[type="radio"] { margin-top: 3px; flex: none; }
.admin-processing-option-body { display: flex; flex-direction: column; gap: 2px; }
.admin-processing-option-body strong { font-size: 14px; color: var(--admin-ink); }
.admin-processing-option-body span { font-size: 12.5px; color: var(--admin-muted); }
.admin-ai-warning-checkbox {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13.5px;
  color: var(--admin-ink);
  margin: 0 0 18px;
  cursor: pointer;
}
.admin-ai-warning-checkbox input { margin-top: 3px; flex: none; }

/* ---------- AI result preview/approval dialog ---------- */
.admin-ai-preview-box { max-width: 720px; text-align: left; }
.admin-ai-preview-grid {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 14px 0 18px;
}
.admin-ai-preview-pane { flex: 1; min-width: 0; }
.admin-ai-preview-label {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--admin-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin: 0 0 6px;
}
.admin-ai-preview-media {
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  overflow: hidden;
  background: #f7f5f3;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 160px;
}
.admin-ai-preview-media img { max-width: 100%; max-height: 320px; display: block; object-fit: contain; }
.admin-ai-preview-actions { flex-wrap: wrap; }
.admin-ai-preview-actions .admin-btn { flex: 1 1 auto; min-width: 130px; }

@media (min-width: 640px) {
  .admin-ai-preview-grid { flex-direction: row; }
}

@media (min-width: 640px) {
  .admin-activity-detail-box { padding: 26px 24px; }
}

/* ==================== Stage 12 (Phase 2): AI multi-background picker ====================
   Grid of selectable preview cards -- Original + up to 3 generated
   backgrounds. Mobile-first: single column by default (comfortable large
   tap targets), 2-column from a small tablet width up. */
.admin-ai-multipreview-box { max-width: 920px; text-align: left; }
/* Stage 13: subtle, not an advertisement -- small icon + number, same
   weight/size as the surrounding hint text, no banner treatment. */
.admin-ai-credits-line { margin: -6px 0 4px; font-weight: 600; font-size: 13px; color: var(--admin-muted); }
.admin-ai-multipreview-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin: 14px 0 18px;
}
@media (min-width: 480px) {
  .admin-ai-multipreview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 760px) {
  .admin-ai-multipreview-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.admin-ai-multipreview-card {
  border: 2px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  overflow: hidden;
  background: var(--admin-white);
  cursor: pointer;
  padding: 0;
  text-align: left;
  font: inherit;
  display: block;
  width: 100%;
}
.admin-ai-multipreview-card:hover { border-color: var(--admin-gold); }
.admin-ai-multipreview-card.is-selected { border-color: var(--admin-rose); box-shadow: 0 0 0 3px rgba(200, 53, 93, 0.15); }
.admin-ai-multipreview-media { aspect-ratio: 1 / 1; background: #f7f5f3; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.admin-ai-multipreview-media img { width: 100%; height: 100%; object-fit: contain; }
.admin-ai-multipreview-media.is-loading { color: var(--admin-muted); font-size: 12.5px; }
.admin-ai-multipreview-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--admin-ink);
}
.admin-ai-multipreview-check {
  width: 18px; height: 18px;
  border-radius: 100px;
  border: 2px solid var(--admin-border);
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--admin-white);
  font-size: 12px;
  line-height: 1;
}
.is-selected .admin-ai-multipreview-check { border-color: var(--admin-rose); background: var(--admin-rose); }
.is-selected .admin-ai-multipreview-check::after { content: "✓"; }

/* ==================== Category management ==================== */

.admin-category-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--admin-border);
}
.admin-category-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 14px;
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
}
.admin-category-row-main { flex: 1 1 140px; min-width: 0; }
.admin-category-row-name { font-weight: 600; font-size: 14px; overflow-wrap: break-word; }
.admin-category-row-meta { font-size: 12px; color: var(--admin-muted); }
.admin-category-row-actions { display: flex; align-items: center; gap: 12px; flex: none; }

/* ==================== Stage 3C: Enquiry management ====================
   Mobile card layout reuses the existing .admin-cards grid container
   (same one Product Management uses) with a new, simpler text-only card
   — enquiries have no image/price to anchor a .admin-pcard-style layout
   around. Desktop table reuses .admin-table/.admin-table-wrap as-is (see
   the shared min-width:960px rule earlier in this file) — no new table
   styling needed at all. */
.admin-enquiry-card {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius);
  box-shadow: var(--admin-shadow);
  padding: 14px 16px;
}
.admin-enquiry-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.admin-enquiry-card-name { font-weight: 700; font-size: 15px; color: var(--admin-ink); overflow-wrap: break-word; }
.admin-enquiry-card-meta { font-size: 12.5px; color: var(--admin-muted); margin: 0 0 8px; }
.admin-enquiry-card-req { font-size: 13.5px; color: var(--admin-ink); margin: 0 0 10px; }
.admin-enquiry-card-foot { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; color: var(--admin-muted); margin-bottom: 12px; flex-wrap: wrap; }

/* Stage 11E-D: order card total, shown in the card header next to the
   order number -- was previously buried mid-card next to the payment
   badge, easy to misread as part of the status line. */
.admin-order-status-total { font-weight: 700; font-size: 14.5px; color: var(--admin-green-dark); white-space: nowrap; }
/* Payment vs. Order (fulfillment) status are deliberately separate
   concepts (see js/admin-orders.js's buildLabeledBadgeHtml comment) --
   these two labeled groups make that explicit instead of showing two
   unlabeled badges back to back. */
.admin-order-status-row { display: flex; gap: 18px; flex-wrap: wrap; margin: 0 0 10px; }
.admin-order-status-item { display: flex; flex-direction: column; gap: 3px; }
.admin-order-status-label { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--admin-muted); }

.admin-enquiry-contact-actions { margin-bottom: 4px; }

/* Deliberately visually distinct (different background + a lock icon in
   the label, see js/admin-enquiries.js) from the rest of the detail
   modal, so it reads unambiguously as "admin-only, not customer-facing" —
   the one explicit requirement for this section. */
.admin-enquiry-internal {
  background: var(--admin-bg);
  border: 1px dashed var(--admin-border);
  border-radius: var(--admin-radius-sm);
  padding: 14px 16px 2px;
  margin: 18px 0;
}
.admin-enquiry-internal-label { font-size: 12.5px; font-weight: 600; color: var(--admin-muted); margin: 0 0 10px; }

/* ==================== Stage 3E: Enquiry field settings ====================
   Lives inside a modal (max-width ~560px on desktop, full-width on
   mobile), so it deliberately does NOT reuse .admin-table — that class is
   `display:none` below the 960px breakpoint and has a 900px min-width
   once shown, neither of which fits inside a modal. This is a small,
   always-visible table with its own scoped styles, wrapped in a
   horizontal-scroll container as a safety net on very narrow phones. */
.admin-fs-table-wrap { overflow-x: auto; margin-bottom: 18px; }
.admin-fs-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.admin-fs-table th,
.admin-fs-table td { padding: 10px 8px; text-align: left; border-bottom: 1px solid var(--admin-border); white-space: nowrap; }
.admin-fs-table th:not(:first-child),
.admin-fs-table td:not(:first-child) { text-align: center; }
.admin-fs-table td:first-child { white-space: normal; }
.admin-fs-table tbody tr:last-child td { border-bottom: none; }
.admin-fs-table input[type="checkbox"] { width: 17px; height: 17px; cursor: pointer; }
.admin-fs-table input[type="checkbox"]:disabled { cursor: not-allowed; opacity: 0.45; }

/* ==================== Stage 4C: Order items editor ====================
   Reuses .admin-fs-table (Stage 3E) for the items table itself — same
   "lives inside a modal, needs to stay narrow and always-visible"
   reasoning applies here too, so no separate table style was created.
   Only the two order-item-specific pieces below are new: the small
   number inputs inside table cells (quantity/unit price, NEW orders
   only) and the "locked" banner shown instead of edit controls once an
   order is CONFIRMED or later (see the Stage 4C item-editing-protection
   rule in js/admin-orders.js). */
.admin-fs-table input[type="number"] {
  width: 100%;
  padding: 6px 8px;
  border: 1.5px solid var(--admin-border);
  border-radius: 8px;
  font: inherit;
}
.admin-oi-locked {
  background: var(--admin-bg);
  border: 1px dashed var(--admin-border);
  color: var(--admin-muted);
  font-size: 13px;
  padding: 10px 14px;
  border-radius: var(--admin-radius-sm);
  margin-bottom: 12px;
}
/* Stage 4D reuses .admin-oi-add-row/.admin-fs-table/.admin-conv-* below
   for the enquiry-conversion product picker in js/admin-orders.js's
   openConversionModal() — same "small always-visible table inside a
   modal" reasoning, just with an extra category-filter <select> ahead of
   the product one; flex-wrap already handles that gracefully on narrow
   screens without any new layout rules. */
.admin-oi-add-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 4px;
}
.admin-oi-add-row select {
  flex: 1 1 220px;
  min-width: 0;
  padding: 9px 10px;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  font: inherit;
}
.admin-oi-add-row input[type="number"] {
  flex: 0 0 70px;
  padding: 9px 10px;
  border: 1.5px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  font: inherit;
}

/* ==================== Stage 5: Admin Dashboard ====================
   Reuses .admin-summary-grid/.admin-summary-card (KPI numbers),
   .admin-state (loading/error), .admin-history-list (Recent Activity) as-
   is -- nothing here duplicates them. Everything below is new because the
   Dashboard is the first Admin page with trend/breakdown charts; per the
   Stage 5 design decision, these are plain CSS/SVG (no charting library
   exists anywhere in this project, and adding one would be a new external
   dependency contrary to its no-bundler convention). */

/* Stage 19: page-head's controls cluster is now a compact right-aligned
   group (date-range pill + Refresh) sitting inline with the title/subtitle
   row, rather than a separate boxed bar below it -- see admin-dashboard.html's
   merged .admin-page-head. */
.admin-dash-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.admin-dash-range { position: relative; display: flex; align-items: center; }
.admin-dash-range-icon { position: absolute; left: 12px; width: 15px; height: 15px; color: var(--admin-muted); pointer-events: none; }
.admin-dash-range select {
  border: 1.5px solid var(--admin-border);
  border-radius: 100px;
  padding: 10px 16px 10px 34px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  background: var(--admin-white);
  color: var(--admin-ink);
  min-height: 40px;
  cursor: pointer;
}
.admin-dash-refreshbtn { padding: 9px 16px; min-height: 40px; }
.admin-dash-refreshbtn svg { width: 15px; height: 15px; flex: none; }
.admin-dash-lastrefreshed { display: block; font-size: 12px; color: var(--admin-muted); margin: 0 0 18px; }
@media (min-width: 640px) {
  .admin-dash-lastrefreshed { text-align: right; }
}

/* ====================================================================
   Stage 18B — Dashboard presentation redesign.

   Stage 18A raised .admin-dashboard's max-width (1280px -> 1760px) to
   fix the outer container being clamped narrow. That CSS math still
   checks out under a fresh re-trace (see that rule's own comment) and
   nothing else in the body -> admin-shell -> admin-main -> admin-
   dashboard -> admin-content-area chain constrains width -- confirmed
   by grepping every width/max-width rule in this file. Given the
   reported screenshot still looked unchanged, a stale cached copy of
   this file is a real possibility (the <link> tag had no cache-busting
   query string until this stage -- see admin-dashboard.html etc., now
   `?v=18b`). That said, the complaint is bigger than container width
   alone: KPI cards/typography/chart height were still modest even
   within a correctly-wide container. This block is the substantive
   redesign for that -- generous card sizing, a dashboard-scoped
   typography scale, and a deliberate (not accidental) desktop grid,
   replacing Stage 18A's ad hoc 640/900/1200/1440 breakpoints with the
   explicit 768/1100/1400 scheme requested. All scoped to dashboard-
   specific classes/selectors (.admin-summary-grid-exec,
   .admin-summary-grid-ops, .admin-dash-*, and
   body[data-admin-page="dashboard"]) so Products/Orders/Enquiries pages,
   which reuse some of the same base classes, are unaffected. */

/* Dashboard page title -- scoped to this one page (not Products/Orders/
   Enquiries' own h1, which share .admin-page-head h1's base rule). */
body[data-admin-page="dashboard"] .admin-page-head h1 { font-size: 28px; }
@media (min-width: 768px) {
  body[data-admin-page="dashboard"] .admin-page-head h1 { font-size: 32px; }
}
@media (min-width: 1400px) {
  body[data-admin-page="dashboard"] .admin-page-head h1 { font-size: 34px; }
}

/* Stage 20: the reference dashboard has no separate green app bar --
   its brand/profile live in the (white) sidebar and page header instead.
   Hidden at 900px+ ONLY for this page, where the sidebar already shows
   the brand and .admin-dash-profile (below) now carries the profile
   control that used to live in this bar -- scoped by body[data-admin-page]
   so every other Admin page's topbar (brand + profile, its ONLY way to
   reach Settings/Activity/Log Out on those pages) is completely
   unaffected. Left visible below 900px (no sidebar yet) so the brand
   identity doesn't disappear on mobile. */
@media (min-width: 900px) {
  body[data-admin-page="dashboard"] .admin-topbar { display: none; }
}

/* Stage 20: profile control relocated here from the green topbar (see
   admin-dashboard.html's header comment) -- same #profileMenuBtn/
   #profileMenu ids/JS, just restyled for a white-card context instead of
   the dark topbar it used to sit in. */
.admin-dash-profile { position: relative; }
.admin-dash-profile .admin-profile-btn {
  background: var(--admin-green-bg);
  color: var(--admin-green-dark);
}
.admin-dash-profile .admin-profile-btn:hover { background: var(--admin-green); color: var(--admin-white); }
.admin-dash-profile .admin-profile-menu { right: 0; top: calc(100% + 8px); }

.admin-dash-rowtitle {
  font-size: 17px;
  font-weight: 700;
  color: var(--admin-green-dark);
  margin: 22px 0 12px;
}
.admin-dash-rowtitle:first-of-type { margin-top: 0; }
@media (min-width: 1100px) {
  .admin-dash-rowtitle { font-size: 19px; margin: 26px 0 14px; }
}

/* Business Overview's 6-card headline KPI strip. Scoped by class (not
   the shared .admin-summary-grid rule, which Products/other pages also
   use with different card counts) so this doesn't affect anything else.
   768/1100/1400 matches the explicitly requested breakpoint scheme:
   2 cols (mobile, inherited base) -> 3 cols at 768px -> stays 3 at
   1100px (six cards at that width would be ~150px each, too tight to
   read comfortably) -> 6 cols only at the "premium wide" 1400px
   threshold, where each card gets real room. */
@media (min-width: 768px) {
  .admin-summary-grid-exec { grid-template-columns: repeat(3, 1fr); gap: 16px; }
}
@media (min-width: 1400px) {
  .admin-summary-grid-exec { grid-template-columns: repeat(6, 1fr); gap: 18px; }
}
/* Stage 18C: reduced from 150/172px -- the outer dashboard width (Stage
   18A/B) is confirmed correct now; this pass targets vertical density
   only. 135-155px per the refined guidance, content still centered
   (a 3-line label/value/subtext block genuinely looks intentional
   centered at THIS height -- the "huge empty space" complaint was about
   the much taller analytics cards below, not these). */
.admin-summary-grid-exec .admin-summary-card {
  padding: 14px 16px;
  min-height: 142px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
.admin-summary-grid-exec .admin-summary-label { font-size: 11.5px; }
.admin-summary-grid-exec .admin-summary-value { font-size: 24px; }
.admin-summary-grid-exec .admin-summary-icon { width: 30px; height: 30px; margin-bottom: 8px; }
.admin-summary-grid-exec .admin-summary-icon svg { width: 15px; height: 15px; }
@media (min-width: 1100px) {
  .admin-summary-grid-exec .admin-summary-card { padding: 16px 18px; min-height: 156px; }
  .admin-summary-grid-exec .admin-summary-value { font-size: 28px; }
  .admin-summary-grid-exec .admin-dash-kpi-sub { font-size: 12.5px; }
}

/* Stage 20: .admin-summary-grid-ops (Attention Required / Order Pipeline)
   removed -- both rows were dropped from the dashboard (see
   admin-dashboard.html's own comment at that spot for why); this class
   is no longer referenced by any markup. */

/* Two-column analytics row (Sales & Order Analytics) -- each column is a
   plain .admin-dash-card that the relevant render function fills directly
   (including its own heading). Breakpoint lowered from Stage 18A's 900px
   to the requested 768px -- at 768-899px there's no fixed sidebar yet
   (bottom nav instead), so the full viewport width is available and two
   columns are comfortable. 65/35 split: the trend is the single largest
   component on the page vs. the status donut. */
.admin-dash-row-2col { display: grid; grid-template-columns: 1fr; gap: 16px; margin-bottom: 8px; }
@media (min-width: 768px) {
  .admin-dash-row-2col { gap: 20px; }
  .admin-dash-row-60-40 { grid-template-columns: 65fr 35fr; }
}
@media (min-width: 1100px) {
  .admin-dash-row-2col { gap: 24px; margin-bottom: 12px; }
}

/* Stage 19: Conversion & Pipeline Health -- 3 equal columns (Conversion
   funnel / Pending Ageing / Sales by Category), matching the reference
   dashboard's 3-up grouping. Stacks on mobile/tablet like every other
   analytics row here; only becomes a real row at 1100px, where 3 cards
   plus the fixed sidebar still have comfortable room each. */
.admin-dash-row-3col { display: grid; grid-template-columns: 1fr; gap: 16px; margin-bottom: 8px; }
@media (min-width: 768px) {
  .admin-dash-row-3col { grid-template-columns: 1fr 1fr; gap: 20px; }
}
@media (min-width: 1100px) {
  .admin-dash-row-3col { grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 12px; }
}

/* Stage 19: Operational Summary -- Top Products / Top Categories /
   At-Risk Orders / Latest Orders, matching the reference's 4-card row.
   JS (renderOpSummary) writes all four .admin-dash-card children directly
   into this one grid container, same pattern .admin-dash-charts-row used
   for Business Performance before it. */
/* Stage 21: align-items:start (CSS Grid defaults to `stretch`, which was
   forcing every card in a row to match the tallest -- e.g. Top Categories
   with 3 rows stretching to Latest Orders' height) so each card sizes to
   its own content instead. Min-heights reduced accordingly -- a small
   floor for visual rhythm, not a forced equal height. */
.admin-dash-opsummary-grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-bottom: 8px; align-items: start; }
@media (min-width: 768px) {
  .admin-dash-opsummary-grid { grid-template-columns: 1fr 1fr; gap: 16px; }
}
@media (min-width: 1400px) {
  .admin-dash-opsummary-grid { grid-template-columns: repeat(4, 1fr); gap: 20px; }
}
.admin-dash-opsummary-grid .admin-dash-card { min-height: 0; }
.admin-dash-opsummary-grid .admin-dash-card.is-compact { min-height: 0; }
/* Stage 22: .admin-dash-card-payment (the old standalone Payment Status
   row's width cap) removed -- that row is gone; Payment Status now
   shares #dashStatusCard inside the Sales & Order Analytics row (see
   admin-dashboard.html's own comment there), sized like every other card
   in that row rather than needing its own cap. */

/* Stage 18C: card sizing/visual-weight differentiation -- deliberately
   NOT uniform, but substantially reduced from Stage 18B's 280-360px
   range, which left charts/short content floating in mostly-empty
   cards. #dashTrendCard (Sales & Orders Trend) stays the single largest
   component; #dashStatusCard now matches its height ("should ... have
   approximately the same visual height" per the refined guidance,
   rather than being smaller). Conversion/Ageing/At-Risk/Latest Orders
   are mid-weight secondary cards. NOTE: padding for .admin-dash-card
   itself is set where that class is canonically defined further down
   this file (search for the main .admin-dash-card rule block).

   .is-compact (added/removed by js/admin-dashboard.js's render*
   functions depending on whether there's real data to show -- see
   renderConversionCard/renderPendingAgeing/renderAtRisk/
   renderLatestOrders/renderMainAnalytics) drops the min-height back to
   near-zero for a genuinely empty state, so "no data" never means "a
   large card around one sentence" (the explicit empty-state rule). */
#dashTrendCard, #dashStatusCard, #dashConversionCard, #dashAgeingCard, #dashCategoryCard {
  min-height: 160px;
}
@media (min-width: 1100px) {
  #dashTrendCard, #dashStatusCard { min-height: 240px; }
  #dashConversionCard, #dashAgeingCard, #dashCategoryCard { min-height: 170px; }
  #dashOpSummary .admin-dash-card { min-height: 0; }
}
@media (min-width: 1400px) {
  #dashTrendCard, #dashStatusCard { min-height: 260px; }
  #dashConversionCard, #dashAgeingCard, #dashCategoryCard { min-height: 190px; }
  #dashOpSummary .admin-dash-card { min-height: 0; }
}
/* Stage 18C: a bare ID selector (specificity 1,0,0,0) beats a bare class
   selector (0,0,2,0) regardless of source order -- so a plain
   `.admin-dash-card.is-compact { min-height: 0 }` would be silently
   overridden by #dashConversionCard/etc.'s own min-height rules above and
   never actually take effect for exactly the cards this is aimed at.
   Repeating the ID selectors here (specificity 1,0,1,0) correctly wins
   over the bare-ID rules. */
.admin-dash-card.is-compact,
#dashTrendCard.is-compact, #dashStatusCard.is-compact,
#dashConversionCard.is-compact, #dashAgeingCard.is-compact, #dashCategoryCard.is-compact {
  min-height: 0;
}

/* Stage 15: lightweight section-level loading placeholders. Reuses each
   section's own real card shape (.admin-summary-card / .admin-dash-card)
   so nothing changes size once real content replaces it -- only a slow,
   restrained opacity pulse is added (no shimmer sweep, no skeleton
   framework), matching "simple CSS-based placeholders" and avoiding
   excessive animation. See js/admin-dashboard.js's renderSkeletons(). */
@keyframes admin-skeleton-pulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}
.admin-skeleton-line {
  display: block;
  border-radius: 6px;
  background: var(--admin-border);
  animation: admin-skeleton-pulse 1.4s ease-in-out infinite;
}
.admin-skeleton-line-label { width: 55%; height: 9px; }
.admin-skeleton-line-value { width: 70%; height: 20px; margin-top: 10px; }
.admin-skeleton-chart { min-height: 120px; display: flex; align-items: center; justify-content: center; }
.admin-skeleton-chart .admin-skeleton-line { width: 50%; height: 12px; }
/* Stage 18A: for chart-shaped cards that are ALREADY .admin-dash-card in
   the static HTML (Sales/Order Analytics, Conversion, Ageing, At-Risk,
   Latest Orders) -- a single placeholder line sized/positioned to
   roughly suggest the eventual chart area, without a second nested
   .admin-skeleton-chart wrapper div. */
.admin-skeleton-line-chart { width: 60%; height: 14px; margin: 40px auto; }

/* Stage 14: Attention Required empty state -- calm, not decorative. */
.admin-dash-caughtup {
  background: var(--admin-green-bg);
  color: var(--admin-green-dark);
  border-radius: var(--admin-radius-sm);
  padding: 14px 16px;
  font-weight: 600;
  font-size: 13.5px;
  margin: 0 0 14px;
}
/* Stage 15: compact back-navigation control shown on Orders/Enquiries
   only when arriving via a Dashboard KPI drill-down link
   (?from=dashboard) -- see applyDrilldownContext() in js/admin-orders.js /
   js/admin-enquiries.js. Hidden by default (the [hidden] attribute), so a
   normal direct visit to either page is unaffected. Replaces the earlier
   full-width "Showing from Dashboard — ..." banner: a small, subtle,
   professional link (not a colored banner) that sits in normal document
   flow right below the page heading -- .admin-topbar isn't fixed/sticky,
   so this needs no special safe-area handling to clear it on mobile,
   just the same comfortable spacing every other element here already
   gets. */
.admin-backto-dashboard {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  margin: 0 0 16px;
  padding: 8px 14px;
  border-radius: var(--admin-radius-btn);
  border: 1.5px solid var(--admin-border);
  background: var(--admin-white);
  color: var(--admin-green-dark);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.admin-backto-dashboard:hover { border-color: var(--admin-green); background: var(--admin-green-bg); }
.admin-backto-dashboard:focus-visible { outline: 2px solid var(--admin-green); outline-offset: 2px; }

/* Stage 18B: the old auto-fill(minmax(150,200)) treatment for
   .admin-summary-grid-ops (Stage 17) is superseded by the explicit
   768/1100/1400 column rules for that same class further up this file
   -- removed from here rather than left as a conflicting duplicate
   later in the cascade. */

.admin-dash-card {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  box-shadow: var(--admin-shadow);
  padding: 16px 18px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow-x: auto; /* safety net: a chart never forces the PAGE to scroll horizontally */
}
/* Stage 18C: Stage 18B centered card content vertically (margin:auto on
   the last child) to avoid dead space under a short chart in a TALL
   card -- but that just relocated the empty space instead of removing
   it, and read as "content floating in a big blank card." Content now
   flows naturally from the top (flex-direction:column with no
   margin:auto) with the card's own min-height reduced enough (see
   #dashTrendCard etc. above) that balanced padding is enough on its
   own; genuinely-empty states use .is-compact (min-height:0, added by
   JS) instead of relying on this card being artificially tall. */
@media (min-width: 1100px) {
  .admin-dash-card { padding: 18px 20px; }
}
.admin-dash-card h3 {
  font-size: 15px;
  font-weight: 700;
  margin: 0 0 10px;
  color: var(--admin-ink);
}
.admin-dash-card .admin-muted-text { font-size: 13.5px; }

/* Stage 18A: small muted note appended to a card heading, e.g. "At-Risk
   Orders — Pending >48h" -- makes the working threshold visible right
   next to the label instead of implying an official SLA. */
.admin-dash-subheading-note {
  display: inline-block;
  margin-left: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--admin-muted);
  text-transform: none;
}

/* Stage 18A/18C: Enquiry -> Order Conversion card -- a large rate figure
   (same visual weight as a KPI value) plus its formula as context, above
   the existing barListHtml() funnel (Total Enquiries vs Converted).
   Margin tightened (16px -> 12px) as part of the 18C density pass. */
.admin-dash-conversion { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.admin-dash-conversion-rate { font-size: 32px; font-weight: 700; color: var(--admin-green-dark); line-height: 1.1; }

/* Stage 18C: deliberate compact empty state for the Conversion card when
   there are no enquiries in the period -- a muted, dashed outline
   standing in for the funnel shape rather than fabricating one, so the
   (now .is-compact, near-zero-min-height) card still reads as
   intentional instead of blank. */
.admin-dash-empty-pipeline {
  border: 1.5px dashed var(--admin-border);
  border-radius: var(--admin-radius-sm);
  color: var(--admin-muted);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  padding: 10px;
  margin-bottom: 8px;
}

/* Stage 19: 2-stage funnel visual for Enquiry -> Order Conversion --
   centered bars of decreasing width (proportional to the real value, 6%
   minimum so a non-zero stage is never visually invisible), not a
   clip-path trapezoid -- reads as a funnel shape without the cross-browser
   edge cases of true trapezoid clipping. Only ever renders the two stages
   this schema actually supports (see renderConversionCard's comment). */
.admin-dash-funnel { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.admin-dash-funnel-row { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.admin-dash-funnel-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  border-radius: 8px;
  background: var(--admin-green);
  color: var(--admin-white);
  font-weight: 700;
  font-size: 14px;
  transition: width 0.3s ease;
}
.admin-dash-funnel-bar-2 { background: var(--admin-gold); }
.admin-dash-funnel-label { font-size: 11.5px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: var(--admin-muted); }

/* Stage 22: "Sales & Orders Trend" -- back to TWO side-by-side mini-
   charts (Orders, Sales), each its own labeled column -- reverted from
   Stage 21's single overlaid chart per explicit direction. */
.admin-dash-dualtrend { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 560px) {
  .admin-dash-dualtrend { grid-template-columns: 1fr 1fr; }
}
.admin-dash-dualtrend-label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--admin-muted);
  margin-bottom: 6px;
}
/* Stage 18C: small legend swatches -- colors match .admin-dash-trend-line's
   stroke for each series (green = Orders, gold = Sales) so the label
   above each mini-chart reads as a real legend entry. */
.admin-dash-legend-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex: none; }
.admin-dash-legend-dot-order { background: var(--admin-green); }
.admin-dash-legend-dot-sales { background: var(--admin-gold); }

/* Stage 19: chart-card header row (title + the trend card's 7D/30D/90D
   pills) -- generic enough to reuse anywhere a card heading needs a
   right-aligned control cluster. */
.admin-dash-card-headrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.admin-dash-card-headrow h3 { margin: 0; }
.admin-dash-trend-pills { display: flex; gap: 4px; background: var(--admin-bg); border-radius: var(--admin-radius-btn); padding: 3px; }
.admin-dash-trend-pill {
  border: none;
  background: none;
  padding: 5px 10px;
  border-radius: 7px;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--admin-muted);
  cursor: pointer;
}
.admin-dash-trend-pill:hover { color: var(--admin-green-dark); }
.admin-dash-trend-pill.is-active { background: var(--admin-white); color: var(--admin-green-dark); box-shadow: var(--admin-shadow); }

.admin-dash-kpi-sub {
  display: block;
  font-size: 12px;
  color: var(--admin-muted);
  margin-top: 4px;
}

/* Stage 19: "vs previous period" comparison badge on the 6 headline KPI
   cards (see compareBadgeHtml() in js/admin-dashboard.js) -- direction is
   purely mechanical (up = green, down = red), matching the reference
   dashboard's own treatment; it does not attempt to judge whether a given
   metric going up or down is "good" (e.g. Cancelled Orders falling is
   shown the same red-down style as Pending Orders falling). */
.admin-dash-kpi-compare { display: flex; align-items: center; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.admin-dash-kpi-trend { font-size: 12.5px; font-weight: 700; }
.admin-dash-kpi-trend-up { color: var(--admin-green); }
.admin-dash-kpi-trend-down { color: var(--admin-rose-dark); }
.admin-dash-kpi-trend-period { font-size: 11px; color: var(--admin-muted); font-weight: 500; }

/* ---------- Horizontal bar list (Top Products / Top Categories) ---------- */
.admin-dash-barlist { display: flex; flex-direction: column; gap: 10px; }
.admin-dash-barrow { display: flex; flex-direction: column; gap: 4px; }
.admin-dash-barlabelrow { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; }
.admin-dash-barlabel { font-weight: 600; color: var(--admin-ink); overflow-wrap: break-word; min-width: 0; }
.admin-dash-barvalue { color: var(--admin-muted); white-space: nowrap; }
.admin-dash-bartrack {
  height: 8px;
  background: var(--admin-bg);
  border-radius: 100px;
  overflow: hidden;
}
.admin-dash-barfill { height: 100%; background: var(--admin-rose); border-radius: 100px; }
/* Stage 21/23: Pending Ageing uses its own risk-escalation colour scale
   instead of the rose used by Top Products/Top Categories -- rose stays
   the default everywhere else (unchanged). Scoped to #dashAgeingCard's
   own .is-risk-* classes (see ageingBarListHtml() in
   js/admin-dashboard.js) so Top Products/Top Categories, which never
   render these classes, are completely unaffected regardless of
   selector specificity. Background only -- the unfilled track
   (.admin-dash-bartrack) keeps its existing neutral --admin-bg colour. */
#dashAgeingCard .admin-dash-barfill.is-risk-healthy { background: var(--admin-green); }   /* 0-2 days */
#dashAgeingCard .admin-dash-barfill.is-risk-amber { background: var(--admin-gold); }      /* 3-7 days */
#dashAgeingCard .admin-dash-barfill.is-risk-orange { background: #b3541f; }               /* 8-15 days */
#dashAgeingCard .admin-dash-barfill.is-risk-red { background: #c0392b; }                  /* 16-30 days */
#dashAgeingCard .admin-dash-barfill.is-risk-critical { background: #7a1f1f; }              /* 30+ days */

/* ---------- Order list rows (Latest Orders / At-Risk Orders, Stage 17) ----------
   flex-wrap (not a fixed-width grid) so narrow phone screens reflow
   instead of overflowing horizontally -- consistent with this project's
   established "never force the page to scroll sideways" rule. */
.admin-dash-orderlist {
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  overflow: hidden;
}
.admin-dash-orderrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  padding: 10px 14px;
  border-top: 1px solid var(--admin-border);
  text-decoration: none;
  color: inherit;
  font-size: 13.5px;
  transition: background 0.15s ease;
}
.admin-dash-orderrow:first-child { border-top: none; }
.admin-dash-orderrow:hover { background: var(--admin-bg); }
.admin-dash-orderrow:focus-visible { outline: 2px solid var(--admin-green); outline-offset: -2px; }
.admin-dash-orderrow-num { font-weight: 700; color: var(--admin-green-dark); flex: none; }
.admin-dash-orderrow-customer { flex: 1 1 120px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.admin-dash-orderrow-total { font-weight: 600; white-space: nowrap; margin-left: auto; }

/* Stage 19: "View all at-risk orders →" / "View all orders →" -- links
   to the real, already-existing filtered Orders view (see
   atRiskCardHtml()/latestOrdersCardHtml() in js/admin-dashboard.js),
   matching the reference's explicit "View all" links at the bottom of
   each Operational Summary card. */
.admin-dash-viewall {
  display: block;
  margin-top: 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--admin-green-dark);
  text-decoration: none;
}
.admin-dash-viewall:hover { text-decoration: underline; }

/* ---------- Status/payment breakdown (donut + legend) ---------- */
.admin-dash-donutwrap { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.admin-dash-donut {
  width: 96px; height: 96px; border-radius: 50%; flex: none;
  background: conic-gradient(var(--admin-border) 0 100%); /* JS overrides via inline style */
}
.admin-dash-legend { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; flex: 1 1 180px; min-width: 0; }
.admin-dash-legend-item { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.admin-dash-legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.admin-dash-legend-label { flex: 1 1 auto; min-width: 0; overflow-wrap: break-word; white-space: normal; }
.admin-dash-legend-value { color: var(--admin-muted); white-space: nowrap; }
/* Stage 19: slim per-row percentage bar (real share of the donut's
   total), matching the reference's donut+bar legend treatment. */
.admin-dash-legend-bartrack {
  flex: 1 1 40px;
  min-width: 24px;
  height: 6px;
  background: var(--admin-bg);
  border-radius: 100px;
  overflow: hidden;
}
.admin-dash-legend-barfill { display: block; height: 100%; border-radius: 100px; }
.admin-dash-legend-pct { color: var(--admin-muted); }

/* ---------- Trend line (inline SVG, no library) ---------- */
.admin-dash-trend { width: 100%; height: auto; display: block; }
.admin-dash-trend-line { fill: none; stroke: var(--admin-green); stroke-width: 2; }
.admin-dash-trend-area { fill: var(--admin-green); opacity: 0.08; }
.admin-dash-trend-empty { text-align: center; color: var(--admin-muted); font-size: 13.5px; padding: 24px 0; }
/* Stage 18C: Sales gets the gold accent (matches .admin-dash-legend-dot-sales)
   so the two mini-charts in "Sales & Orders Trend" are visually
   distinguishable, not just labeled. */
.admin-dash-trend-sales .admin-dash-trend-line { stroke: var(--admin-gold); }
.admin-dash-trend-sales .admin-dash-trend-area { fill: var(--admin-gold); }
.admin-dash-trend-sales .admin-dash-trend-dot { fill: var(--admin-gold); }
/* Stage 18C: subtle gridlines + date-axis labels, only drawn when
   trendSvg()/trendSvgInteractive() are called with showLabels:true (the
   hero Sales & Orders Trend usage) -- gives the chart real structure
   instead of a bare floating line. */
.admin-dash-trend-grid { stroke: var(--admin-border); stroke-width: 1; }
.admin-dash-trend-axislabel { font-size: 8.5px; fill: var(--admin-muted); font-family: var(--ff-body); }

/* Stage 22: hoverable data points (trendSvgInteractive() only -- KPI
   sparklines use the plain, non-interactive trendSvg()). .admin-dash-trend-dot
   is the small always-visible marker; .admin-dash-trend-point is the
   larger invisible hit-circle carrying the tooltip data, cursor:pointer
   signals it's interactive. */
.admin-dash-trend-dot { fill: var(--admin-green); }
.admin-dash-trend-point { cursor: pointer; }
.admin-dash-trend-tooltip {
  position: fixed;
  z-index: 200;
  background: var(--admin-green-dark);
  color: var(--admin-white);
  padding: 7px 11px;
  border-radius: var(--admin-radius-sm);
  font-size: 12.5px;
  line-height: 1.4;
  pointer-events: none;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  max-width: 200px;
}
.admin-dash-trend-tooltip strong { display: block; font-size: 11.5px; font-weight: 600; opacity: 0.85; }

/* Stage 16: tiny inline sparkline embedded in a Business Overview KPI
   card (see kpiCardHtmlWithSpark()/sparkHtmlOrEmpty() in
   js/admin-dashboard.js) -- reuses the exact same .admin-dash-trend SVG
   markup/data as the full-size Trends section, just constrained to a
   small fixed height via CSS (the SVG's own viewBox handles the scale-
   down). Card values keep normal spacing above this. */
.admin-kpi-spark { display: block; width: 100%; height: 26px; margin-top: 6px; line-height: 0; }
.admin-kpi-spark .admin-dash-trend { height: 26px; }
.admin-kpi-spark .admin-dash-trend-line { stroke-width: 1.75; }

/* ---------- KPI value states (empty vs. error, per section 13) ---------- */
.admin-dash-kpi-empty { color: var(--admin-muted); font-size: 16px; }
.admin-dash-section-error { color: var(--admin-error-text); font-size: 13.5px; }

/* Stage 19: donut/ageing "Total ..." footer lines -- reuse .admin-dash-kpi-sub's
   text styling, just with a bit more separation from the chart above them. */
.admin-dash-donut-total, .admin-dash-ageing-total { margin-top: 10px; }

/* ==================== Stage 6: Site Status (ADMIN-09) ====================
   Reuses .admin-dash-card (chart-card shell), .admin-badge (+ existing
   .admin-badge-active/.admin-badge-outofstock tones -- green for LIVE,
   amber for MAINTENANCE, same "needs attention" semantic outofstock
   already carries elsewhere), .admin-confirm-overlay/.admin-confirm-box
   (Stage 6's confirmation dialog is the same shared family every other
   Admin confirm dialog already uses) -- nothing new visually introduced. */
.admin-dash-sitestatus { margin-bottom: 18px; }
.admin-dash-sitestatus-row {
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: flex-start;
}
.admin-dash-sitestatus-info { min-width: 0; }
.admin-dash-sitestatus-info .admin-badge { font-size: 13px; padding: 6px 14px; }
.admin-dash-sitestatus-info .admin-muted-text { margin: 8px 0 2px; }
#siteStatusToggleBtn { width: 100%; }
@media (min-width: 640px) {
  .admin-dash-sitestatus-row { flex-direction: row; align-items: center; justify-content: space-between; }
  #siteStatusToggleBtn { width: auto; flex: none; }
}

/* Stage 14: admin-settings.html -- simple link-rows for settings that are
   really just a shortcut to functionality that already lives on another
   page (Categories, Recent Activity), not a control in their own right. */
.admin-settings-list { display: flex; flex-direction: column; gap: 10px; margin: 0 0 22px; }
.admin-settings-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  background: var(--admin-white);
  border: 1px solid var(--admin-border);
  border-radius: var(--admin-radius-sm);
  box-shadow: var(--admin-shadow);
  padding: 14px 16px;
  text-decoration: none;
  color: var(--admin-text);
}
.admin-settings-row:hover { border-color: var(--admin-green); }
.admin-settings-row-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.admin-settings-row-text strong { font-size: 14px; }
.admin-settings-row-text small { font-size: 12.5px; color: var(--admin-muted); }
