/* Counter - partner dashboard styles. The tokens and the app-shell/
   topbar/sidebar/onboarding chrome below are copied to match Abracket's
   internal Loop app's own app.css exactly (same class names, same
   layout) per instruction - only the content-area component styles
   further down (site cards, tabs, embed box, inbox, API key page) are
   new, since Loop has no equivalent screens. */
:root{
  --surface-1:#fcfcfb;
  --page:#f9f9f7;
  --text-primary:#0b0b0b;
  --text-secondary:#52514e;
  --text-muted:#898781;
  --gridline:#e1e0d9;
  --border:rgba(11,11,11,0.10);
  --accent:#1bae70;
  --accent-wash:rgba(27,174,112,0.08);
  --accent-2:#06b6d4;
  --accent-2-wash:rgba(6,182,212,0.10);
  --accent-3:#fecda5;
  --accent-3-wash:rgba(254,205,165,0.30);
  --good:#1bae70;
  --good-text:#0c7a4f;
  --warning:#fab219;
  --serious:#ec835a;
  --critical:#d03b3b;
  --critical-wash:rgba(208,59,59,0.08);
  --warning-wash:rgba(250,178,25,0.12);
  --good-wash:rgba(27,174,112,0.08);
  --indigo:#6952d6;
  --indigo-wash:rgba(105,82,214,0.10);
}
*{box-sizing:border-box;}
html, body{height:100%;}
body{
  margin:0;
  background:var(--page);
  color:var(--text-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing:antialiased;
  font-size:14px;
}
a{color:var(--accent);}

/* ===== Onboarding (matches Loop's onboard-* / auth-tab* classes) ===== */
.onboard-screen{min-height:100vh;display:flex;align-items:center;justify-content:center;background:var(--page);padding:24px;}
.onboard-shell{width:100%;max-width:960px;display:flex;align-items:stretch;background:var(--surface-1);border:1px solid var(--border);border-radius:20px;overflow:hidden;box-shadow:0 1px 3px rgba(11,11,11,0.06);}
.onboard-showcase{flex:1 1 50%;background:#f4f9fe;display:flex;align-items:center;justify-content:center;padding:44px 40px;}
/* #f4f9fe matches niro-mascot.png's own baked-in background
   exactly (sampled from the source art) - this is a DIFFERENT
   crop than the earlier green-background attempt (that pose's
   lower-left arm overlapped marketing text in the source sheet
   with no clean rectangular crop available - Manish: "the big
   one is still getting cropped" - so this uses the isolated
   "Greeting" reference icon instead, which has no text nearby
   at any crop size). */
/* Flat, not a gradient - matches niro-mascot.png's own baked-in
   background exactly (#ebf8f1) so the image's rectangular edge is
   invisible against the panel instead of showing a seam. */
.onboard-showcase-inner{max-width:320px;}
.onboard-showcase-mascot{width:100%;max-width:220px;height:auto;display:block;margin:0 auto 24px;}
.onboard-showcase-tagline{font-size:19px;font-weight:700;line-height:1.35;color:var(--text-primary);margin-bottom:22px;}
.onboard-showcase-features{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px;}
.onboard-showcase-features li{position:relative;padding-left:25px;font-size:13.5px;color:var(--text-secondary);line-height:1.45;}
.onboard-showcase-features li::before{content:"";position:absolute;left:0;top:2px;width:16px;height:16px;border-radius:50%;background:var(--accent);}
.onboard-showcase-features li::after{content:"";position:absolute;left:5.5px;top:5px;width:5px;height:9px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(40deg);}
.onboard-card{flex:1 1 50%;min-width:0;background:var(--surface-1);border:none;border-radius:0;padding:40px 44px;display:flex;flex-direction:column;justify-content:center;}
@media (max-width:760px){
  .onboard-shell{max-width:420px;border-radius:16px;}
  .onboard-showcase{display:none;}
  .onboard-card{padding:34px 32px;}
}
.onboard-logo-mark{width:44px;height:44px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;margin-bottom:18px;}
.onboard-logo-mark img{width:100%;height:100%;object-fit:cover;}
.onboard-title{font-size:19px;font-weight:700;margin-bottom:6px;}
.onboard-sub{font-size:14px;color:var(--text-secondary);line-height:1.5;margin-bottom:22px;}
.onboard-field{margin-bottom:14px;}
.onboard-field label{display:block;font-size:13px;font-weight:600;color:var(--text-secondary);margin-bottom:6px;}
.onboard-field input, .onboard-field select{width:100%;border:1px solid var(--gridline);border-radius:8px;padding:10px 12px;font-family:inherit;font-size:15px;background:var(--page);}
.onboard-error{color:var(--critical);font-size:13px;margin:-8px 0 12px;min-height:0;}
.btn-block{width:100%;text-align:center;padding:11px 15px;}

.auth-tabs{display:flex;gap:4px;background:var(--page);border:1px solid var(--gridline);border-radius:10px;padding:3px;margin-bottom:20px;}
.auth-tab{flex:1;appearance:none;border:none;background:transparent;font-family:inherit;font-size:14px;font-weight:600;color:var(--text-secondary);padding:8px 10px;border-radius:8px;cursor:pointer;}
.auth-tab.active{background:var(--surface-1);color:var(--text-primary);box-shadow:0 1px 2px rgba(11,11,11,0.08);}
.auth-panel{display:none;}
.auth-panel.active{display:block;}

/* ===== App shell: top brand bar, left icon rail, content (matches Loop) ===== */
.app-shell{display:flex;flex-direction:column;height:100vh;overflow:hidden;}

.global-topbar{
  display:flex;align-items:center;justify-content:space-between;padding:14px 28px;
  background:var(--surface-1);border-bottom:1px solid var(--gridline);flex-shrink:0;z-index:30;
  gap:14px;flex-wrap:wrap;
}
.brand-word{font-size:18px;font-weight:700;color:var(--text-primary);letter-spacing:-0.01em;display:flex;align-items:center;gap:8px;}
.brand-logo{width:26px;height:26px;border-radius:50%;overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center;}
.brand-logo img{width:100%;height:100%;object-fit:cover;}
.brand-link{appearance:none;border:none;background:transparent;font-family:inherit;padding:0;cursor:pointer;border-radius:6px;}
.brand-link:hover{color:var(--accent);}

.body-row{display:flex;flex:1;min-height:0;}

.sidebar{
  width:68px;flex-shrink:0;background:var(--surface-1);
  /* A real border (not just a box-shadow) - box-shadow spills into the
     .main-col sibling's own box, but .main-col is later in paint order
     and has an opaque background, so it was quietly painting over the
     1px shadow line and leaving the sidebar with no visible edge at all
     next to the content. A border occupies its own layout space (safe
     here - box-sizing:border-box is set globally) so it can't be covered
     that way. Paired with the same soft drop-shadow the header uses on
     its own border-bottom, for a matching "raised above the content"
     look on both edges of the shell. */
  border-right:1px solid var(--gridline);
  box-shadow:3px 0 10px rgba(11,11,11,0.04);
  display:flex;flex-direction:column;padding:14px 0;overflow-y:auto;
  position:relative;z-index:20;
}
.nav-icon-btn{
  position:relative;width:100%;border:none;border-left:3px solid transparent;background:transparent;
  color:var(--text-muted);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;
  padding:16px 4px;cursor:pointer;font-family:inherit;
}
.nav-icon-btn:hover{background:var(--page);}
.nav-icon-btn.active{border-left-color:var(--accent);color:var(--accent);background:var(--accent-wash);}
.nav-icon-btn svg{width:22px;height:22px;}
.nav-icon-btn .nav-label{display:none;}
/* Store Info's "needs attention" nudge (Firms::isComplete()), shown on the
   Settings icon (see navDotHtml()) - --warning, matching
   .firm-incomplete-banner's tone below, rather than the accent used for
   .active, so it reads as "needs action" and never gets confused with the
   selected tab. */
.nav-icon-dot{position:absolute;top:10px;right:14px;width:7px;height:7px;border-radius:50%;background:var(--warning);}

/* Global "Store Info incomplete" banner (see updateFirmBanner() in app.js) -
   sits above .global-topbar, inside #appShell, so it's visible no matter
   which screen a partner is on. Hidden by default in index.php; app.js
   toggles it via style.display. */
.firm-incomplete-banner{
  display:flex;align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;
  padding:9px 28px;background:var(--warning-wash);border-bottom:1px solid var(--gridline);
  font-size:13px;color:#8a5a00;flex-shrink:0;
}
.firm-incomplete-banner button{
  appearance:none;border:1px solid #8a5a00;background:transparent;color:#8a5a00;
  font-family:inherit;font-size:12.5px;font-weight:600;padding:4px 12px;border-radius:20px;cursor:pointer;
}
.firm-incomplete-banner button:hover{background:#8a5a00;color:#fff;}

.main-col{flex:1;min-width:0;display:flex;flex-direction:column;background:var(--page);overflow-y:auto;height:100%;}
.shell{max-width:1100px;margin:0 auto;padding:22px 28px 80px;width:100%;}
/* The fast order screen + order detail page's own wider shell, now shared
   by every other data-dense screen (Customers, Refill Reminders,
   Promotions, Inventory, and the Reports/Settings hubs including their
   internal pages) so page content stays a consistent width across the
   app instead of Orders alone looking wider - see render()'s own
   body.content-wide toggle (navWantsWideShell()) in app.js. */
body.content-wide .shell{max-width:1720px;}

.page-heading{margin-bottom:18px;}
.page-title{font-size:20px;font-weight:700;}
.page-sub{font-size:13px;color:var(--text-muted);margin-top:2px;}

.header-right{display:flex;align-items:center;gap:9px;flex-wrap:wrap;}
.user-chip{position:relative;}
.user-chip-btn{display:flex;align-items:center;gap:8px;background:var(--surface-1);border:1px solid var(--gridline);border-radius:20px;padding:5px 12px 5px 6px;cursor:pointer;font-family:inherit;font-size:13px;font-weight:600;color:var(--text-primary);}
.user-chip-btn .avatar{width:24px;height:24px;font-size:10.5px;}
.user-dropdown{display:none;position:absolute;right:0;top:42px;width:190px;background:var(--surface-1);border:1px solid var(--border);border-radius:12px;box-shadow:0 12px 30px rgba(0,0,0,0.14);z-index:60;padding:8px;}
.user-dropdown.show{display:block;}
.user-dropdown-item{display:block;width:100%;text-align:left;background:transparent;border:none;font-family:inherit;font-size:13px;color:var(--text-secondary);padding:8px 10px;border-radius:8px;cursor:pointer;}
.user-dropdown-item:hover{background:var(--page);color:var(--text-primary);}

.avatar{width:38px;height:38px;border-radius:50%;background:var(--accent-wash);color:var(--accent);display:flex;align-items:center;justify-content:center;font-weight:600;font-size:15px;flex-shrink:0;}
.avatar.sm{width:24px;height:24px;font-size:10.5px;}

/* ===== Shared components ===== */
.card{background:var(--surface-1);border:1px solid var(--border);border-radius:14px;padding:20px 22px;margin-bottom:18px;}
.muted{color:var(--text-muted);}

/* Settings hub / Reports hub landing pages (renderSettingsHub()/
   renderReportsHub() in app.js) - a grid of link cards to the actual
   screens each hub icon now stands in for. */
.settings-hub-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:14px;}
.settings-hub-card{
  display:flex;align-items:flex-start;gap:12px;text-align:left;cursor:pointer;
  background:var(--surface-1);border:1px solid var(--border);border-radius:14px;padding:16px 18px;
  transition:border-color .12s,box-shadow .12s;
}
.settings-hub-card:hover{border-color:var(--accent);box-shadow:0 4px 14px rgba(11,11,11,0.06);}
.settings-hub-card-icon{width:34px;height:34px;flex-shrink:0;border-radius:9px;background:var(--accent-wash);color:var(--accent);display:flex;align-items:center;justify-content:center;}
.settings-hub-card-icon svg{width:18px;height:18px;}
.settings-hub-card-body strong{display:block;font-size:14px;margin-bottom:3px;}
.settings-hub-card-body .muted{font-size:12.5px;line-height:1.4;}
.settings-hub-card.disabled{cursor:not-allowed;opacity:0.55;}
.settings-hub-card.disabled:hover{border-color:var(--border);box-shadow:none;}
.settings-hub-card.disabled .settings-hub-card-icon{background:var(--page);color:var(--text-muted);}

/* Vertical hub-subnav (renderHubSubnav()/wrapWithHubSubnav() in app.js) -
   shown alongside every Settings/Reports screen (the hub landing page and
   each of its child screens alike) so switching between them is one click
   instead of a trip back to the hub icon. */
.hub-layout{display:flex;gap:26px;align-items:flex-start;}
.hub-subnav{width:190px;flex-shrink:0;display:flex;flex-direction:column;gap:2px;}
.hub-subnav-item{
  appearance:none;border:none;background:transparent;text-align:left;font-family:inherit;
  font-size:13.5px;color:var(--text-secondary);padding:9px 12px;border-radius:9px;cursor:pointer;
}
.hub-subnav-item:hover{background:var(--page);color:var(--text-primary);}
.hub-subnav-item.active{background:var(--accent-wash);color:var(--accent);font-weight:600;}
.hub-subnav-item.disabled{cursor:not-allowed;color:var(--text-muted);opacity:0.55;}
.hub-subnav-item.disabled:hover{background:transparent;color:var(--text-muted);}
.hub-content{flex:1;min-width:0;}
/* Group headers for the reports-hub subnav/landing grid (Manish: "group
   reports by type -- like all order reports should be next to each
   other") - see groupedReportsHubItems() in app.js for the actual
   ordering; these are purely the visual labels for each group. */
.hub-subnav-group-label{
  font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-muted);padding:14px 12px 4px;
}
.hub-subnav-group-label:first-child{padding-top:2px;}
.settings-hub-group-label{
  font-size:12.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  color:var(--text-muted);margin:22px 0 10px;
}
.settings-hub-group-label:first-of-type{margin-top:0;}
/* Print/Download toolbar shown above every reports-hub child screen (see
   render()'s own "isReportScreen" step and downloadReportCsv() in app.js).
   .report-print-header is the reverse of the toolbar - invisible on
   screen, shown only inside the print stylesheet below so a printed page
   carries the report's own title instead of the app chrome around it. */
.report-toolbar{display:flex;justify-content:flex-end;gap:8px;margin-bottom:14px;}
.report-print-header{display:none;}
.chip{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:4px 10px;border-radius:20px;border:1px solid var(--gridline);background:var(--page);color:var(--text-secondary);}
.chip.on{background:var(--accent-wash);color:var(--good-text);border-color:transparent;}

.btn{appearance:none;border:none;border-radius:8px;padding:9px 15px;font-family:inherit;font-size:14px;font-weight:600;cursor:pointer;transition:opacity .15s ease;background:transparent;}
.btn:hover{opacity:0.88;}
.btn:disabled{opacity:0.55;cursor:default;}
.btn-primary, .btn{background:var(--accent);color:#fff;}
.btn.secondary, .btn-ghost{background:transparent;color:var(--text-secondary);border:1px solid var(--gridline);}
.btn.danger{background:var(--critical);color:#fff;}

/* ===== Busy-button state - every button that hits the server disables
   itself and shows this the instant it's clicked (see setBtnBusy() in
   app.js), so a slow request can't be fired twice by an impatient
   double-click. Icon-only buttons (rename/delete/remove "x" buttons)
   have no room for a text label, so they just dim instead. ===== */
.btn-spinner{display:inline-block;width:12px;height:12px;margin-right:6px;vertical-align:-2px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;opacity:.75;animation:btn-spin .6s linear infinite;}
@keyframes btn-spin{to{transform:rotate(360deg);}}
.icon-btn:disabled, .site-switcher-icon-btn:disabled{opacity:.4;cursor:default;}
select:disabled, input:disabled{opacity:.6;cursor:default;}

/* ===== Compact SweetAlert2 toast (see notify() in app.js) - the
   library's default toast is sized for a standalone announcement;
   !important is needed to beat its own injected stylesheet. Shrinking
   the icon via transform:scale() (rather than width/height) keeps its
   checkmark/x lines correctly proportioned - see SweetAlert2's own
   .swal2-icon markup, whose lines are positioned in em's off the
   original size. ===== */
.awc-toast.swal2-toast{padding:8px 14px !important;min-height:0 !important;box-shadow:0 2px 10px rgba(0,0,0,.14) !important;border-radius:8px !important;align-items:center !important;}
.awc-toast.swal2-toast .swal2-title{font-size:12.5px !important;font-weight:600 !important;margin:0 !important;padding:0 !important;line-height:1.3 !important;}
.awc-toast.swal2-toast .swal2-icon{transform:scale(0.45) !important;margin:-8px -6px -8px -10px !important;}
.awc-toast.swal2-toast .swal2-timer-progress-bar{height:2px !important;}

/* ===== Compact SweetAlert2 dialog (confirm/prompt popups - see
   confirmDialog()/promptDialog()/promptOrderItemDialog()/
   promptRefillFollowUpDialog() in app.js) - same "shrink the library's
   own generous defaults down to this app's own dense aesthetic"
   reasoning as .awc-toast above, just for a standalone (non-toast)
   popup instead of a corner notification. !important throughout for the
   same reason - beating SweetAlert2's own injected stylesheet. ===== */
.awc-swal-compact.swal2-popup{width:23em !important;padding:22px 22px 18px !important;border-radius:12px !important;font-family:inherit !important;}
.awc-swal-compact .swal2-title{font-size:15px !important;font-weight:700 !important;padding:0 !important;margin:10px 0 0 !important;color:var(--text-primary) !important;}
.awc-swal-compact .swal2-html-container{font-size:13px !important;line-height:1.5 !important;color:var(--text-secondary) !important;margin:8px 0 0 !important;}
.awc-swal-compact .swal2-icon{transform:scale(0.62) !important;margin:6px auto -6px !important;}
.awc-swal-compact .swal2-input, .awc-swal-compact .swal2-textarea{font-family:inherit !important;font-size:13px !important;padding:8px 10px !important;border-radius:8px !important;border:1px solid var(--gridline) !important;box-shadow:none !important;margin:14px auto 0 !important;background:var(--surface-1) !important;color:var(--text-primary) !important;}
.awc-swal-compact .swal2-input:focus, .awc-swal-compact .swal2-textarea:focus{border-color:var(--accent) !important;box-shadow:0 0 0 2px var(--accent-wash) !important;}
.awc-swal-compact .swal2-actions{margin-top:18px !important;gap:8px !important;}
.awc-swal-compact .swal2-confirm, .awc-swal-compact .swal2-cancel, .awc-swal-compact .swal2-deny{font-family:inherit !important;font-size:12.5px !important;font-weight:600 !important;padding:7px 15px !important;border-radius:8px !important;box-shadow:none !important;}
.awc-swal-compact .swal2-cancel{background:transparent !important;color:var(--text-secondary) !important;border:1px solid var(--gridline) !important;}
.awc-swal-compact .swal2-validation-message{font-size:12px !important;border-radius:6px !important;margin-top:10px !important;}
.awc-swal-compact .swal2-close{font-size:22px !important;}
/* The multi-field "+ Add item"/"Mark followed up" dialogs build their
   own tiny form via `html` (SweetAlert2 only ever takes one built-in
   `input`) - this just stacks their labels/fields consistently with the
   single-field .swal2-input/.swal2-textarea styling above. */
.swal-field-stack{text-align:left;}
.swal-field-stack label{display:block;font-size:11.5px;font-weight:600;color:var(--text-secondary);margin:12px 0 4px;}
.swal-field-stack label:first-child{margin-top:4px;}
.swal-field-stack .swal2-input, .swal-field-stack .swal2-textarea{margin:0 !important;width:100% !important;}
.swal-field-stack textarea.swal2-textarea{min-height:60px !important;}

label.field-label{display:block;font-size:12.5px;font-weight:600;color:var(--text-secondary);margin:14px 0 5px;}
/* Wraps a field-label with a small trailing action (e.g. the knowledge-base
   "Expand" button) on the same row instead of stacking them - the label
   inside still carries margin:0 inline since the row itself owns the
   14px/5px spacing that label.field-label would otherwise apply twice. */
.field-label-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:14px 0 5px;}
.field-label-row .field-label{margin:0;}
input, select, textarea{width:100%;border:1px solid var(--gridline);border-radius:8px;padding:9px 12px;font-family:inherit;font-size:14px;background:var(--page);color:var(--text-primary);}
input:focus, select:focus, textarea:focus{outline:none;border-color:var(--accent);}
/* The rule above targets every <input>, which - without this override -
   also stretched every radio/checkbox into a full-width, padded,
   bordered box instead of its small native circle/square. That's what
   made a "radio button section" like .awc-type-choice-option (Store
   Info's single/multiple picker, Widget Settings' display-mode picker,
   the new-site type picker) or a .toggle-row checkbox look mis-aligned -
   the tiny native control sat pinned to the left of its own oversized
   invisible box instead of simply next to its label text. */
input[type="radio"], input[type="checkbox"]{width:auto;flex-shrink:0;border:none;padding:0;background:transparent;border-radius:0;}
.form-error{color:var(--critical);font-size:12.5px;min-height:16px;margin-top:8px;}
/* Type-ahead combobox (comboBoxHtml()/wireComboBox() in app.js) - Store
   Info's Country/State/City fields and the multi-store builder's own
   City/State. Same floating-menu look as .site-filter-menu above. */
.combo-wrap{position:relative;}
.combo-list{display:none;position:absolute;left:0;right:0;top:calc(100% + 4px);min-width:180px;max-height:220px;overflow-y:auto;background:var(--surface-1);border:1px solid var(--gridline);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.12);z-index:20;}
.combo-list.show{display:block;}
.combo-option{padding:8px 12px;font-size:13.5px;cursor:pointer;}
.combo-option:hover{background:var(--accent-wash);color:var(--accent);}
/* The "*" next to a field-label for a required field (see
   firstBlankRequiredField()/showRequiredFieldError() in app.js) - and the
   red outline that same code puts on a required input a person tried to
   submit blank, cleared the moment they start typing in it. */
.field-required-mark{color:var(--critical);margin-left:3px;}
.field-invalid{border-color:var(--critical) !important;}
.field-invalid:focus{border-color:var(--critical) !important;box-shadow:0 0 0 2px var(--critical-wash);}
/* REQUIRED_FIELD_LEGEND (app.js) - the one "* Required" note per form,
   sitting directly above that form's own submit/action row. */
.field-required-legend{font-size:11.5px;color:var(--text-muted);margin:10px 0 0;}
.field-required-legend .field-required-mark{margin-left:0;margin-right:3px;}

.empty-state{display:flex;align-items:center;justify-content:center;height:200px;color:var(--text-muted);font-size:13.5px;}
.empty-state.with-illustration{flex-direction:column;gap:12px;height:auto;padding:40px 20px;text-align:center;}
.empty-state.with-illustration img{width:120px;height:auto;display:block;}
.empty-state.with-illustration div{max-width:360px;}

/* ===== Sites list ===== */
.site-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;}
.site-card{cursor:pointer;margin-bottom:0;}
.site-card:hover{border-color:var(--accent);}
.site-card-name{font-weight:700;font-size:15px;margin-bottom:6px;}

/* ===== Site detail tabs ===== */
.tab-row{display:flex;gap:6px;border-bottom:1px solid var(--gridline);margin-bottom:18px;}
.tab-btn{appearance:none;border:none;background:transparent;font-family:inherit;font-size:13.5px;font-weight:600;color:var(--text-secondary);padding:9px 4px;margin-right:18px;border-bottom:2px solid transparent;cursor:pointer;}
.tab-btn.active{color:var(--text-primary);border-bottom-color:var(--accent);}

.embed-box{background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:12px;font-family:ui-monospace,Consolas,monospace;font-size:12.5px;white-space:pre-wrap;word-break:break-all;}
.toggle-row{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--gridline);}

/* ===== Settings tab - pre-chat form builder ===== */
.prechat-builder{margin-top:18px;padding-top:16px;border-top:1px solid var(--gridline);}
.prechat-fields-empty{font-size:12.5px;padding:4px 0 10px;}
.prechat-field-row{display:flex;align-items:flex-start;gap:10px;padding:9px 0;border-bottom:1px solid var(--gridline);}
.prechat-field-row:last-child{border-bottom:none;}
.prechat-field-info{flex:1;font-size:13px;}
.badge-required{display:inline-block;font-size:10.5px;font-weight:600;color:var(--accent);background:var(--accent-wash);border-radius:10px;padding:1px 7px;margin-left:4px;vertical-align:1px;}
.prechat-add-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px;padding-top:12px;border-top:1px dashed var(--gridline);}
.prechat-add-row input, .prechat-add-row select{font-family:inherit;}

/* ===== Live Chat - full-bleed, same treatment as Loop's own Live Chat/
   Team Chat views (see the body.chat-fullbleed rules above .card). ===== */
body.chat-fullbleed .main-col{overflow:hidden;}
body.chat-fullbleed .shell{max-width:none;width:100%;height:100%;padding:0;display:flex;flex-direction:column;}
body.chat-fullbleed .page-heading{display:none;}
body.chat-fullbleed #viewContent{flex:1;min-height:0;display:flex;flex-direction:column;}

.inbox{display:flex;gap:0;border:1px solid var(--gridline);border-radius:14px;overflow:hidden;background:var(--surface-1);flex:1;min-height:0;}
body.chat-fullbleed .inbox{border-radius:0;border-left:0;border-right:0;border-bottom:0;}

/* ===== Unified Chat screen (SITE_NAV's single 'chat' entry) - a
   vertical channel rail on the left, plus whichever channel's existing
   .inbox markup (unchanged) is currently selected. See renderChatTab()'s
   own comment in app.js. The rail deliberately echoes the main app
   sidebar's own .nav-icon-btn look (border-left accent bar + tinted
   background on .active, muted otherwise) so it reads immediately as
   "a row of switchable destinations" rather than two unrelated buttons -
   plus its own "CHANNELS" label, which the main sidebar doesn't need
   (it's the only rail in the app) but this one does, since it sits
   beside a second, taller list (.chat-list) that could otherwise read
   as part of the same group. ===== */
.chat-shell{display:flex;gap:0;flex:1;min-height:0;position:relative;}
.chat-shell .inbox{border-left:0;border-radius:0 14px 14px 0;}
body.chat-fullbleed .chat-shell .inbox{border-radius:0;}

/* ===== Chat-integrated "+ Create order" split panel - see
   renderChatTab()/renderOrderSplitPanel() in app.js. .chat-order-split
   wraps .chat-shell (unchanged) and .order-split-panel as 50/50 flex
   siblings, replacing .chat-shell directly in #viewContent's flex column
   (same flex:1;min-height:0 role .chat-shell played on its own), so chat
   and the order screen are both fully visible and usable at once instead
   of one covering the other. Not position:absolute - a deliberate move
   away from an earlier full-cover overlay that used exactly that (see the
   Promotions-tab comment further down for the last trace of it), since a
   flex sibling can't accidentally expand past a missing positioned
   ancestor the way absolute+inset:0 did. ===== */
.chat-order-split{display:flex;gap:12px;flex:1;min-height:0;}
.chat-order-split .chat-shell{flex:0 0 50%;min-width:0;}
.order-split-panel{
  flex:0 0 50%;min-width:0;display:flex;flex-direction:column;
  background:var(--surface-1);border:1px solid var(--gridline);border-radius:14px;overflow:hidden;
}
.order-split-panel-header{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:14px 18px;border-bottom:1px solid var(--gridline);flex-shrink:0;}
.order-split-panel-body{flex:1;min-height:0;overflow-y:auto;padding:16px 18px;}
/* The panel's own copy of the fast order screen is always single-column,
   regardless of viewport width - .fast-order-grid's normal two-column
   layout (below) assumes the full nav tab's own width, which this half-
   width panel never has. */
.order-split-panel .fast-order-grid{grid-template-columns:1fr;}
@media (max-width: 1100px){
  /* Below this, 50/50 leaves both halves too cramped to use - stack them
     instead, same "give up on side-by-side, give each its own full-width
     turn" fallback .fast-order-grid already uses at 900px. */
  .chat-order-split{flex-direction:column;}
  .chat-order-split .chat-shell{flex:1 1 50%;min-height:0;}
  .order-split-panel{flex:1 1 50%;min-height:0;}
}
/* 78px - a touch wider than the main .sidebar's own 68px (that one has
   no text label to fit; this one does, and "CHANNELS" was crowding the
   64px version's edges at 9px/0.06em). */
.chat-channel-rail{
  width:78px;flex-shrink:0;display:flex;flex-direction:column;overflow:hidden;
  background:var(--page);border:1px solid var(--gridline);border-right:1px solid var(--gridline);
  border-radius:14px 0 0 14px;
}
body.chat-fullbleed .chat-channel-rail{border-radius:0;border-left:0;border-top:0;border-bottom:0;}
.chat-channel-rail-label{
  padding:10px 8px 7px;font-size:9.5px;font-weight:700;letter-spacing:0.04em;text-transform:uppercase;
  color:var(--text-muted);background:var(--surface-1);border-bottom:1px solid var(--gridline);flex-shrink:0;
  text-align:center;
}
.chat-channel-list{display:flex;flex-direction:column;padding:5px 0;gap:2px;overflow-y:auto;}
.chat-channel-btn{
  position:relative;width:100%;border:none;border-left:3px solid transparent;background:transparent;
  color:var(--text-secondary);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;
  padding:10px 3px;cursor:pointer;font-family:inherit;
}
.chat-channel-btn:hover{background:var(--surface-1);}
.chat-channel-btn.active{border-left-color:var(--accent);color:var(--accent);background:var(--accent-wash);}
.chat-channel-icon{width:22px;height:22px;border-radius:50%;background:var(--surface-1);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.chat-channel-btn.active .chat-channel-icon{background:#fff;}
.chat-channel-icon svg{width:13px;height:13px;}
.chat-channel-label{font-size:9.5px;font-weight:600;line-height:1.15;}
.chat-channel-badge{position:absolute;top:6px;right:9px;min-width:15px;height:15px;padding:0 3px;border-radius:8px;background:var(--accent);color:#fff;font-size:9px;font-weight:700;display:flex;align-items:center;justify-content:center;line-height:1;}

.chat-list{width:300px;flex-shrink:0;border-right:1px solid var(--gridline);display:flex;flex-direction:column;min-height:0;}
.chat-list-items{flex:1;overflow-y:auto;}
/* WhatsApp inbox's toolbar strip below the Active/Pending/Resolved tabs
   (tag filter chips, + the "New conversation" button for an admin) - see
   renderWhatsappInboxTab()'s own comment on why this replaced a per-site
   filter there. Only rendered when it has something in it, so there's no
   empty gap for a partner with no tags and no admin rights. Stacked as a
   column - a site can define quite a few tags, and letting the button
   sit inline with them (align-items:center in a wrapping row) meant it
   could end up squeezed onto whatever tag chips' last, partly-filled
   line rather than reliably on its own; .wa-tag-filter-row stretches to
   the full width so its own chips still wrap freely across as many lines
   as they need, while the button (left to its natural, unstretched
   width via align-items:flex-start here) always lands directly below. */
.chat-list-toolbar{display:flex;flex-direction:column;align-items:flex-start;gap:8px;padding:10px 12px;border-bottom:1px solid var(--gridline);flex-shrink:0;}

/* Active/Resolved tabs - reused for anything that filters one list by a
   status without swapping panels (same idea as Loop's own .chat-tabs). */
.chat-tabs{display:flex;gap:4px;padding:10px 12px 0;flex-shrink:0;}
.chat-tab{flex:1;appearance:none;border:none;background:transparent;border-radius:8px;padding:7px 10px;font-family:inherit;font-size:12.5px;font-weight:600;color:var(--text-muted);cursor:pointer;}
.chat-tab:hover{background:var(--page);}
.chat-tab.active{background:var(--accent-wash);color:var(--accent);}

/* Site filter dropdown ("All sites (N)") above the conversation list -
   scoped to whichever Active/Resolved tab is selected, see
   renderChatSiteFilter(). Closed by default; .show reveals the menu,
   toggled the same way as .site-switcher-dropdown/.user-dropdown. */
.site-filter{position:relative;padding:8px 12px;flex-shrink:0;border-bottom:1px solid var(--gridline);}
.site-filter-trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;appearance:none;border:1px solid var(--gridline);background:var(--page);border-radius:8px;padding:7px 10px;font-family:inherit;font-size:12.5px;font-weight:600;color:var(--text-secondary);cursor:pointer;}
.site-filter-trigger svg{width:14px;height:14px;flex-shrink:0;}
.site-filter-menu{display:none;position:absolute;left:12px;right:12px;top:calc(100% + 2px);background:var(--surface-1);border:1px solid var(--gridline);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.12);z-index:20;max-height:240px;overflow-y:auto;}
.site-filter.show .site-filter-menu{display:block;}
.site-filter-option{width:100%;display:flex;align-items:center;justify-content:space-between;gap:8px;appearance:none;border:none;background:transparent;padding:8px 12px;font-family:inherit;font-size:12.5px;text-align:left;cursor:pointer;}
.site-filter-option:hover, .site-filter-option.active{background:var(--accent-wash);color:var(--accent);}
.site-filter-option-count{color:var(--text-muted);font-size:11.5px;}
.site-filter-option.active .site-filter-option-count{color:inherit;}

.chat-list-item{display:flex;gap:10px;padding:12px 14px;border-bottom:1px solid var(--gridline);cursor:pointer;}
.chat-list-item:hover, .chat-list-item.active{background:var(--accent-wash);}
.chat-list-avatar{margin-top:2px;}
.chat-list-body{min-width:0;flex:1;}
.chat-list-name{font-weight:600;font-size:13.5px;display:flex;align-items:center;gap:6px;}
.chat-list-preview{font-size:12px;color:var(--text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:2px;}
.chat-list-unread{width:8px;height:8px;border-radius:50%;background:var(--accent);flex-shrink:0;}
.chat-pane{flex:1;display:flex;flex-direction:column;min-width:0;min-height:0;}
.chat-pane-header{padding:12px 16px;border-bottom:1px solid var(--gridline);display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;}
.prechat-answers{padding:8px 16px;border-bottom:1px solid var(--gridline);background:var(--page);flex-shrink:0;}
.prechat-answers summary{cursor:pointer;font-size:12.5px;font-weight:600;color:var(--text-secondary);}
.prechat-answer-row{display:flex;gap:8px;font-size:12.5px;padding:4px 0 0 2px;}
.prechat-answer-label{color:var(--text-muted);min-width:120px;flex-shrink:0;}
.chat-pane-controls{display:flex;align-items:center;gap:14px;flex-wrap:wrap;}
.chat-assign-label{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--text-secondary);white-space:nowrap;}
.chat-assign-label select{font-family:inherit;font-size:12.5px;padding:4px 6px;border-radius:6px;border:1px solid var(--gridline);background:var(--surface-1);color:var(--text-primary);max-width:150px;}
.chat-ai-toggle{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--text-secondary);cursor:pointer;white-space:nowrap;}
.btn-sm{padding:6px 12px;font-size:12.5px;}
/* A button that reads as a text link (the knowledge-base "Expand" action) -
   no background/border/padding so it sits inline next to a field-label
   without looking like a full secondary button. */
.btn-link{appearance:none;border:none;background:transparent;padding:0;font-family:inherit;font-size:12.5px;font-weight:600;color:var(--accent);cursor:pointer;text-decoration:none;white-space:nowrap;}
.btn-link:hover{text-decoration:underline;}
/* The WhatsApp inbox thread header's contact name, when it's a clickable
   jump to that contact's Customers record (see app.js's
   openWaContactCustomer()) - same .btn-link base, sized back up to match
   the plain <strong> text it replaces so an order-type site's header
   doesn't look different from a chat-type site's non-clickable one. */
.wa-contact-name-btn{font-size:inherit;color:inherit;}
.wa-contact-name-btn:hover{color:var(--accent);}
.chat-thread{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;background:var(--page);}
/* margin-bottom widened from a cramped 10px so consecutive messages read
   as clearly separate groups rather than running together, especially
   once a status+time meta line sits under most of them. */
.dash-msg{display:flex;flex-direction:column;max-width:70%;margin-bottom:16px;}
.dash-msg.mine{align-self:flex-end;align-items:flex-end;}
.dash-msg.theirs{align-self:flex-start;align-items:flex-start;}
.dash-msg-sender{font-size:11px;color:var(--text-muted);margin-bottom:4px;}
.dash-bubble{padding:10px 13px;border-radius:14px;font-size:13.5px;line-height:1.45;white-space:pre-wrap;word-break:break-word;background:var(--surface-1);border:1px solid var(--gridline);}
.dash-msg.mine .dash-bubble{background:var(--accent);color:#fff;border-color:transparent;}
/* Manish: "in chat widget when getting messages like this can we make
 * links clickable" - a linkified tracking/order-status URL (see
 * linkifyText() in app.js) inherits the bubble's own text color rather
 * than the browser's default link blue (which would be unreadable on
 * the "mine" bubble's own accent-colored background), staying
 * recognizable as a link only via the underline. */
.dash-bubble a{color:inherit;text-decoration:underline;}
/* A flex row with an explicit gap (rather than the old plain text-with-
   "&middot;"-glued-in approach) so the timestamp and, on WhatsApp, the
   delivery-status label always sit a consistent, deliberate distance
   apart no matter how long either piece of text is. */
.dash-msg-meta{display:flex;align-items:center;gap:5px;font-size:10.5px;color:var(--text-muted);margin-top:5px;}
.dash-msg-meta-sep{color:var(--text-muted);}
/* A horizontal rule with a floating pill label, dropped between message
   groups whenever the calendar day changes (see app.js's
   buildMessageThreadHtml()/dateDividerLabel()) - same idea as most
   messaging apps' own "Today"/"Yesterday" dividers. */
.chat-date-divider{display:flex;align-items:center;gap:10px;margin:6px 0 14px;align-self:stretch;}
.chat-date-divider::before,.chat-date-divider::after{content:'';flex:1;height:1px;background:var(--gridline);}
.chat-date-divider span{font-size:11px;font-weight:600;color:var(--text-muted);white-space:nowrap;padding:3px 12px;border-radius:999px;background:var(--surface-1);border:1px solid var(--gridline);}
/* The "Load previous messages" affordance for a thread whenever more
   history exists beyond what's currently loaded (see
   ChatWidget::messages()/WhatsAppInbox::messages()'s has_more) - also
   auto-triggered by scrolling near the top (wireMessageThreadScroll()),
   this link/button is what a click on it looks like. `position:sticky`
   pins it to the top of the thread's own scroll area (.chat-thread is
   the scrolling element - see that rule below) once scrolling reaches
   it, so it stays reachable as a persistent, always-visible option
   rather than something that only ever shows up if a scroll gesture
   happens to land within wireMessageThreadScroll()'s own 60px trigger
   zone. Its own opaque background + z-index keep older message bubbles
   from visibly scrolling up underneath/through it once it's stuck. */
.chat-load-older{display:flex;justify-content:center;align-self:stretch;margin-bottom:14px;position:sticky;top:0;z-index:2;background:var(--page);padding-bottom:6px;}
.chat-load-older-btn{appearance:none;font-family:inherit;font-size:12px;font-weight:600;color:var(--accent);background:var(--accent-wash);border:1px solid transparent;border-radius:999px;padding:6px 16px;cursor:pointer;}
.chat-load-older-btn:hover{opacity:0.85;}
.chat-load-older-btn:disabled{cursor:default;opacity:0.6;}
/* The small "Refreshing in Ns" indicator in each thread's header (see
   startChatCountdownTicker() in app.js) - a quiet, ambient sign that this
   inbox is polling on its own rather than something the agent needs to
   act on, so it's deliberately understated (muted text, a small pulsing
   dot) rather than styled like a button or a live badge count. */
.chat-refresh-badge{display:flex;align-items:center;gap:6px;font-size:11.5px;color:var(--text-muted);white-space:nowrap;}
.chat-refresh-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0;animation:chatRefreshPulse 2s ease-in-out infinite;}
@keyframes chatRefreshPulse{0%,100%{opacity:0.35;}50%{opacity:1;}}
.chat-composer{display:flex;flex-direction:column;gap:8px;padding:12px;border-top:1px solid var(--gridline);}
.chat-composer-row{display:flex;align-items:flex-end;gap:8px;}
.chat-composer textarea{flex:1;resize:none;}
.chat-send-btn,.chat-attach-btn{appearance:none;border:none;border-radius:50%;width:36px;height:36px;flex-shrink:0;display:flex;align-items:center;justify-content:center;cursor:pointer;}
.chat-send-btn{background:var(--accent);color:#fff;}
.chat-send-btn:hover{opacity:0.88;}
.chat-send-btn svg{width:17px;height:17px;}
.chat-attach-btn{background:var(--page);color:var(--text-secondary);border:1px solid var(--gridline);}
.chat-attach-btn:hover{background:var(--accent-wash);color:var(--accent);}
.chat-attach-btn svg{width:16px;height:16px;}
.chat-attach-preview{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--text-secondary);background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:6px 10px;}
.chat-attach-preview svg{width:15px;height:15px;flex-shrink:0;}
.chat-attach-preview span{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.icon-btn{appearance:none;border:none;background:none;color:var(--text-muted);cursor:pointer;padding:2px;flex-shrink:0;display:flex;align-items:center;justify-content:center;}
.icon-btn:hover{color:var(--text-primary);}
.icon-btn svg{width:14px;height:14px;}
.chat-attachment-img{display:block;max-width:220px;max-height:180px;border-radius:10px;object-fit:cover;cursor:pointer;margin-bottom:4px;}
.chat-attachment-video{display:block;max-width:260px;max-height:200px;border-radius:10px;background:#000;margin-bottom:4px;}
.chat-attachment-file{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:10px;font-size:13px;text-decoration:none;color:inherit;background:var(--page);border:1px solid var(--gridline);margin-bottom:4px;}
.chat-attachment-file svg{width:16px;height:16px;flex-shrink:0;}
.dash-msg.mine .chat-attachment-file{background:rgba(255,255,255,0.15);border-color:transparent;color:#fff;}
/* Order-widget-only: real catalog candidates an AI reply attached (see
   app.js's chatProductsHtml()) - read-only for the partner, deliberately
   outside .dash-bubble so it stays neutral (not tinted the "mine" accent
   color) whichever side of the thread it renders on. */
.dash-msg-products{display:flex;flex-direction:column;gap:4px;margin-top:4px;width:100%;min-width:200px;}
.dash-msg-product-row{display:flex;justify-content:space-between;gap:10px;font-size:12px;padding:5px 9px;border-radius:8px;background:var(--page);border:1px solid var(--gridline);color:var(--text-secondary);}
.dash-msg-product-row span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dash-msg-product-row span:last-child{flex-shrink:0;font-weight:600;color:var(--text-primary);}

/* ===== WhatsApp Inbox - reuses Live Chat's .inbox/.chat-list/.chat-pane/
   .dash-msg/.chat-composer/.chat-attachment-* classes above wholesale;
   only what's genuinely WhatsApp-specific (session-expired banner, tag
   chips/filter row, delivery ticks, template picker) gets its own rules
   here. ===== */
.wa-session-banner{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px;margin:12px;border-radius:10px;background:var(--critical-wash);color:var(--critical);font-size:12.5px;}
/* WhatsApp inbox's own composer - modeled on web.whatsapp.com's rounded
   message bar (icons + growing text field inside one pill, Send as a
   separate circular button outside it) rather than reusing Live Chat's
   plain bordered-row .chat-composer. See renderWhatsappInboxTab()'s own
   comment on why mic/camera/sticker stay out (no gateway API support)
   while emoji is included (it's still just typed text). */
.wa-composer{display:flex;flex-direction:column;gap:8px;padding:10px 12px;border-top:1px solid var(--gridline);}
/* Centered, not bottom-aligned: the pill (.wa-composer-bar) is taller
   than the round Send button once its own padding is counted, so
   lining up their bottom edges left Send looking like it floated a few
   pixels above the pill instead of sitting level with it. Centering
   keeps Send visually level with the pill at the normal one-line
   height, and stays reasonable as the pill grows with a longer message. */
.wa-composer-row{display:flex;align-items:center;gap:8px;}
.wa-composer-bar{flex:1;min-width:0;display:flex;align-items:flex-end;gap:2px;background:var(--page);border:1px solid var(--gridline);border-radius:22px;padding:5px 6px;}
.wa-composer-textarea{flex:1;min-width:0;resize:none;border:none;background:none;padding:7px 6px;line-height:1.35;max-height:120px;overflow-y:auto;}
.wa-composer-textarea:focus{outline:none;box-shadow:none;}
.wa-composer-icon-btn{position:relative;appearance:none;border:none;background:none;color:var(--text-muted);border-radius:50%;width:34px;height:34px;flex-shrink:0;display:flex;align-items:center;justify-content:center;cursor:pointer;}
.wa-composer-icon-btn:hover{background:var(--accent-wash);color:var(--accent);}
.wa-composer-icon-btn svg{width:17px;height:17px;pointer-events:none;}
/* The canned-replies trigger is a real <select> (full keyboard/screen-
   reader support, and the browser's own native dropdown for free,
   instead of a hand-rolled one) drawn to look like a plain icon button -
   the select sits directly on top of the icon at zero opacity and fills
   the same box, so any click on the icon opens it. */
.wa-composer-select-wrap select{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer;border:none;}
/* Anchors the emoji picker panel (absolutely positioned) to the emoji
   button specifically, not the whole composer bar - see
   toggleWaEmojiPicker()/insertEmojiIntoWaComposer() in app.js for the
   open/insert logic and the top-level document click handler that
   closes it on an outside click. */
.wa-emoji-anchor{position:relative;display:flex;}
.wa-emoji-picker{position:absolute;bottom:calc(100% + 8px);left:0;z-index:20;display:grid;grid-template-columns:repeat(8,1fr);gap:2px;width:264px;max-height:180px;overflow-y:auto;padding:8px;background:var(--surface-1);border:1px solid var(--gridline);border-radius:12px;box-shadow:0 8px 24px rgba(0,0,0,0.18);}
.wa-emoji-option{appearance:none;border:none;background:none;font-size:19px;line-height:1;padding:5px;border-radius:6px;cursor:pointer;}
.wa-emoji-option:hover{background:var(--accent-wash);}
@media (max-width:640px){
  .wa-composer{padding:8px;}
  .wa-emoji-picker{width:224px;grid-template-columns:repeat(7,1fr);}
  .wa-composer-bar{border-radius:18px;}
}
.wa-tag-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--gridline);}
/* The filter-chip row above the conversation list (renderWhatsappInboxTab()'s
   toolbarHtml) - unlike .wa-tag-row above, this one carries no padding/
   border of its own since it's nested inside .chat-list-toolbar, which
   already provides both (see that class below). */
.wa-tag-filter-row{align-self:stretch;display:flex;flex-wrap:wrap;gap:6px;}
/* Coloring (background/color/border-color) is set inline per-tag from
   the tag's own saved hex color (see app.js's tagChips/tagFilterHtml) -
   this just handles shape, spacing and the pointer cursor on the
   clickable filter-row variant (.wa-tag-filter-chip). */
.wa-tag-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 8px 3px 10px;border-radius:20px;font-size:11.5px;font-weight:600;border:1px solid transparent;}
.wa-tag-filter-chip{appearance:none;cursor:pointer;font-family:inherit;}
/* Was previously missing an explicit svg size rule (unlike .icon-btn svg
   elsewhere) - the "x" glyph fell back to the browser's default replaced-
   element sizing and rendered oversized inside the tiny chip, which made
   the remove control look broken rather than clickable. A fixed 16x16px
   hit area (bigger than the 10px icon itself) plus a hover circle now
   makes it obviously present and easy to tap, not just technically wired. */
.wa-tag-remove{appearance:none;border:none;background:none;color:inherit;opacity:0.7;cursor:pointer;padding:0;width:16px;height:16px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;}
.wa-tag-remove svg{width:10px;height:10px;}
.wa-tag-remove:hover{opacity:1;background:rgba(0,0,0,0.12);}
.wa-status-sent,.wa-status-delivered,.wa-status-read,.wa-status-failed{font-size:10.5px;}
.wa-status-read{color:var(--accent);font-weight:600;}
.wa-status-failed{color:var(--critical);font-weight:600;}
.wa-tpl-header-var,.wa-tpl-body-var,.wa-tpl-params,.wa-tpl-param-input{margin-bottom:0;}
.wa-tpl-advanced{margin-top:6px;font-size:12.5px;color:var(--text-secondary);}
.wa-tpl-advanced summary{cursor:pointer;color:var(--accent);font-weight:600;padding:4px 0;}
.wa-tpl-advanced[open] summary{margin-bottom:4px;}
/* The synced template's own content - shown above the Params field in
   both "Send template" modals, so filling in {{1}}, {{2}}, … means
   reading the actual template text instead of guessing. */
.wa-tpl-preview{background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:10px 12px;margin-top:10px;margin-bottom:10px;font-size:12.5px;}
.wa-tpl-preview-label{font-size:11px;text-transform:uppercase;letter-spacing:0.03em;color:var(--text-muted);font-weight:600;margin-bottom:6px;}
.wa-tpl-preview-header{font-weight:700;margin-bottom:4px;color:var(--text-primary);}
.wa-tpl-preview-body{line-height:1.5;color:var(--text-secondary);white-space:pre-wrap;word-break:break-word;}
.wa-tpl-placeholder{background:var(--good-wash);color:var(--good-text);border-radius:4px;padding:0 3px;font-weight:600;}
.wa-tpl-placeholder em{font-style:normal;font-weight:400;opacity:0.85;}
.wa-tpl-preview-buttons{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;}
.wa-tpl-preview-btn{border:1px solid var(--gridline);border-radius:20px;padding:3px 10px;font-size:11.5px;color:var(--accent);}
.wa-template-list{display:flex;flex-direction:column;gap:8px;}
.wa-template-card{border:1px solid var(--gridline);border-radius:8px;padding:10px 12px;}
.wa-template-name{font-weight:600;font-size:13px;margin-bottom:4px;}
.wa-template-fields{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:12px;color:var(--text-secondary);}
.wa-bh-days{display:flex;flex-wrap:wrap;gap:10px;margin-top:8px;}
.wa-bh-day-label{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;color:var(--text-secondary);font-weight:500;}
.wa-bh-day-label input{margin:0;}

/* ===== API Key page ===== */
.key-status{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;padding:5px 12px;border-radius:20px;margin-bottom:16px;}
.key-status.connected{background:var(--accent-wash);color:var(--good-text);}
.key-status.missing{background:var(--critical-wash);color:var(--critical);}

/* ===== Header site switcher - picks the site the sidebar's site-scoped
   sections (Live Chat/Settings/Embed code/WooCommerce) apply to. Same
   chip+dropdown visual language as .user-chip/.user-dropdown next to it. ===== */
.site-switcher{position:relative;}
.site-switcher-chip{display:flex;align-items:center;gap:7px;background:var(--surface-1);border:1px solid var(--gridline);border-radius:20px;padding:7px 12px;cursor:pointer;font-family:inherit;font-size:13px;font-weight:600;color:var(--text-primary);}
.site-switcher-current{max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.site-switcher-dropdown{display:none;position:absolute;left:0;top:42px;width:260px;max-height:340px;overflow-y:auto;background:var(--surface-1);border:1px solid var(--border);border-radius:12px;box-shadow:0 12px 30px rgba(0,0,0,0.14);z-index:60;padding:8px;}
.site-switcher-dropdown.show{display:block;}
.site-switcher-row{display:flex;align-items:center;gap:2px;border-radius:8px;}
.site-switcher-row:hover, .site-switcher-row.active{background:var(--page);}
.site-switcher-name{flex:1;text-align:left;background:transparent;border:none;font-family:inherit;font-size:13.5px;color:var(--text-primary);padding:8px 8px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border-radius:8px;}
.site-switcher-row.active .site-switcher-name{color:var(--accent);font-weight:600;}
.site-switcher-icon-btn{background:transparent;border:none;color:var(--text-muted);font-size:14px;padding:6px 8px;cursor:pointer;border-radius:6px;}
.site-switcher-icon-btn:hover{background:var(--gridline);color:var(--text-primary);}
.site-switcher-icon-btn.danger:hover{background:var(--critical-wash);color:var(--critical);}
.site-switcher-divider{height:1px;background:var(--gridline);margin:6px 4px;}
.site-switcher-add{width:100%;text-align:left;background:transparent;border:none;font-family:inherit;font-size:13.5px;font-weight:600;color:var(--accent);padding:8px;cursor:pointer;border-radius:8px;}
.site-switcher-add:hover{background:var(--accent-wash);}

/* ===== Create-site empty state - the whole dashboard collapses to this
   until a partner has at least one site, see render() in app.js. ===== */
.create-site-card{max-width:520px;margin:40px auto 0;}
.create-site-title{font-size:17px;font-weight:700;}

/* ===== Team page ===== */
.team-row{display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid var(--gridline);}
.team-row:last-child{border-bottom:none;}
.team-row-name{flex:1;font-size:13.5px;font-weight:600;}

/* ===== AI Usage page ===== */
.ai-usage-summary{display:flex;flex-wrap:wrap;gap:22px;padding:18px 22px;}
.ai-usage-stat{min-width:100px;}
.ai-usage-stat-num{font-size:22px;font-weight:700;color:var(--text-primary);}
.ai-usage-stat-num.good{color:var(--good-text);}
.ai-usage-stat-num.bad{color:var(--critical);}
/* insightsStatCard() (Insights dashboard) nests a <span class="good"/"bad">
   inside the num div instead of putting the class on the div itself (its
   numHtml can be a plain value OR a value + a separate muted breakdown
   line - see the "New conversations" card - so the color can't always
   live on the outer div), same color, different selector shape. */
.ai-usage-stat-num .good{color:var(--good-text);}
.ai-usage-stat-num .bad{color:var(--critical);}
.ai-usage-stat-secondary{display:block;font-size:13px;font-weight:400;color:var(--text-muted);}
.ai-usage-stat-label{font-size:12px;color:var(--text-muted);margin-top:2px;}

/* ===== Insights dashboard - clickable stat cards/table rows (Manish:
   "make things clickable and redirect to relevant details on click in
   insight") and the "Live" auto-refresh badge (Manish: "give something
   live and interactive in insight"). ===== */
.ai-usage-stat-clickable{cursor:pointer;border-radius:10px;padding:6px 10px;margin:-6px -10px;transition:background .15s ease;}
.ai-usage-stat-clickable:hover,.ai-usage-stat-clickable:focus-visible{background:var(--accent-wash);outline:none;}
.ai-usage-stat-link{display:block;font-size:11px;font-weight:600;color:var(--accent);margin-top:3px;}
.clickable-row{cursor:pointer;}
.clickable-row:hover td{background:var(--accent-wash);}
.insights-live-badge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;color:var(--good-text);margin-left:10px;}
.insights-live-dot{width:7px;height:7px;border-radius:50%;background:var(--good-text);animation:insights-live-pulse 1.6s ease-in-out infinite;}
@keyframes insights-live-pulse{0%,100%{opacity:1;}50%{opacity:.3;}}
.ai-usage-toolbar{display:flex;align-items:center;gap:10px;margin-bottom:14px;}
.ai-usage-toolbar select{width:auto;min-width:160px;}
.ai-usage-list{display:flex;flex-direction:column;gap:10px;}
.ai-log-row{background:var(--surface-1);border:1px solid var(--gridline);border-radius:10px;padding:12px 14px;}
.ai-log-row.has-error{border-color:rgba(208,59,59,0.35);background:var(--critical-wash);}
.ai-log-row-head{display:flex;flex-wrap:wrap;align-items:center;gap:10px;font-size:12.5px;}
.ai-log-status{font-weight:700;font-size:12px;padding:2px 8px;border-radius:20px;}
.ai-log-status.ok{color:var(--good-text);background:var(--good-wash);}
.ai-log-status.err{color:var(--critical);background:var(--critical-wash);}
.ai-log-cost{margin-left:auto;color:var(--text-secondary);font-weight:600;white-space:nowrap;}
.ai-log-row-body{margin-top:8px;padding-top:8px;border-top:1px solid var(--gridline);}
.ai-log-msg{font-size:12.5px;line-height:1.5;color:var(--text-secondary);word-break:break-word;}
.ai-log-msg + .ai-log-msg{margin-top:4px;}
.ai-log-msg.error{color:var(--critical);}

/* ===== Insights dashboard - 70/30 split layout (Manish: "split screen
   in 2 parts 70% current insight and in 30% show things for today to
   do"). .insights-main is the existing report content, unchanged;
   .insights-today is the new always-on "today to do" panel, kept fresh
   on its own 5-minute timer (see app.js's startInsightsPolling()). Both
   columns are flex children rather than a fixed-percentage grid so
   .insights-today can carry its own min-width and never get crushed on
   a narrower desktop window, and stack below 1100px the same way
   .fast-order-grid already stacks at its own breakpoint above. ===== */
.insights-layout{display:flex;gap:16px;align-items:flex-start;}
.insights-main{flex:7 1 0;min-width:0;}
.insights-today{flex:3 1 0;min-width:260px;display:flex;flex-direction:column;gap:16px;}
@media (max-width: 1100px){.insights-layout{flex-direction:column;}.insights-main,.insights-today{flex:1 1 auto;width:100%;min-width:0;}}
/* Dot legend (Manish: "add legends in insights whereever needed") -
   explains the attention/clear coloring used by every row in the Today
   panel, the same job .forecast-chart-legend already does for the trend
   chart's two series above. */
.insights-today-legend{display:flex;align-items:center;gap:14px;margin:2px 0 10px;}
.insights-legend-item{display:inline-flex;align-items:center;gap:5px;}
.insights-dot{display:inline-block;width:8px;height:8px;border-radius:50%;flex:none;}
.insights-dot-attention{background:var(--critical);}
.insights-dot-clear{background:var(--good-text);}
.insights-todo-list{display:flex;flex-direction:column;}
.insights-todo-row{display:flex;align-items:center;gap:9px;padding:9px 4px;border-bottom:1px solid var(--gridline);}
.insights-todo-row:last-child{border-bottom:none;}
.insights-todo-row-clickable{cursor:pointer;border-radius:8px;margin:0 -4px;padding:9px 8px;transition:background .15s ease;}
.insights-todo-row-clickable:hover,.insights-todo-row-clickable:focus-visible{background:var(--accent-wash);outline:none;}
.insights-todo-label{flex:1;font-size:13px;color:var(--text-secondary);}
.insights-todo-count{font-size:14px;font-weight:700;color:var(--text-primary);}
.insights-todo-count.bad{color:var(--critical);}
.insights-staff-table{font-size:12.5px;}

/* ===== Raw debug output (WhatsApp Webhook Log report, Templates API raw
   response in WhatsApp Settings) - both show a gateway's exact reply/request
   bytes verbatim, since the gateway's response/payload shapes are
   undocumented and heuristic key-matching can silently miss real data. */
.wa-raw-debug{margin-top:12px;font-size:12.5px;}
.wa-raw-debug summary{cursor:pointer;color:var(--text-secondary);font-weight:600;}
.wa-raw-debug pre,
.wa-raw-debug-pre{background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:10px 12px;margin-top:8px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;line-height:1.5;white-space:pre-wrap;word-break:break-word;max-height:260px;overflow-y:auto;color:var(--text-secondary);}

/* ===== Compact, collapsible log rows (WhatsApp Webhook Log / API Send
   Log reports) - each row is a <details> so the full raw payload/
   response stays one click away instead of always expanded, which is
   what keeps a page of many rows scannable. */
.wa-log-list{gap:6px;}
.wa-log-compact{padding:0;}
.wa-log-compact > summary{padding:9px 14px;list-style:none;cursor:pointer;}
.wa-log-compact > summary::-webkit-details-marker{display:none;}
.wa-log-compact > summary::before{content:'▸';display:inline-block;width:10px;color:var(--text-muted);transition:transform 0.15s ease;}
.wa-log-compact[open] > summary::before{transform:rotate(90deg);}
.wa-log-compact > .ai-log-row-body{margin:0 14px 10px;padding-top:8px;}
.wa-log-preview{color:var(--text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;}
.wa-log-url{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11.5px;word-break:break-all;color:var(--text-secondary);}

/* ===== Batched sync/crawl progress (WooCommerce catalog, website crawl) ===== */
/* A determinate bar (product count known via WooCommerce's X-WP-Total
   header) fills to a real %; an indeterminate one (crawl page count is
   only known once it finishes) shows a moving stripe instead - see
   updateInlineProgress()/renderWoocommerceTab()/renderWebsiteContentCard()
   in app.js. */
.progress-row{margin-top:10px;}
.progress-track{height:8px;border-radius:20px;background:var(--page);border:1px solid var(--gridline);overflow:hidden;}
.progress-fill{height:100%;background:var(--accent);border-radius:20px;transition:width 0.3s ease;}
.progress-fill.indeterminate{width:35%!important;background:linear-gradient(90deg,transparent,var(--accent),transparent);animation:progress-indeterminate 1.3s ease-in-out infinite;}
@keyframes progress-indeterminate{0%{transform:translateX(-100%);}100%{transform:translateX(285%);}}
.progress-text{font-size:12px;color:var(--text-muted);margin-top:6px;}

/* ===== Products / crawled-pages list panels (WooCommerce tab, Settings tab) ===== */
.list-table{width:100%;border-collapse:collapse;font-size:13px;}
.list-table th{text-align:left;font-size:11.5px;text-transform:uppercase;letter-spacing:0.03em;color:var(--text-muted);font-weight:600;padding:6px 8px;border-bottom:1px solid var(--gridline);}
.list-table td{padding:8px;border-bottom:1px solid var(--gridline);vertical-align:middle;color:var(--text-secondary);}
.list-table tr:last-child td{border-bottom:none;}
.list-thumb-cell{width:40px;}
.list-check-cell{width:28px;}
/* Fixed-width truncation for a product name in the Inventory table - a
   long one used to blow the row out and push every other column around
   (Manish: "after a fixed length truncate product text"); title="" on
   the same element is the native browser tooltip, so hovering still
   shows the full name ("on mouse over show complete name") with no JS
   of its own needed for the hover itself. */
.list-name-truncate{display:inline-block;max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom;}
.list-thumb{width:32px;height:32px;border-radius:6px;object-fit:cover;background:var(--page);display:block;}
.badge{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px;white-space:nowrap;}
/* "Copy customer link" moved up next to the order number/status badges
   (Manish: "move copy customer link button near order number above just
   after status badge... match size of the copy customer link button
   with badge") - same pill shape/size as .badge itself, just as a real
   <button> (border, cursor, hover) since it's clickable and a plain
   .badge is not. */
.badge-link-btn{display:inline-block;font-size:11px;font-weight:600;padding:2px 9px;border-radius:20px;white-space:nowrap;border:1px solid var(--accent-2-wash);background:var(--accent-2-wash);color:#0e6b7a;cursor:pointer;vertical-align:1px;margin-left:4px;}
.badge-link-btn:hover{background:rgba(14,107,122,0.16);}
.badge.in-stock{color:var(--good-text);background:var(--good-wash);}
.badge.out-stock{color:var(--critical);background:var(--critical-wash);}
/* Product detail page's "better section for store wise stock" (Manish) -
   the total-across-stores summary line above the per-store table, and a
   dimmed treatment for a store row that's currently at 0 so the eye goes
   straight to the stores that actually have stock. */
.store-stock-summary{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-top:4px;}
.store-stock-table{margin-top:8px;}
.store-stock-row-empty td{opacity:.6;}

/* Fast Order's mandatory "pick a store before anything else" gate
   (Manish: "user can do things only if store is selected") - a single
   narrow, centered card in place of the whole form, not a full-width
   one, so it reads as a deliberate checkpoint rather than just another
   form panel. */
.fast-order-store-gate{max-width:420px;margin:40px auto 0;text-align:center;}
.fast-order-store-gate .field-label{text-align:left;}
.fast-order-store-gate p.muted{text-align:left;margin:0 0 4px;}

.list-panel-pager{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:12px;font-size:12.5px;color:var(--text-secondary);}
.ai-log-msg strong{color:var(--text-primary);font-weight:600;margin-right:4px;}

/* ===== Analytics page - daily pageview sparkline ===== */
.sparkline{display:flex;align-items:flex-end;gap:3px;height:70px;border-bottom:1px solid var(--gridline);padding-bottom:2px;}
.sparkline-bar{flex:1;background:var(--accent);border-radius:3px 3px 0 0;min-height:2px;opacity:0.85;}
.sparkline-bar:hover{opacity:1;}

/* ===== Top Customers / Top Products reports - the small inline bar
   under each row's revenue figure, sized relative to the #1 row (see
   renderTopCustomersReportTab()/renderTopProductsReportTab()'s own
   maxRevenue) so the ranking reads at a glance without a separate chart
   taking up the page. ===== */
.rank-bar-track{height:4px;border-radius:2px;background:var(--gridline);margin-top:4px;max-width:140px;}
.rank-bar-fill{height:100%;border-radius:2px;background:var(--accent);}

/* ===== Sales Forecast report - a history half + a forecast half of the
   same sparkline, sharing one scale (see renderForecastChart()'s own
   maxVal, computed across both) so the two halves stay honestly
   comparable across the divider. The forecast half is a diagonal hatch,
   not a second color - same value (revenue) projected forward, not a
   different series, so it stays in --accent and differentiates by
   texture instead (see this app's own convention of never color-coding
   two states of one metric as if they were two separate ones). ===== */
.forecast-chart-row{display:flex;align-items:stretch;}
.forecast-chart-half{flex:1;min-width:0;}
.forecast-chart-divider{width:1px;align-self:stretch;background:var(--gridline);margin:0 12px;}
.sparkline-bar.forecast{background:repeating-linear-gradient(135deg,var(--accent) 0,var(--accent) 3px,transparent 3px,transparent 6px);opacity:0.55;}
.sparkline-bar.forecast:hover{opacity:0.8;}
.forecast-chart-legend{display:flex;align-items:center;flex-wrap:wrap;}
.forecast-legend-swatch{display:inline-block;width:10px;height:10px;border-radius:2px;background:var(--accent);vertical-align:-1px;margin-right:5px;}
.forecast-legend-swatch.forecast{background:repeating-linear-gradient(135deg,var(--accent) 0,var(--accent) 2px,transparent 2px,transparent 4px);opacity:0.7;}

/* ===== Modal (site-creation type picker) - the only modal in this app,
   see promptCreateSite()'s own comment for why one was warranted here. ===== */
.awc-modal-backdrop{position:fixed;inset:0;background:rgba(11,11,11,0.45);display:flex;align-items:center;justify-content:center;z-index:1000;padding:16px;}
.awc-modal-card{background:var(--surface-1);border-radius:14px;padding:24px;width:100%;max-width:420px;max-height:calc(100vh - 32px);overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,0.3);}
/* Wider variant for content that needs real room - currently just the
   knowledge-base full-screen editor's 22-row textarea, which felt cramped
   at the default 420px card width. min(90vw, ...) keeps a side gutter on
   phone widths instead of touching the screen edges. */
.awc-modal-card-wide{max-width:min(90vw, 760px);}
/* Pairs fields two-to-a-row inside the wide modal card above (Site +
   WhatsApp number, per-template {{n}} variable inputs, the Advanced
   media/header params) instead of one long single column - see
   openStartWaConversationModal()'s and renderWaTemplateVarInputs()'s own
   comments. Each child carries its own label+input, so field-label's
   normal 14px top margin still applies per pair, not just per row. */
.wa-sc-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:0 16px;}
@media (max-width: 560px){.wa-sc-grid-2{grid-template-columns:1fr;}}
.awc-modal-title{font-size:16px;font-weight:700;margin-bottom:4px;}
.awc-modal-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:18px;}
/* Centered variants - the Log call/Send email/Send WhatsApp modals
   (Manish's own attached reference screenshots) read as a single focused
   form rather than app.js's usual left-aligned settings dialog, so their
   title and button row are centered instead of the default left/
   flex-end. */
.awc-modal-title-center{text-align:center;}
.awc-modal-actions-center{justify-content:center;}
/* Minimal contenteditable "rich text" toolbar for the Send email modal's
   Message field (renderCustEmailModalHtml()) - Bold/Italic/Underline/
   bullet+numbered list/Link/Clear, each just document.execCommand()
   against the editable div right below. Cosmetic only: a mailto: link
   (this app's own Send button - no server-side email sending exists)
   can only carry plain text, so formatting here doesn't survive into the
   opened email client - see wireCustEmailModalEvents()'s own docblock. */
.rte-toolbar{display:flex;gap:2px;flex-wrap:wrap;border:1px solid var(--gridline);border-bottom:none;border-radius:8px 8px 0 0;background:var(--page);padding:5px;}
.rte-toolbar button{appearance:none;border:none;background:transparent;color:var(--text-secondary);font-family:inherit;font-size:12.5px;font-weight:600;padding:5px 9px;border-radius:6px;cursor:pointer;}
.rte-toolbar button:hover{background:var(--gridline);}
.rte-editor{min-height:110px;max-height:260px;overflow-y:auto;border:1px solid var(--gridline);border-radius:0 0 8px 8px;padding:9px 12px;font-size:14px;background:var(--surface-1);color:var(--text-primary);}
.rte-editor:focus{outline:none;border-color:var(--accent);}
.rte-editor:empty:before{content:attr(data-placeholder);color:var(--text-muted);}
/* Orders/Bills tabs' own count (renderCustomerDetail()) - Manish: "its
   count and total for order and bill both should be visible in
   respective tabs only, remove from top panel". */
.tab-btn-count{font-size:11px;font-weight:700;color:var(--text-muted);margin-left:5px;}
.tab-btn.active .tab-btn-count{color:var(--text-secondary);}
.awc-type-choice{display:flex;flex-direction:column;gap:8px;}
.awc-type-choice-option{display:flex;align-items:flex-start;gap:10px;border:1px solid var(--gridline);border-radius:10px;padding:10px 12px;cursor:pointer;font-size:13.5px;}
.awc-type-choice-option input{margin-top:3px;}
.awc-type-choice-option.active{border-color:var(--accent);background:var(--accent-wash);}

/* ===== Orders tab / order detail (order-type sites, Orders.php) ===== */
.order-row{cursor:pointer;}
.order-row:hover td{background:var(--accent-wash);}
.badge.status-pending_review{color:#8a5a00;background:var(--warning-wash);}
.badge.status-confirmed,.badge.status-processing{color:#0e6b7a;background:var(--accent-2-wash);}
.badge.status-packed{color:#9a4b12;background:rgba(236,131,90,0.16);}
.badge.status-shipped{color:#8a5a1e;background:var(--accent-3-wash);}
.badge.status-delivered{color:var(--good-text);background:var(--good-wash);}
.badge.status-cancelled,.badge.status-rejected{color:var(--critical);background:var(--critical-wash);}
.badge.refill-status-none{color:var(--text-muted);background:var(--gridline);}
.badge.refill-status-pending{color:#8a5a00;background:var(--warning-wash);}
.badge.refill-status-done{color:var(--good-text);background:var(--good-wash);}
.badge.refill-status-overdue{color:var(--critical);background:var(--critical-wash);}
.badge.refill-status-today{color:#8a5a00;background:var(--warning-wash);}
/* Inventory - source badges (which of the five intake paths a product
   came from - see Inventory.php's own docblock) and the store-stock rows
   in the add/edit product modal, shown only on a 'multiple'-store firm. */
.badge.source-woocommerce{color:#6b3fa0;background:#f1e9fb;}
.badge.source-manual{color:var(--text-muted);background:var(--gridline);}
.badge.source-import{color:#0e6b7a;background:var(--accent-2-wash);}
.badge.source-google_sheet{color:#1a7a3c;background:var(--good-wash);}
.badge.source-erp{color:#9a4b12;background:rgba(236,131,90,0.16);}
/* Order source badges (Manish: "add a source option in orders and show
   on list and kanban") - one color per Orders::ORDER_SOURCE_LABELS
   channel, shown next to the order number on every List row and Kanban
   card (app.js's orderSourceBadgeHtml()). Deliberately its own
   order-source-* class family, NOT the source-* classes above - those
   already mean "which of the five catalog-sync paths a PRODUCT came
   from" and share plain names like source-manual/source-woocommerce;
   reusing them here would make an order's badge silently inherit
   whichever color a product badge happens to have. */
.badge.order-source-badge{margin-left:4px;vertical-align:1px;}
.badge.order-source-widget{color:#0e6b7a;background:var(--accent-2-wash);}
.badge.order-source-fast{color:#9a4b12;background:rgba(236,131,90,0.16);}
.badge.order-source-promotion{color:#b8449a;background:#fbe7f5;}
.badge.order-source-woocommerce{color:#6b3fa0;background:#f1e9fb;}
.badge.order-source-mobile{color:#1a7f4b;background:#e3f6ec;}
.badge.source-swil{color:#1d5fa8;background:rgba(29,95,168,0.12);}
.inv-store-stock-row{display:flex;align-items:center;gap:10px;margin-bottom:6px;}
.inv-store-stock-row label{flex:1;font-size:13px;margin:0;}
.inv-store-stock-row input{width:90px;margin:0;}
.inv-field-map-row{display:flex;align-items:center;gap:10px;margin-bottom:6px;}
.inv-field-map-row label{width:120px;flex:none;font-size:12px;color:var(--text-muted);margin:0;}
.inv-field-map-row input{flex:1;margin:0;}
/* The small "pick a field seen in the last Preview" dropdown beside each
   Field mapping input - see renderSwilMapPickOptions()/.swil-map-pick in
   app.js. Fixed width rather than flex:1 so it reads as a secondary,
   assistive control next to the real input, not a second equally-weighted
   field. */
.inv-field-map-row select.swil-map-pick{flex:none;width:180px;margin:0;font-size:12px;}
/* SWIL ERP's per-branch rows (Settings -> ERP Sync) - one per registered
   Store, each with its own Branch ID, Preview/sync buttons and status
   (see Inventory.php's own SWIL docblock on why this is per-branch). */
.inv-swil-branch-row{border-top:1px solid var(--gridline);padding:12px 0;}
.inv-swil-branch-row:first-of-type{border-top:none;padding-top:6px;}
.inv-swil-preview{margin-top:8px;}
.inv-swil-preview-json{max-height:260px;overflow:auto;background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:10px;font-size:12px;white-space:pre-wrap;word-break:break-word;margin:6px 0 0;}

.refill-date-overdue{color:var(--critical);font-weight:700;}
.refill-date-today{color:#8a5a00;font-weight:700;}
.awo-back-btn-lite{background:none;border:none;color:var(--text-secondary);font-size:12.5px;cursor:pointer;padding:0 0 12px;display:block;}
.order-detail-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:16px;}
.order-detail-total{font-size:18px;font-weight:700;}
.order-attachments{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px;}
.order-attachment-thumb{width:72px;height:72px;border-radius:10px;object-fit:cover;border:1px solid var(--gridline);}
/* Shared dot-timeline - colored dot, an icon+bold label with its own
   timestamp right-aligned on the same line, an optional muted "by
   <actor>" line, and an optional note shown as its own light box below
   (Manish's attached reference screenshot: icon+label/timestamp pair per
   row, no separate time column) - used by BOTH the order detail page's
   own Activity timeline (o.activity_log - Orders::logActivity()) and the
   customer detail page's own Timeline (c.activity -
   Customers::addActivity()), via the one shared renderTimelineRowsHtml()
   in app.js, so the two pages read as one consistent system instead of
   two separately-designed timelines. Newest entry first. No card/white-bg
   box around any of this - just a .section-divider above it, sitting
   directly in the page's own flow. The dot's color varies by entry type
   (ORDER_ACTIVITY_DOT_COLORS/CUSTOMER_ACTIVITY_DOT_COLORS in app.js)
   rather than a single accent color, so a long history is scannable by
   kind of event at a glance. */
.section-divider{border:none;border-top:1px solid var(--gridline);margin:18px 0 14px;}
.activity-date-group{margin-top:16px;}
.activity-date-group:first-child{margin-top:0;}
.activity-date-heading{font-size:11.5px;font-weight:700;color:var(--text-muted);text-transform:uppercase;letter-spacing:.03em;margin-bottom:8px;}
.activity-timeline{list-style:none;margin:0;padding:0;}
.activity-row{position:relative;display:flex;gap:9px;padding:0 2px 16px 3px;align-items:flex-start;}
.activity-row:last-child{padding-bottom:0;}
/* The connecting line - spans from just below this row's own dot down to
   the very bottom of this row's box (which is exactly where the next
   row's dot sits, thanks to the padding-bottom gap above). left:7.5px
   centers it under the dot: 3px row padding + half the 9px dot. */
.activity-row:not(:last-child)::before{
  content:'';position:absolute;top:15px;bottom:0;left:7.5px;width:1px;background:var(--gridline);
}
.activity-dot-col{flex:0 0 9px;display:flex;justify-content:center;}
.activity-dot{width:9px;height:9px;margin-top:5px;border-radius:50%;position:relative;z-index:1;}
.activity-dot-green{background:var(--good);}
.activity-dot-orange{background:var(--warning);}
.activity-dot-purple{background:var(--indigo);}
.activity-dot-blue{background:var(--accent-2);}
.activity-dot-amber{background:#8a5a1e;}
.activity-dot-grey{background:var(--text-muted);}
.activity-body{flex:1;min-width:0;padding-top:1px;}
.activity-head-row{display:flex;align-items:baseline;justify-content:flex-start;gap:8px;}
.activity-text{font-size:13.5px;line-height:1.4;display:flex;align-items:center;gap:6px;min-width:0;}
.activity-text strong{font-weight:600;}
.activity-text svg{width:13px;height:13px;flex-shrink:0;color:var(--text-secondary);}
.activity-timestamp{font-size:11.5px;color:var(--text-muted);white-space:nowrap;flex-shrink:0;padding-top:1px;min-width:38px;}
.activity-actor{font-size:12px;color:var(--text-muted);margin-top:2px;}
.activity-note{margin-top:6px;background:var(--page);border-radius:8px;padding:6px 10px;font-size:12.5px;color:var(--text-secondary);line-height:1.4;}

/* Customer Detail's action-icon row (renderCustomerDetail()) - Log a
   call/WhatsApp/Email/Add note are icon-only buttons in one line, just
   before Edit, then Delete right after Edit (Manish: "these should be
   icons just befor edit in same line" / "move delete button after
   edit... make delete button of same size") - same round pill, same
   size, as each other, colored the same as that action's own timeline
   dot (CUSTOMER_ACTIVITY_DOT_COLORS in app.js) so a logged activity
   visually ties back to the icon that logged it. Edit/Delete are neutral
   (Delete red) rather than activity-colored, since neither logs a timeline
   entry of its own. */
.cust-detail-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px;align-items:center;}
.cust-icon-btn{flex-shrink:0;width:34px;height:34px;border-radius:50%;border:none;display:flex;align-items:center;justify-content:center;background:var(--page);color:var(--text-secondary);cursor:pointer;padding:0;}
.cust-icon-btn svg{width:15px;height:15px;}
.cust-icon-btn:hover{filter:brightness(0.96);}
.cust-icon-btn-call{background:var(--accent-2-wash);color:#0e6b7a;}
.cust-icon-btn-whatsapp{background:var(--good-wash);color:var(--good-text);}
.cust-icon-btn-email{background:var(--accent-3-wash);color:#8a5a1e;}
.cust-icon-btn-edit{background:var(--accent-wash);color:var(--accent);}
.cust-icon-btn-delete{background:var(--critical-wash);color:var(--critical);}
/* Compact size variant for the same icon buttons when they sit inside a
   table row (Refill Reminders) instead of the customer detail page's own
   action bar - same colors, just smaller so they fit a table cell. */
.cust-icon-btn-sm{width:26px;height:26px;}
.cust-icon-btn-sm svg{width:13px;height:13px;}
/* Insights color for the promo card's action row below - reuses the
   same indigo the activity timeline's 'purple' dot already uses, since
   call/whatsapp/email/edit/delete are all spoken for by other actions. */
.cust-icon-btn-insights{background:var(--indigo-wash);color:var(--indigo);}
/* ---- Fast order screen (order-type sites, "New Order" nav + the chat
   slide-over) - see app.js's own "Fast order screen" section docblock. ---- */
.fast-order-screen{display:flex;flex-direction:column;gap:14px;min-height:0;}
.fast-order-tabs{display:flex;flex-wrap:wrap;gap:6px;}
.fast-order-tab{appearance:none;border:1px solid var(--gridline);background:var(--surface-1);border-radius:10px;padding:6px 12px;font-family:inherit;cursor:pointer;text-align:left;display:flex;flex-direction:column;gap:1px;min-width:110px;}
.fast-order-tab.active{border-color:var(--accent);background:var(--accent-wash);}
.fast-order-tab-num{font-size:10px;font-weight:700;color:var(--text-muted);}
.fast-order-tab.active .fast-order-tab-num{color:var(--accent);}
.fast-order-tab-name{font-size:13px;font-weight:600;color:var(--text-primary);}
.fast-order-tab-meta{font-size:11px;color:var(--text-muted);}
.fast-order-tab-add{align-items:center;justify-content:center;color:var(--accent);font-weight:700;min-width:56px;}
/* 3 columns - who (customer+billing/shipping), what (the item ledger,
   deliberately the widest since it's what an operator spends most of
   their time in), and the money/workflow rail (summary, payment, stage/
   status, assignment, attachments, notes) - see renderFastOrderScreen()'s
   own docblock in app.js. body.content-wide's own widened .shell (see
   above) is what actually gives this room to breathe; without it, 3
   columns at the old 1100px shell width would squeeze the ledger down to
   nothing. */
.fast-order-grid{display:grid;grid-template-columns:300px 1fr 320px;gap:14px;align-items:start;}
@media (max-width: 1300px){.fast-order-grid{grid-template-columns:1fr;}.order-detail-with-switch{flex-direction:column;}.order-switch-panel-wrap{flex:1 1 auto;width:100%;}.order-switch-panel{position:static;max-height:340px;}}
.fast-order-col{display:flex;flex-direction:column;gap:10px;min-width:0;}
.fast-order-col-rail{min-width:0;}
/* .card (the generic rule) sets its own margin-bottom:18px - left in place
   everywhere else, but here the parent .fast-order-col already spaces
   stacked cards with its own flex `gap` above, so the two were stacking
   (18px + 10px between every pair of rail cards) and were most of why this
   screen scrolled so much more than it needed to. Padding is also trimmed
   from the generic card's 20px/22px down to something sized for a dense
   sidebar of small controls rather than a full page section. */
.fast-order-card{position:relative;margin-bottom:0;padding:13px 15px;}
.fast-order-card .field-label{margin:10px 0 4px;}
.fast-order-card .field-label:first-child{margin-top:0;}
/* Order Detail's own "Pay via UPI" card (Manish: "lets build payment qr
   code on order detail page") - a compact rail card, same width as the
   summary panel it sits right above. The QR itself is a plain inline SVG
   (QRCodeLib.toSVG() in app.js) sized by this wrapper, not by the SVG's
   own markup, so it stays crisp at any size. */
.pay-upi-card{text-align:center;}
.pay-upi-amount{font-size:22px;font-weight:800;letter-spacing:-0.01em;margin:2px 0 10px;}
.pay-upi-qr{width:148px;height:148px;margin:0 auto 10px;}
.pay-upi-qr svg{display:block;width:100%;height:100%;}
.pay-upi-btn{display:inline-block;text-decoration:none;}
.pay-upi-download-link{display:block;margin-bottom:10px;font-size:12px;font-weight:700;color:var(--accent);background:none;border:none;cursor:pointer;font-family:inherit;padding:0;width:100%;}
.pay-upi-screenshot-link{display:block;margin-top:10px;font-size:12px;font-weight:700;color:var(--accent);}
/* One horizontal strip for Payment/Stage/Status/Assign, sitting above the
   3-column grid on both the fast-order screen and the order detail page -
   these are small controls, not real content, so folding them into a
   single row here (instead of each being its own stacked rail card) is
   most of what cut down the rail's own scroll height. */
.fast-order-toolbar{display:flex;flex-wrap:wrap;column-gap:28px;row-gap:10px;align-items:center;margin-bottom:14px;}
.fast-order-toolbar-group{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.fast-order-toolbar-group .muted{font-size:12px;white-space:nowrap;}
.fast-order-toolbar-group select, .fast-order-toolbar-group input{width:auto;min-width:110px;}
.fast-order-toolbar-status input{min-width:140px;}
.fast-order-actions-card{display:flex;flex-direction:column;gap:8px;}
.fast-order-customer-fields{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px;}
.fast-order-customer-fields input{margin:0;}
.fast-search-dropdown:not(:empty){border:1px solid var(--gridline);border-radius:10px;margin-top:4px;max-height:220px;overflow-y:auto;background:var(--surface-1);box-shadow:0 6px 18px rgba(11,11,11,0.08);}
.fast-search-row{display:flex;flex-direction:column;gap:1px;width:100%;text-align:left;appearance:none;border:none;background:none;padding:8px 12px;font-family:inherit;cursor:pointer;border-bottom:1px solid var(--gridline);}
.fast-search-row:last-child{border-bottom:none;}
.fast-search-row:hover, .fast-search-row.highlight{background:var(--accent-wash);}
.fast-search-row strong{font-size:13px;}
.fast-search-row .muted{font-size:11.5px;}
.fast-search-empty{padding:10px 12px;font-size:12.5px;color:var(--text-muted);}
.fast-search-add-new{color:var(--accent);font-weight:600;font-size:13px;flex-direction:row;align-items:center;}
.fast-order-cart-card{display:flex;flex-direction:column;}
.fast-order-cart-table{margin-bottom:6px;}
.fast-order-qty-cell{display:flex;align-items:center;justify-content:flex-start;gap:4px;white-space:nowrap;}
.fast-order-qty-input{width:46px;padding:4px 6px;text-align:center;margin:0;font-variant-numeric:tabular-nums;}
/* Manish: "qty, rate text is not aligned with its textbox" - both are
   type="number", and a browser's native up/down spin buttons eat a fair
   slice of an input this narrow (46px/78px), pushing the typed digits off
   to one side of the visible box instead of where text-align:center/right
   above says they should sit. Qty already has its own -/+ icon buttons
   right next to it (see the entry row's own markup), so the native
   spinner here is pure redundant clutter either way - same for Rate,
   which never needed one. Suppressed on both rather than adding
   box-sizing/line-height tweaks that wouldn't touch the real cause. */
.fast-order-qty-input::-webkit-outer-spin-button, .fast-order-qty-input::-webkit-inner-spin-button,
.fast-order-rate-input::-webkit-outer-spin-button, .fast-order-rate-input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0;}
.fast-order-qty-input, .fast-order-rate-input{-moz-appearance:textfield;}
/* An already-added row's Item cell - a plain borderless input, same look
   as the entry row's own #fast-item-search (.fast-order-item-entry-input
   below) so renaming a confirmed line feels like editing any other cell
   on this grid (qty/rate already worked this way) rather than opening a
   separate dialog. */
.fast-order-item-name-input{border:none;background:none;padding:8px 6px;margin:0;border-radius:0;}
.fast-order-item-name-input:focus{outline:none;border:none;box-shadow:inset 0 -2px 0 var(--accent);}

/* ---- Row-based ("Tally/Marg style") item entry, inside the same ledger
   table as the confirmed lines - see renderFastCartRowsHtml()/
   wireFastOrderEntryRowEvents() in app.js. No separate "search box above,
   cart below" anymore: the table's own last row IS the entry point, and
   stays there permanently (never removed, only ever reset back to blank
   after a pick), the same "one continuous grid, cursor always lands on
   the next blank line" shape those two products use for voucher entry. */
.fast-order-ledger-table th:first-child, .fast-order-ledger-table td.fast-order-row-num{width:26px;}
.fast-order-row-num{color:var(--text-muted);font-size:11.5px;font-variant-numeric:tabular-nums;text-align:center;}
.fast-order-ledger-table th:nth-child(3), .fast-order-ledger-table th:nth-child(4), .fast-order-ledger-table th:nth-child(5){text-align:left;}
.fast-order-amount-cell{text-align:left;font-variant-numeric:tabular-nums;white-space:nowrap;}
.fast-order-rate-cell{text-align:left;}
.fast-order-rate-input{width:78px;padding:4px 6px;text-align:left;margin:0;font-variant-numeric:tabular-nums;}
/* The entry row reads as "still open for typing" - a soft accent wash
   distinguishes it from the confirmed rows above it at a glance, the same
   way Tally/Marg tint or otherwise mark the grid's own active line. */
.fast-order-entry-row{background:var(--accent-wash);}
.fast-order-entry-row td{border-bottom:none;}
.fast-order-item-entry-cell{position:relative;padding:0 !important;}
.fast-order-item-entry-input{border:none;background:none;padding:8px;margin:0;border-radius:0;}
.fast-order-item-entry-input:focus{outline:none;border:none;box-shadow:inset 0 -2px 0 var(--accent);}
/* Overlay, not normal flow (unlike the customer-search dropdown above,
   which just pushes its own card taller) - this one sits inside a table
   cell, and letting it push the row taller would stretch every other cell
   in that same row along with it. */
.fast-order-item-entry-cell .fast-search-dropdown:not(:empty){position:absolute;left:0;right:0;top:100%;z-index:15;margin-top:2px;}
/* ---- Order Summary panel (subtotal/discount/delivery/grand total) -
   shared by the fast order screen and the order detail page's own Edit
   mode, see renderOrderSummaryPanelHtml() in app.js. ---- */
.order-summary-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:5px 2px;font-size:13px;}
.order-summary-row + .order-summary-row{border-top:1px solid var(--gridline);}
.order-summary-row input[type="number"]{width:96px;padding:5px 8px;text-align:right;margin:0;font-variant-numeric:tabular-nums;}
.order-summary-discount-controls{display:flex;gap:4px;align-items:center;}
.order-summary-discount-controls select{width:52px;padding:5px 4px;margin:0;}
.order-summary-grand-total{padding-top:10px;margin-top:4px;border-top:2px solid var(--gridline) !important;}
.order-summary-grand-total span:first-child{font-size:12.5px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;font-weight:600;}
.order-summary-grand-total span:last-child{font-size:26px;font-weight:800;letter-spacing:-0.02em;font-variant-numeric:tabular-nums;}
.fast-order-payment-toggle{display:inline-flex;border:1px solid var(--gridline);border-radius:999px;padding:2px;gap:2px;}
.fast-pay-btn{appearance:none;border:none;background:none;font-family:inherit;font-size:12.5px;font-weight:700;padding:6px 14px;border-radius:999px;cursor:pointer;color:var(--text-muted);}
.fast-pay-btn.active{background:var(--warning-wash);color:#8a5a00;}
.fast-pay-btn.paid.active{background:var(--good-wash);color:var(--good-text);}
.fast-order-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:12px;}
.fast-order-finalize-btn{font-size:15px;padding:11px 20px;}
.fast-order-shortlink{word-break:break-all;}

/* ---- Order Stage badges - purely derived from status, see
   Orders::STAGE_FOR_STATUS's own docblock and app.js's mirrored
   ORDER_STAGE_FOR_STATUS. Reuses the same pill shape as the existing
   status/refill badges (.badge) rather than inventing a second look. ---- */
.badge.stage-new{color:#3730a3;background:#eef2ff;}
.badge.stage-processing{color:#8a5a00;background:var(--warning-wash);}
.badge.stage-fulfilled{color:var(--good-text);background:var(--good-wash);}
.badge.stage-closed{color:var(--text-muted);background:var(--gridline);}
/* The independent Order status (Active/Delivered/Cancelled/Rejected) -
   see Orders::ORDER_STATUSES's own docblock for why this is separate
   from the detailed status- badges and stage- badges above. */
.badge.lifecycle-active{color:#0e6b7a;background:var(--accent-2-wash);}
.badge.lifecycle-delivered{color:var(--good-text);background:var(--good-wash);}
.badge.lifecycle-cancelled{color:var(--critical);background:var(--critical-wash);}
.badge.lifecycle-rejected{color:#8a5a00;background:var(--warning-wash);}
.order-lifecycle-toggle{display:flex;gap:4px;flex-wrap:wrap;}
.lifecycle-btn{appearance:none;border:none;background:var(--gridline);font-family:inherit;font-size:11.5px;font-weight:700;padding:5px 10px;border-radius:999px;cursor:pointer;color:var(--text-muted);}
.lifecycle-btn:disabled{opacity:0.6;cursor:default;}
/* Storefront's own On/Off toggle (renderStorefrontTab() in app.js) is the
   only place in the codebase that renders a bare .lifecycle-btn.active with
   no status-specific modifier class (every other user - Order status,
   Promotion status - pairs .active with a modifier like .lifecycle-btn-
   active/-delivered/-promo-active below) - so it toggled correctly but had
   no CSS rule to show it, meaning zero visual feedback on click (Manish:
   "on, off button not working properly"). This generic fallback covers it;
   the more specific compound rules below still win by source order/
   specificity for every other lifecycle button. */
.lifecycle-btn.active{background:var(--good-wash);color:var(--good-text);}
.lifecycle-btn-active.active{background:var(--accent-2-wash);color:#0e6b7a;}
.lifecycle-btn-delivered.active{background:var(--good-wash);color:var(--good-text);}
.lifecycle-btn-cancelled.active{background:var(--critical-wash);color:var(--critical);font-weight:700;}
.lifecycle-btn-rejected.active{background:var(--warning-wash);color:#8a5a00;}
/* Promotional catalog's own Status pill toggle (renderPromotionEditor())
   - same .lifecycle-btn base as Order status above, own color set so it
   matches the catalog card's own badge (renderPromotionCard()): green/
   amber/red for Active/On Hold/Ended rather than reusing lifecycle-btn-
   active's teal (that's specifically an ORDER status color). */
.lifecycle-btn-promo-active.active{background:var(--good-wash);color:var(--good-text);}
.lifecycle-btn-promo-on_hold.active{background:var(--warning-wash);color:#8a5a00;}
.lifecycle-btn-promo-ended.active{background:var(--critical-wash);color:var(--critical);}
.order-row-stage-select,.order-row-lifecycle-select{font-size:11.5px;font-weight:600;padding:3px 6px;border-radius:6px;border:1px solid var(--gridline);background:var(--surface-1);color:var(--text-primary);max-width:132px;}
.order-row-lifecycle-select.lifecycle-select-delivered{color:var(--good-text);border-color:var(--good-wash);}
.order-row-lifecycle-select.lifecycle-select-cancelled{color:var(--critical);border-color:var(--critical-wash);}
.order-row-lifecycle-select.lifecycle-select-rejected{color:#8a5a00;border-color:var(--warning-wash);}

/* Order detail page's own "more detail" Stage tracker - see
   renderStageTrackerHtml() in app.js. A left-to-right New -> Processing ->
   Fulfilled progress row (Closed is handled separately below, since a
   cancelled/rejected order isn't "further along" than a fulfilled one). */
.order-stage-tracker{display:flex;align-items:center;flex-wrap:wrap;gap:0;margin:7px 0 2px;font-size:11.5px;color:var(--text-muted);}
.order-stage-step{display:flex;align-items:center;gap:5px;}
.order-stage-step:not(:last-child)::after{content:'';display:inline-block;width:20px;height:1px;background:var(--gridline);margin:0 7px;}
.order-stage-step.done{color:var(--text-secondary);}
.order-stage-step.done .order-stage-dot{background:var(--accent);border-color:var(--accent);}
.order-stage-step.current{color:var(--text-primary);font-weight:700;}
.order-stage-step.current .order-stage-dot{background:var(--accent);border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-wash);}
.order-stage-dot{width:7px;height:7px;border-radius:50%;background:var(--surface-1);border:2px solid var(--gridline);flex-shrink:0;}
.order-stage-tracker-closed{gap:8px;margin:7px 0 2px;}

/* ---- Orders tab: List/Kanban view toggle - see renderOrdersTab() in
   app.js. Same pill-group shape as .fast-order-payment-toggle above, just
   two plain-text options instead of Paid/Unpaid. ---- */
.view-toggle{display:inline-flex;border:1px solid var(--gridline);border-radius:8px;padding:2px;gap:2px;}
.view-toggle-btn{appearance:none;border:none;background:none;font-family:inherit;font-size:12.5px;font-weight:600;padding:6px 12px;border-radius:6px;cursor:pointer;color:var(--text-muted);}
.view-toggle-btn.active{background:var(--accent-wash);color:var(--accent);}

/* ---- Orders tab: Kanban board - see renderOrdersKanbanHtml()/
   wireOrdersKanbanEvents() in app.js. Equal-width columns, one per
   Orders::STAGES bucket (New/Processing/Packing/Out for Delivery/
   Cancelled); cards are natively HTML5-draggable (no library),
   .dragging/.drag-over are the only two states that need their own look. ---- */
.kanban-board{display:flex;gap:10px;align-items:flex-start;overflow-x:auto;padding-bottom:4px;}
.kanban-column{flex:1 1 0;min-width:200px;background:var(--page);border:1px solid var(--gridline);border-radius:12px;padding:8px;display:flex;flex-direction:column;gap:6px;}
.kanban-column-head{display:flex;align-items:center;justify-content:space-between;font-size:12.5px;font-weight:700;color:var(--text-secondary);padding:2px 4px;}
.kanban-column-count{color:var(--text-muted);font-weight:600;font-variant-numeric:tabular-nums;}
.kanban-column-body{display:flex;flex-direction:column;gap:5px;min-height:40px;}
.kanban-column-body.drag-over{background:var(--accent-wash);border-radius:8px;}
/* Two rows total now (Manish: "make card height half of the existing
   cards") - order#/source-badge/customer name share the top row, and
   status/amount/the two mark-delivered-cancelled icons share the
   bottom, instead of each getting its own row. */
.kanban-card{background:var(--surface-1);border:1px solid var(--border);border-radius:8px;padding:6px 8px;cursor:grab;display:flex;flex-direction:column;gap:2px;line-height:1.3;}
.kanban-card.dragging{opacity:0.4;}
.kanban-card-top{display:flex;align-items:center;justify-content:space-between;gap:6px;font-size:12.5px;}
.kanban-card-top-left{display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden;}
.kanban-card-customer{font-size:11px;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1 1 auto;}
.kanban-card-bottom{display:flex;align-items:center;justify-content:space-between;gap:6px;font-size:11.5px;font-weight:600;font-variant-numeric:tabular-nums;}
.kanban-card-bottom-left{display:flex;align-items:center;gap:6px;min-width:0;overflow:hidden;}
.kanban-view-all{align-self:center;font-size:12px;}

/* ---- List row / Kanban card quick-action icon buttons - "Mark
   delivered"/"Mark cancelled" without opening the detail page (see
   markOrderDelivered()/markOrderCancelled() in app.js). Builds on the
   shared .icon-btn base (transparent, muted - see that class's own rule
   further down) with a bigger, more clickable hit area than that class's
   own inline-marker default, plus a colored hover per action so the two
   read as opposite/paired at a glance. ---- */
.order-row-quick-actions,.kanban-card-actions{display:flex;gap:4px;}
.order-row-quick-actions .icon-btn,.kanban-card-actions .icon-btn{width:28px;height:28px;border-radius:8px;}
.order-row-quick-actions .icon-btn svg,.kanban-card-actions .icon-btn svg{width:16px;height:16px;}
/* Kanban's own actions are inline in the bottom row now, not a separate
   bordered row below the fold (see .kanban-card's own docblock) - and a
   touch smaller than the List view's, since cards are already tight on
   space (Manish: "make cards compact so that we can view more orders in
   one go" / "make card height half of the existing cards"). Same
   specificity as the shared rule above, later in the source, so it wins
   for Kanban only. */
.kanban-card-actions{flex-shrink:0;gap:2px;}
.kanban-card-actions .icon-btn{width:20px;height:20px;}
.kanban-card-actions .icon-btn svg{width:12px;height:12px;}
.icon-btn-success:hover{background:var(--good-wash);color:var(--good-text);}
.icon-btn-danger:hover{background:var(--critical-wash);color:var(--critical);}

/* ---- Order Detail: collapsible "switch orders" rail - see
   renderOrderSwitchPanelHtml()/refreshOrderSwitchPanel() in app.js. The
   wrap div is always in the DOM; :empty collapsing it (rather than a
   height/transform transition) matches this app's existing "instant
   show/hide, no panel animations" convention (see .order-split-panel's own
   comment). ---- */
/* left padding clears the vertical .order-switch-fixed-btn strip pinned
   at the page's own left edge (left:68px, ~34px wide) - see that class's
   own docblock below for the button redesign this pairs with. Scoped to
   just this wrap (not #viewContent generally) since the fixed button only
   ever shows on this one page. */
.order-detail-with-switch{display:flex;gap:14px;align-items:flex-start;padding-left:40px;}
/* The page title above this (#pageTitle/.page-heading - shared shell
   markup every other tab also uses) and the "Back to orders" link sit
   OUTSIDE .order-detail-with-switch, so without this they never picked up
   its 40px clearance and read as misaligned against the order content
   below them. Scoped to body.order-detail-open (toggled in app.js's
   render(), same condition updateOrderSwitchFixedBtn() uses to show that
   fixed tab) so every other tab's page title is untouched. */
body.order-detail-open .page-heading{padding-left:40px;}
#order-detail-back-btn,#customer-detail-back-btn,#product-detail-back-btn{margin-left:40px;}
.order-switch-panel-wrap{flex:0 0 260px;}
.order-switch-panel-wrap:empty{display:none;}
.order-detail-main{flex:1;min-width:0;}
.order-switch-panel{background:var(--surface-1);border:1px solid var(--border);border-radius:14px;padding:14px;position:sticky;top:14px;max-height:calc(100vh - 100px);display:flex;flex-direction:column;}
.order-switch-panel-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;}
/* The head and the list below both carry their own margin-bottom, but the
   #order-switch-search box sandwiched between them (plain shared input,
   qv,select,textarea{} - no margin of its own) had none, so the order list
   sat flush against the bottom of the search box with no breathing room. */
#order-switch-search{margin-bottom:8px;}
#customer-switch-search{margin-bottom:8px;}
#product-switch-search{margin-bottom:8px;}
.order-switch-panel-list{overflow-y:auto;display:flex;flex-direction:column;gap:6px;margin-bottom:8px;}
.order-switch-row{appearance:none;border:1px solid transparent;background:none;text-align:left;border-radius:8px;padding:7px 8px;font-family:inherit;cursor:pointer;display:flex;flex-direction:column;gap:2px;}
.order-switch-row:hover{background:var(--page);}
.order-switch-row.active{background:var(--accent-wash);border-color:var(--accent);}
.order-switch-row-top{display:flex;align-items:center;justify-content:space-between;gap:6px;font-size:12.5px;}
/* Product Detail's own switch rows/image box - see renderProductSwitchPanelHtml()/
   renderProductDetail() in app.js (Manish: "maintain gap in switch product div
   data" / "create better UI to show product image"). Scoped to this one wrap
   so orders/customers' own switch rows are untouched. */
#product-switch-panel-wrap .order-switch-panel-list{gap:10px;}
#product-switch-panel-wrap .order-switch-row{gap:4px;padding:9px 10px;}
.prod-detail-image{width:96px;height:96px;border-radius:12px;background:var(--page);border:1px solid var(--gridline);display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0;}
.prod-detail-image img{width:100%;height:100%;object-fit:cover;display:block;}
.prod-detail-image svg{width:28px;height:28px;color:var(--text-muted);}
.prod-detail-field{margin-top:14px;}


/* The "Switch order" tab pinned flush against the left nav bar - see
   ensureOrderSwitchFixedBtn()/updateOrderSwitchFixedBtn() in app.js.
   position:fixed (not sticky) so it stays put regardless of how far the
   operator has scrolled the Order Detail page, and left:68px matches
   .sidebar's own width exactly so it reads as an extension of the nav
   rail rather than a floating button. top:50%+translateY(-50%) centers it
   vertically in the viewport (Manish's own request: "switch order shuld
   be a vertical button in center of the screen") rather than pinning it
   near the top - since that's independent of the firm-incomplete banner's
   own height (unlike the old top:76px/116px pair this replaced), there's
   no separate banner-visible override needed any more. Hidden by default
   (app.js toggles display via updateOrderSwitchFixedBtn() whenever it
   isn't relevant, e.g. on any other tab) - display:flex (not block) once
   shown so its label centers both ways inside the strip. The label text
   itself lives in a child span (.order-switch-fixed-btn-label) rotated
   with writing-mode rather than rotating this whole button, so the
   border-radius/shadow stay put on the right-hand edge instead of
   flipping to the wrong side. */
.order-switch-fixed-btn{
  position:fixed;left:68px;top:50%;transform:translateY(-50%);z-index:25;display:none;
  align-items:center;justify-content:center;min-height:140px;
  border:1px solid var(--gridline);border-left:none;
  background:var(--surface-1);color:var(--text-secondary);
  font-family:inherit;font-size:12.5px;font-weight:700;letter-spacing:0.02em;
  padding:16px 9px;border-radius:0 12px 12px 0;cursor:pointer;
  box-shadow:2px 2px 10px rgba(11,11,11,0.06);
}
.order-switch-fixed-btn-label{writing-mode:vertical-rl;transform:rotate(180deg);white-space:nowrap;}
.order-switch-fixed-btn:hover{background:var(--page);}
.order-switch-fixed-btn.active{background:var(--accent);border-color:var(--accent);color:#fff;}

/* ---- Billing/Shipping address panel - see renderBillingShippingPanelHtml(). ---- */
.fast-order-card input[type="checkbox"]{margin:0;}
/* Address is a textarea (multi-line - flat/apartment/street on their own
   lines) rather than a single-line input, but still sized to roughly 2
   lines by default (its rows="2" attribute already does that) so it
   doesn't dominate the rail - resize:vertical lets the operator drag it
   taller for a longer address. */
.address-textarea{resize:vertical;}

/* ---- Attachments panel's own upload control - a plain <label> wrapping
   a hidden file input (no separate "Choose file" system button visible),
   same small-secondary-button look as the rest of this screen's own
   buttons rather than the browser's default file-picker chrome. ---- */
.attachment-upload-btn{position:relative;margin-top:8px;display:inline-block;cursor:pointer;}
.attachment-upload-btn input[type="file"]{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;}

/* The chat-integrated "+ Create order" split panel now lives with the
   .chat-order-split/.order-split-panel rules up near .chat-shell (see
   above) - it's a 50/50 flex sibling of the chat thread, not a
   position:absolute overlay layered on top of it anymore. */

/* ---- Promotions tab (order-type sites) - see Promotions.php ---------- */
.badge.status-promo_lead{color:#0e6b7a;background:var(--accent-2-wash);}

.promo-admin-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px;margin-top:14px;}
.promo-admin-card{border:1px solid var(--gridline);border-radius:12px;overflow:hidden;background:var(--surface-1);display:flex;flex-direction:column;}
.promo-admin-card-media{aspect-ratio:16/9;background:var(--page);}
.promo-admin-card-media img{width:100%;height:100%;object-fit:cover;display:block;}
.promo-admin-card-media.placeholder{background:linear-gradient(135deg,var(--page),var(--gridline));}
.promo-admin-card-body{padding:12px 14px 14px;display:flex;flex-direction:column;gap:6px;}
.promo-admin-card-title{font-weight:700;font-size:14px;}
.promo-admin-card-actions{display:flex;gap:8px;margin-top:6px;}

.promo-link-row{display:flex;gap:6px;margin-top:6px;}
.promo-link-row input{flex:1;min-width:0;font-size:12px;color:var(--text-secondary);background:var(--page);border:1px solid var(--gridline);border-radius:8px;padding:6px 8px;}

.promo-design-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px;margin:6px 0 14px;}
.promo-design-card{display:flex;flex-direction:column;gap:4px;border:1px solid var(--gridline);border-radius:10px;padding:10px;cursor:pointer;}
.promo-design-card:has(input:checked){border-color:var(--accent);background:var(--accent-wash);}
.promo-design-card input{margin:0 0 2px;}
.promo-design-swatch{display:block;height:34px;border-radius:6px;background:linear-gradient(135deg,var(--gridline),var(--page));}
.promo-design-swatch-classic{background:linear-gradient(135deg,#f3f3f0,#ffffff);border:1px solid var(--gridline);}
.promo-design-swatch-bold{background:linear-gradient(135deg,#161615,#3a3a38);}
.promo-design-swatch-minimal{background:#ffffff;border:1px solid var(--gridline);}
.promo-design-swatch-festive{background:linear-gradient(135deg,#fff1de,#fecda5);}
.promo-design-swatch-magazine{background:linear-gradient(135deg,#eee7da,#c9beac);}
.promo-design-swatch-trust{background:linear-gradient(135deg,#dce8f2,#ffffff);border:1px solid var(--gridline);}
.promo-design-swatch-feed{background:linear-gradient(135deg,#e4f3ef,#ffffff);border:1px solid var(--gridline);}
.promo-design-swatch-spotlight{background:linear-gradient(135deg,#f2f0fc,#e2def9);}
.promo-design-swatch-landing{background:linear-gradient(135deg,#fbf8f4,#e7eee2);border:1px solid var(--gridline);}
.promo-design-swatch-compact{background:linear-gradient(135deg,#f3f4f2,#e1e3de);border:1px solid var(--gridline);}
.promo-design-swatch-storefront-modern{background:linear-gradient(135deg,#eaf1fb,#ffffff);border:1px solid var(--gridline);}
.promo-design-swatch-storefront-neighborhood{background:linear-gradient(135deg,#fbf2e4,#f0d9b5);}
.promo-design-swatch-storefront-clinical{background:linear-gradient(135deg,#0f2d45,#5a7d99);}
.promo-design-swatch-storefront-boutique{background:linear-gradient(135deg,#faf8f5,#d8d3c8);border:1px solid var(--gridline);}
.promo-design-swatch-storefront-bazaar{background:linear-gradient(135deg,#ffc466,#ff7878,#a06eff);}

.promo-conversion-options{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:10px;margin:6px 0 14px;}
.promo-radio-option{display:flex;align-items:flex-start;gap:8px;border:1px solid var(--gridline);border-radius:10px;padding:10px;cursor:pointer;}
.promo-radio-option:has(input:checked){border-color:var(--accent);background:var(--accent-wash);}
.promo-radio-option input{margin-top:3px;}

.mdm-group{border-bottom:1px solid var(--gridline);padding-bottom:14px;margin-bottom:14px;}
.mdm-group:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0;}

.promo-editor-product-row,.promo-product-search-row{display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--gridline);}
.promo-editor-product-row:last-child,.promo-product-search-row:last-child{border-bottom:none;}
.promo-editor-product-thumb{width:40px;height:40px;border-radius:8px;object-fit:cover;flex-shrink:0;background:var(--page);}
/* Shown for a catalog product with no image_url, and (via the
   .promo-media-broken class app.js's onerror handler adds) for one
   whose image_url 404s - same generic "no photo" glyph either way, same
   icon promo-facing promo.css uses for the customer-facing card so an
   admin sees the exact same fallback their customers will. Hardcoded
   grey rather than a theme var since it has to read on both the light
   and dark gradient this sits on. */
.promo-editor-product-thumb.placeholder,img.promo-editor-product-thumb.promo-media-broken{
  background-color:var(--page);
  background-image:linear-gradient(135deg,var(--page),var(--gridline)),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239a9992' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='18' height='18' rx='2'/%3E%3Ccircle cx='8.5' cy='8.5' r='1.5'/%3E%3Cpath d='M21 15l-5-5L5 21'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:center;
  background-size:cover, 18px 18px;
}
.promo-editor-product-info{flex:1;min-width:0;font-size:13px;}
.promo-price-input{width:150px;flex-shrink:0;}
.promo-product-search-results{max-height:220px;overflow:auto;margin-top:6px;border-top:1px solid var(--gridline);}

/* A custom/"not in inventory" row (see Promotions.php's own docblock) -
   its name/note are typed directly here rather than picked from a
   catalog search result, so it gets two stacked inputs instead of the
   read-only <strong>/muted-price a catalog row shows, and a dashed
   placeholder thumb so it visually reads as "not a real SKU yet". */
.promo-editor-product-row-custom .promo-editor-product-info{display:flex;flex-direction:column;gap:4px;}
.promo-editor-product-row-custom .promo-custom-name-input,.promo-editor-product-row-custom .promo-custom-note-input{width:100%;}
.promo-editor-product-row-custom .promo-custom-note-input{font-size:12px;}
.promo-editor-product-thumb.placeholder.custom{background:repeating-linear-gradient(135deg,var(--page),var(--page) 6px,var(--gridline) 6px,var(--gridline) 12px);}

/* The photo controls on a custom row's own thumb
   (renderPromotionEditorProductsListHtml()) - a small always-there
   camera badge (bottom-right, opens the file picker its own hidden
   <input> wraps) plus an "x" badge (top-right) that only renders once
   the row actually has a photo. Absolutely positioned over the same
   40x40 thumb every other product row uses, so a custom row's overall
   layout never shifts whether it has a photo yet or not. */
.promo-editor-custom-thumb-wrap{position:relative;flex-shrink:0;width:40px;height:40px;}
.promo-editor-custom-thumb-wrap .promo-editor-product-thumb{width:40px;height:40px;}
.promo-editor-thumb-edit{
  position:absolute;bottom:-4px;right:-4px;width:18px;height:18px;border-radius:50%;
  background:var(--accent);color:#fff;display:flex;align-items:center;justify-content:center;
  cursor:pointer;box-shadow:0 0 0 2px var(--surface-1);
}
.promo-editor-thumb-edit svg{width:11px;height:11px;}
.promo-editor-thumb-edit input[type="file"]{position:absolute;width:1px;height:1px;opacity:0;overflow:hidden;}
.promo-editor-thumb-remove{
  position:absolute;top:-6px;right:-6px;width:16px;height:16px;border-radius:50%;border:none;
  background:#a5292c;color:#fff;display:flex;align-items:center;justify-content:center;
  padding:0;box-shadow:0 0 0 2px var(--surface-1);
}
.promo-editor-thumb-remove svg{width:10px;height:10px;}

.promo-banner-preview{display:flex;align-items:center;gap:10px;margin-top:6px;}

/* ---- Fast-order "Finalize" review/print modal - see
   renderFastOrderReviewModalHtml() in app.js. A full-screen overlay on top
   of the (still-mounted, unchanged) fast-order screen; the order isn't
   actually saved until Save is clicked inside it. ---- */
.fast-order-review-overlay{position:fixed;inset:0;background:rgba(11,11,11,0.45);z-index:1000;display:flex;align-items:center;justify-content:center;padding:16px;}
.fast-order-review-modal{background:var(--surface-1);border-radius:14px;max-width:560px;width:100%;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,0.25);}
.fast-order-review-print{overflow-y:auto;padding:24px 26px;}
.fast-order-review-shop{font-size:16px;font-weight:700;}
.fast-order-review-title{margin:2px 0 16px;font-size:13px;font-weight:600;color:var(--text-secondary);text-transform:uppercase;letter-spacing:0.04em;}
.fast-order-review-section{margin-bottom:14px;font-size:13.5px;}
.fast-order-review-section strong{display:block;font-size:12px;font-weight:600;color:var(--text-secondary);margin-bottom:3px;}
.fast-order-review-items{margin-bottom:14px;}
.fast-order-review-items th:nth-child(2), .fast-order-review-items th:nth-child(3), .fast-order-review-items th:nth-child(4),
.fast-order-review-items td:nth-child(2), .fast-order-review-items td:nth-child(3), .fast-order-review-items td:nth-child(4){text-align:right;}
.fast-order-review-totals{margin-bottom:14px;}
/* Print (left) and Save/Cancel (right) - a deliberate split so Print reads
   as "just look at/hand over a copy" while the actual commit action sits
   grouped with its own Cancel on the opposite side. */
.fast-order-review-actions{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:14px 20px;border-top:1px solid var(--gridline);}
.fast-order-review-actions-right{display:flex;gap:8px;}
@media (max-width: 480px){.fast-order-review-actions{flex-direction:column;align-items:stretch;}.fast-order-review-actions-right{justify-content:flex-end;}}

/* The "order created" success modal's tracking-link textbox - see
   renderOrderCreatedModalHtml(). Plain readonly textarea (not an
   <input>) so a longer message/link still wraps and stays fully visible
   rather than scrolling off to one side. */
.order-created-textbox{
  width:100%;resize:none;border:1px solid var(--gridline);border-radius:10px;
  padding:10px 12px;font-family:inherit;font-size:13px;line-height:1.5;
  color:var(--text-primary);background:var(--page);
}
/* Printing only the order itself, not the rest of the app/overlay chrome
   around it - the standard "hide everything, then un-hide just the one
   thing" trick. */
@media print{
  body *{visibility:hidden;}
  #fast-order-review-print, #fast-order-review-print *,
  #report-print-area, #report-print-area *{visibility:visible;}
  #fast-order-review-print{position:absolute;left:0;top:0;width:100%;padding:0;}
  #report-print-area{position:absolute;left:0;top:0;width:100%;padding:16px;}
  .report-toolbar, .hub-subnav{display:none !important;}
  .report-print-header{display:block !important;margin-bottom:16px;}
}
.promo-banner-preview img{width:80px;height:52px;object-fit:cover;border-radius:8px;border:1px solid var(--gridline);}
