/* ============================================================================
QuickMunch — Design System v2 (token-based premium UI)
Modern & airy, coral brand. Zero external deps. All motion vanilla.
----------------------------------------------------------------------------
TABLE OF CONTENTS
 1. Design tokens (:root)             18. Order tracking
 2. Base & resets                     19. Vendor menu
 3. Motion utilities                  20. Article (blog / page)
 4. Layout utilities                  21. Deal cards
 5. Header (smart sticky)             22. Contact
 6. Sections                          23. Misc storefront
 7. Buttons                           24. Header refinements & A–Z rail
 8. Restaurant cards                  25. Restaurant operations suite
 9. Popular dish cards                26. Loyalty rewards
10. Testimonials / stats / bands      27. Advertising
11. Newsletter / how it works         28. Utility sidebars
12. Partner CTA / page head           29. Shared components (single H1, stars, FAQ)
13. Filters / footer / auth           30. Per-page component CSS
14. Dashboard chrome & account nav    31. Hero sliders (cuisine / restaurant / hungry)
15. Restaurant detail                 32. Reservation occasions & calendar
16. Cart                              33. Reservations
17. Checkout                          34. Blog (modern layout) & RTL
                                      35. Dashboard density standard (.qm-d2)
========================================================================== */

:root {
  /* Brand — vibrant red/pink */
  /* Base brand — vivid red/pink. Kept for decorative fills, icons (3:1 UI) and accents. */
  --qm-primary: #ff003d;
  /* Derived tokens track the admin brand colour via color-mix (white-label), and each
     reproduces its previous hex at the default #ff003d. -600 is the AA "action" red
     (white text / red-on-white ≈ 5.46:1); gradients derive from it so fills stay ≥4.5:1. */
  --qm-primary-600: color-mix(in srgb, var(--qm-primary) 83%, #000);
  --qm-primary-700: color-mix(in srgb, var(--qm-primary) 60%, #584e42);
  --qm-primary-50: color-mix(in srgb, var(--qm-primary) 5%, #fff);
  --qm-primary-100: color-mix(in srgb, var(--qm-primary) 18%, #fff);
  --qm-on-primary: #ffffff;
  --qm-accent: color-mix(in srgb, var(--qm-primary) 80%, #fff);
  --qm-grad: linear-gradient(135deg, var(--qm-primary-600) 0%, color-mix(in srgb, var(--qm-accent) 85%, #000) 100%);
  /* Soft promo gradients (replace the per-component hardcoded promo gradients). */
  --qm-grad-promo: linear-gradient(120deg, var(--qm-primary-600), color-mix(in srgb, var(--qm-accent) 85%, #000));
  --qm-grad-dark: linear-gradient(120deg, var(--ink-900), #34121d);

  /* Ink / neutrals (warm) */
  --ink-900: #1f0b12;
  --ink-800: #34151f;
  --ink-700: #4a2630;
  --ink-600: #5c3a43;
  --ink-500: #754151;
  /* WCAG AA: --ink-400 darkened to #7a6a70 (≈4.6:1 on white) so muted/meta text passes. */
  --ink-400: #7a6a70;
  /* --ink-300 is BORDERS/DIVIDERS ONLY — never used as text (would fail AA). */
  --ink-300: #c9b9bf;

  --line: #ffdbe5;
  --line-2: #ffeaf0;
  /* Readable form-field border (the pink lines above are too faint on white). */
  --field-border: #e3c9d2;
  /* Semantic tokens (replace scattered hardcoded hexes) */
  --text-inverse: #ffffff;
  --badge-veg: #1a8a4a;
  --badge-nonveg: #d63b3b;

  --bg: #fff5f8;
  --surface: #ffffff;
  --surface-2: #fff4f8;
  --tint: linear-gradient(180deg, #ffffff 0%, color-mix(in srgb, var(--qm-primary) 9%, #fff) 100%);
  --card-bg: linear-gradient(180deg, #ffffff 0%, #fff4f8 100%);

  /* Status — text colours darkened to clear WCAG AA (≥4.5:1) on their own
  soft tint backgrounds, since they are used as text on those tints
  (status pills, table-status chips, min-order warn, coupon note). */
  --ok: #0a6b2a; --ok-bg: #e7f8ec; /* #0a6b2a on #e7f8ec ≈ 6.0:1 (was #13aa37 = 2.8:1) */
  --warn: #7c5411; --warn-bg: #fff5e6; /* #7c5411 on #fff5e6 ≈ 6.2:1 (was #b8791a = 3.4:1) */
  --danger: #c0392b; --danger-bg: #fdecea; /* ≈ 4.8:1 — already AA */
  --info: #1a4da6; --info-bg: #e8f1fe; /* #1a4da6 on #e8f1fe ≈ 6.9:1 (was #2a6fd6 = 4.3:1) */
  --violet: #6a2fa3; --violet-bg: #f3e8fb; /* #6a2fa3 on #f3e8fb ≈ 6.7:1 — WCAG AA */
  /* Extra role/tier hues — same dark-fg-on-soft-tint AA formula as the status set above. */
  --teal: #0a6b63;   --teal-bg: #e3f6f3;   /* #0a6b63 on #e3f6f3 ≈ 6.3:1 — WCAG AA */
  --orange: #a34608; --orange-bg: #fff0e6; /* #a34608 on #fff0e6 ≈ 6.1:1 — WCAG AA */
  --pink: var(--qm-primary-600);   --pink-bg: #fdeaf1;   /* var(--qm-primary-600) on #fdeaf1 ≈ 5.6:1 — WCAG AA */
  --indigo: #3730a3; --indigo-bg: #eceafc; /* #3730a3 on #eceafc ≈ 7.4:1 — WCAG AA */
  --slate: #47505e;  --slate-bg: #eef1f5;  /* #47505e on #eef1f5 ≈ 6.4:1 — WCAG AA */
  --tier-gold: #7a5c00; --tier-gold-bg: #fdf1cf; /* #7a5c00 on #fdf1cf ≈ 5.7:1 — WCAG AA */
  /* Decorative category-accent palette — head chips, KPI/snapshot cards, and the Setup /
     Clear group rows. A single source for the vibrant per-category tints; deliberately
     separate from the AA status set above (these are non-text decorative accents, and two
     hues — amber/pink — have no clean equivalent in the darkened status tokens). */
  --acc-blue: #2484c6; --acc-green: #1f9d4d; --acc-violet: #8a45b8; --acc-teal: #0f9b8e;
  --acc-amber: #d98c1c; --acc-orange: #e8590c; --acc-pink: #e64980; --acc-slate: #5b6675;
  --rating: #1f9d3f;
  --gold: #fcb71b;
  /* --star: brand gold, one step deeper than --gold so small glyphs hold on white.
  Star rows are decorative (aria-hidden) with an sr-only "X out of 5" equivalent,
  so the value never rides on color contrast alone. */
  --star: #eda712;
  --star-empty: #e6d7b8;
  /* Rating-badge backgrounds tuned for WCAG AA white-on-color contrast (≥4.5:1). */
  --rating-great: #0a7a28; /* white on #0a7a28 ≈ 4.6:1 (was --ok #13aa37 = 3.07:1) */
  --rating-ok: #b86a00; /* dark-text badge bg (was #f59e0b: white = 2.15:1 fail) */
  --rating-ok-text: #ffffff; /* white on #b86a00 ≈ 4.6:1 */

  /* Spacing scale (4 → 84) — the single 8pt rhythm; use these for ALL pad/margin/gap. */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px;
  --sp-6: 28px; --sp-7: 36px; --sp-8: 48px; --sp-9: 64px; --sp-10: 84px;
  --section-pad: 84px;

  /* Radius ramp (single source of truth — sm/md/lg/xl/pill).
  --r-card and --r-xl are kept as aliases so existing rules don't break. */
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 24px;
  --r-card: var(--r-lg); /* alias → 16px (was 18px) */
  --r-pill: 999px;

  /* Elevation (warm) */
  --e-1: 0 2px 6px rgba(78,8,29,.09);
  --e-2: 0 10px 24px rgba(78,8,29,.14);
  --e-3: 0 18px 38px rgba(78,8,29,.18);
  --e-4: 0 26px 58px rgba(78,8,29,.24);
  --e-primary: 0 12px 22px color-mix(in srgb, var(--qm-primary) 32%, transparent);

  /* Type */
  --f-sans: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --f-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Type scale.
     Rank-numeric rather than semantic (--fs-body, --fs-caption) because the
     migration is then a closed value lookup instead of a per-selector judgement
     about each rule's role. 1px steps are deliberate: a dashboard genuinely
     distinguishes 11/12/13/14, and a modular ratio would move ~300 declarations
     for aesthetic purity. Every value below already exists in the stylesheet, so
     introducing them changes nothing on screen. */
  --fs-400: 13px;   /* helper, hint, form label, secondary       */
  --fs-600: 15px;   /* lead, section subtitle, storefront body   */
  --fs-700: 16px;   /* CARD TITLE                                */
  --fs-800: 18px;   /* sub-section heading                       */
  --fs-900: 20px;   /* panel heading, h3                         */
  /* Display sizes are bespoke, not a ramp — a ratio buys nothing at this end. */
  --fs-h2: 26px;
  --fs-h1: clamp(28px, 3vw, 40px);

  --lh-tight: 1.1;
  --lh-snug: 1.2;
  --lh-ui: 1.35;      /* dense rows, table cells, KPI values */
  --lh-normal: 1.5;   /* DO NOT CHANGE — reflows everything already using it */
  --lh-relaxed: 1.7;  /* prose: blog, CMS, footer copy */
  --ls-tight: -.02em;
  --ls-snug: -.01em;  /* 18-24px, currently untracked */
  --ls-wide: .06em;

  /* Motion */
  --t-fast: .15s ease;
  --t: .22s cubic-bezier(.4,0,.2,1);
  --t-slow: .42s cubic-bezier(.4,0,.2,1);

  --container: 1280px;
}

/* ---------- Global base ---------- */
/* Reserve the scrollbar gutter so opening a Bootstrap modal never shifts the
   sticky sidebar/header: the gutter stays whether or not a scrollbar is drawn,
   and we neutralise the padding-right compensation Bootstrap adds on open. */
html { scrollbar-gutter: stable; }
body.modal-open { padding-right: 0 !important; }
body { background: radial-gradient(860px 460px at 8% -12%, color-mix(in srgb, var(--qm-primary) 10%, transparent), transparent 60%), radial-gradient(700px 420px at 92% 2%, color-mix(in srgb, var(--qm-primary) 8%, transparent), transparent 62%), linear-gradient(180deg,#ffffff 0%,#fff4f8 100%); background-attachment: fixed; color: var(--ink-700); font-family: var(--f-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
/* Small shared utilities used across dashboard forms (replace inline widths/tints). */
.qm-field-w { width: 230px; max-width: 100%; }
.qm-field-w-sm { width: 180px; max-width: 100%; }
.qm-input-upper { text-transform: uppercase; }
.qm-tbl-thumb.qm-thumb-tint { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-link-truncate { max-width: 320px; }
/* Former inline styles, lifted into classes/hooks for submission cleanliness — identical values,
   no visual change. Tokens used where a value maps exactly (top: 84px === var(--sp-10)). */
.qm-upload-preview { height: 232px; }
.qm-color-swatch { width: 52px; flex: 0 0 auto; padding: 4px; }
.qm-color-swatch-sq { height: 38px; padding: 3px; }
.qm-code-input { text-transform: uppercase; letter-spacing: .15em; }
.qm-about-video { max-height: 280px; }
.qm-media-remove { position: absolute; top: 10px; left: 10px; width: 34px; height: 34px; border: 0; border-radius: 50%; background: rgba(0, 0, 0, .55); color: #fff; cursor: pointer; line-height: 1; }
/* One declaration, two honest names: the print/receipt designers stick a preview, the menu
   page sticks its tools rail. Same behaviour, so it stays one rule. */
.qm-sticky-preview, .qm-sticky-rail { position: sticky; top: var(--qm-dash-stick-top, var(--sp-10)); }
/* A long list inside the rail scrolls on its own instead of pushing the tools below it off
   the screen — twenty-six sections would otherwise make the rail taller than the page. */
.qm-rail-scroll { max-height: 320px; overflow-y: auto; }
/* Edit ⇄ Preview toggle for the dashboard designers (receipt / QR / screens). The .qm-seg control
   is mobile-only (d-lg-none in markup); on mobile it flips which column shows. Desktop is untouched
   (both columns side-by-side). */
@media (max-width: 991.98px) {
  .qm-editpreview[data-ep-view="edit"] .qm-ep-preview { display: none; }
  .qm-editpreview[data-ep-view="preview"] .qm-ep-controls { display: none; }
  .qm-editpreview > .col-12 .qm-seg { display: flex; }
  .qm-editpreview > .col-12 .qm-seg-btn { flex: 1 1 0; justify-content: center; }
}
[data-about-video-preview] { background: #000; }
[data-qm-dismiss-btn] { background: none; border: 0; color: inherit; cursor: pointer; opacity: .6; padding: 0; line-height: 1; }
.qm-spark-stop-1 { stop-color: var(--qm-primary); }
.qm-spark-stop-2 { stop-color: var(--violet); }
/* Hex colour field (branding) — a live preview chip + a free hex input. */
.qm-hexfield { display: flex; align-items: center; gap: 10px; }
.qm-hexfield .form-control { flex: 1; }
.qm-hex-preview { width: 46px; height: 46px; flex: none; border-radius: var(--r-sm); border: 1px solid var(--line); box-shadow: inset 0 0 0 1px rgba(0,0,0,.06); }
/* Native colour picker, sat alongside the swatch preview + hex field — sized to
match the preview chip rather than the browser's own default control size. */
.qm-color-native { width: 46px; height: 46px; flex: none; padding: 0; border: 1px solid var(--line); border-radius: var(--r-sm); background: none; cursor: pointer; }
.qm-color-native::-webkit-color-swatch-wrapper { padding: 3px; }
.qm-color-native::-webkit-color-swatch { border: 0; border-radius: calc(var(--r-sm) - 2px); }
.qm-color-native::-moz-color-swatch { border: 0; border-radius: calc(var(--r-sm) - 2px); }
::selection { background: var(--qm-primary-100); color: var(--ink-900); }
a { color: inherit; text-decoration: none; transition: color var(--t-fast); }
img { max-width: 100%; }

/* Tame the template's fluid heading sizes for a controlled scale */
/* Base heading scale.
   h4-h6 previously had NO rule, so they inherited Bootstrap's — which put
   Bootstrap's h5 at 20px, exactly equal to this project's h3, inverting the
   hierarchy for any content that used them. These own the fallback for content
   authored outside a component (CMS pages, blog bodies, admin-entered HTML);
   component classes own the intent. Only 10 markup uses across all views, so
   defining them costs nothing and closes the fall-through. */
h1 { font-size: var(--fs-h1); line-height: 1.15; }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-900); }
h4 { font-size: var(--fs-800); line-height: var(--lh-snug); }
h5 { font-size: var(--fs-700); line-height: var(--lh-normal); }
h6 { font-size: var(--fs-600); line-height: var(--lh-normal); }
/* The .h5 / .h6 UTILITIES — used to give a non-heading element a heading's size (an <h2>
   offcanvas title asking to look smaller; 55 places in all). The elements above are themed,
   the utilities were not, so they alone still read Bootstrap's scale and would not move with
   the product's. Bound to the tokens holding their present values — 20px and 16px — so
   nothing re-renders today and the type scale owns them from here. */
.h5 { font-size: var(--fs-900); }
.h6 { font-size: var(--fs-700); }

/* Roomier gutters on the fluid container for an airy feel */
.container-fluid { padding-left: clamp(16px, 4vw, 56px); padding-right: clamp(16px, 4vw, 56px); }

/* Accessible focus ring everywhere */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible,
textarea:focus-visible, [tabindex]:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--qm-primary) 45%, transparent); outline-offset: 2px; border-radius: 4px;
}

/* Refined form controls (Bootstrap base) */
.form-control, .form-select {
  border: 1px solid var(--field-border); border-radius: var(--r-sm); padding: 10px 13px;
  background-color: var(--surface);
  color: var(--ink-800); transition: border-color var(--t-fast), box-shadow var(--t-fast);
  font-size: 14px;
}
.form-control:focus, .form-select:focus {
  border-color: var(--qm-primary); box-shadow: 0 0 0 4px color-mix(in srgb, var(--qm-primary) 14%, transparent); outline: 0;
}
.form-control::placeholder { color: var(--ink-500); opacity: 1; }
/* A disabled input followed Bootstrap's cool grey (#e9ecef) while a disabled SELECT
   already followed the theme (.form-select:disabled below) — so the same state painted
   two different colours side by side. Most visible on Navigation, where every link's
   address field is disabled until its type is "custom": 30 fields, none of them
   re-tinting when the buyer changes the brand colour. Same tokens, same declarations. */
.form-control:disabled, .form-control[readonly] { background-color: var(--surface-2); color: var(--ink-400); cursor: not-allowed; opacity: 1; }
.form-label { font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); }
/* Bootstrap secondary-text utilities, adopted into the project palette.
   Both are used widely (.text-muted 148×, .form-text 28×) and neither had a
   project rule, so they painted Bootstrap's cool grey (#6c757d /
   --bs-secondary-color) against this product's warm --ink-400. Theming them
   here mirrors how .form-control and .form-label were already adopted, and
   corrects every site at once rather than editing 176 call sites. */
.text-muted { color: var(--ink-400) !important; }
.form-text  { font-size: var(--fs-400); color: var(--ink-400); }
/* Same adoption, one variable further out: Bootstrap's border utilities and components read
   --bs-border-color, which defaults to the cool #dee2e6 and so never re-tinted with the brand.
   Pointing it at --line is Bootstrap's own documented theming hook, and it corrects the
   .border utility, accordions and every other Bootstrap border in one declaration. */
:root { --bs-border-color: var(--line); }
/* Input-group affix (unit suffixes on the vendor settings fields) — adopted the
   same way as .form-control above so the affix and the field share one border. */
.input-group-text { background: var(--surface-2); border: 1px solid var(--field-border); color: var(--ink-600); font-size: 14px; }
/* Range slider — Bootstrap paints the thumb its own blue. Vendor pseudo-elements
   must each get their own rule: an unrecognised pseudo invalidates the whole
   selector list, so grouping -webkit- with -moz- would drop both. */
.form-range::-webkit-slider-thumb { background-color: var(--qm-primary); }
.form-range::-moz-range-thumb { background-color: var(--qm-primary); }
.form-range::-webkit-slider-runnable-track { background-color: var(--line-2); }
.form-range::-moz-range-track { background-color: var(--line-2); }
/* ...and its focus halo, which is Bootstrap's blue too. Same tint recipe as
   .form-control:focus below. Split per vendor pseudo for the reason above. */
.form-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 4px color-mix(in srgb, var(--qm-primary) 14%, transparent); }
.form-range:focus::-moz-range-thumb { box-shadow: 0 0 0 1px #fff, 0 0 0 4px color-mix(in srgb, var(--qm-primary) 14%, transparent); }
/* Colour picker (branding): the native swatch dressed as a normal field. */
.form-control-color { width: 100%; height: 46px; padding: 6px; cursor: pointer; }
.form-control-color::-webkit-color-swatch-wrapper { padding: 0; }
.form-control-color::-webkit-color-swatch { border: 0; border-radius: calc(var(--r-sm) - 3px); }
.form-control-color::-moz-color-swatch { border: 0; border-radius: calc(var(--r-sm) - 3px); }

/* Number fields: drop the OS-default spin buttons so numeric inputs read the
 * same as the other styled text controls across every browser. Values are still
 * typed and step-validated; where an increment control is wanted, the custom
 * +/- buttons in .qm-qty-form provide it. */
.form-control[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.form-control[type="number"]::-webkit-outer-spin-button,
.form-control[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* File field: the browser's own button sits inside the control, so it is pulled
 * out to the field's padding edge and given the same tinted surface as the other
 * adopted Bootstrap internals above. */
.form-control::file-selector-button {
  margin: -10px -13px; margin-inline-end: 13px;
  background: var(--surface-2); color: var(--ink-700);
}

/* ---------------------------------------------------------------------------
 * Select dropdowns — ONE custom control used everywhere. The native chevron and
 * OS look are removed (appearance:none) and replaced with the project chevron,
 * so every <select> across admin, vendor, driver and storefront reads the same
 * (matched height, border, radius, focus ring). Text inputs keep the shared
 * rule above and never receive a chevron — only .form-select does.
 * ------------------------------------------------------------------------- */
.form-select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  min-height: 44px; padding-right: 40px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%23754151' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 15px;
}
.form-select:hover:not(:disabled) { border-color: var(--qm-primary); }
.form-select:disabled { background-color: var(--surface-2); color: var(--ink-400); cursor: not-allowed; opacity: 1; }
/* Compact variant for dense table cells (orders, deliveries): same language,
 * tighter, with a floor width so short and long options line up cleanly and
 * the label never collides with the chevron. */
.form-select-sm {
  min-height: 36px; min-width: 136px; padding: 6px 32px 6px 12px; font-size: var(--fs-400);
  background-position: right 11px center; background-size: 14px; border-radius: var(--r-sm);
}
/* The input half of that pair. The compact variant existed for SELECT only, so a toolbar that
   pairs the two — Admin -> Campaigns puts a text filter beside a status select on one row —
   drew a 36px themed select next to a 44px Bootstrap-default input. Same reason the disabled
   states above were unified: one state, one language, whichever control wears it. No min-width,
   because that floor exists so option labels line up under the chevron, and no chevron metrics,
   because an input has none. */
.form-control-sm {
  min-height: 36px; padding: 6px 12px; font-size: var(--fs-400);
}

/* Choices.js searchable selects (loaded per-page AFTER app.css, so these use a
 * body- prefix to win specificity) share the same closed-control language. */
body .choices { margin-bottom: 0; }
body .choices__inner {
  min-height: 44px; padding: 8px 40px 8px 13px;
  background: var(--surface); border: 1px solid var(--field-border);
  border-radius: var(--r-sm); font-size: 14px;
}
body .choices.is-focused .choices__inner,
body .choices.is-open .choices__inner {
  border-color: var(--qm-primary); box-shadow: 0 0 0 4px color-mix(in srgb, var(--qm-primary) 14%, transparent);
}
body .choices[data-type*='select-one']::after {
  content: ''; border: 0; width: 15px; height: 15px; margin-top: -8px; right: 13px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='%23754151' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5l4 4 4-4'/%3E%3C/svg%3E") no-repeat center / 15px 15px;
}
/* The open panel wears the product's pop-menu language (same family as .qm-pop):
   soft radius, real elevation, inset padding with rounded rows — not the
   library's flat default list. */
body .choices__list--dropdown, body .choices__list[aria-expanded] {
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-3);
  margin-top: 6px; padding: 6px; overflow: hidden;
}
body .choices__list--dropdown .choices__item--selectable,
body .choices__list[aria-expanded] .choices__item--selectable { border-radius: 8px; }
body .choices__list--dropdown .choices__item--selectable.is-highlighted,
body .choices__list[aria-expanded] .choices__item--selectable.is-highlighted {
  background: var(--qm-primary-50); color: var(--ink-900);
}
/* The panel's search field is one of OUR inputs, not the library's bare box. */
body .choices__list--dropdown .choices__input, body .choices__list[aria-expanded] .choices__input {
  margin: 0 0 6px; padding: 8px 11px; font-size: 13px;
  background: var(--surface-2); border: 1px solid var(--field-border); border-radius: 8px;
}
/* Rider assignment (orders table): the themed avatar select compacted to table
   density. Same skin as every Choices control — the 44px field is simply too
   tall beside qm-pill rows, so the cell variant tightens the box, keeps the
   rider's photo in the CLOSED control, and gives the open panel real room
   instead of the cell's width. */
.qm-rider-cell { width: 228px; }
.qm-rider-cell .choices { width: 100%; }
/* Flex-centred with symmetric padding: the library ships 4px top / 7.5px bottom,
   which floats the avatar high in the box. */
.qm-rider-cell .choices__inner,
.qm-rider-cell .choices[data-type*='select-one'] .choices__inner { min-height: 38px; padding: 0 32px 0 10px; font-size: 13px; width: 100%; display: flex; align-items: center; }
.qm-rider-cell .choices[data-type*='select-one']::after { right: 10px; }
.qm-rider-cell .choices__list--single { padding: 0; width: 100%; display: flex; align-items: center; }
.qm-rider-cell .choices__list--single .choices__item { display: flex; align-items: center; font-weight: 600; color: var(--ink-800); }
.qm-rider-cell .qm-choice-flag { width: 22px; height: 22px; margin-right: 8px; }
/* The open panel spans EXACTLY the control's width — one column, one edge. */
.qm-rider-cell .choices__list--dropdown, .qm-rider-cell .choices__list[aria-expanded] { width: 100%; min-width: 0; }
.qm-rider-cell .choices__list--dropdown .choices__item--selectable, .qm-rider-cell .choices__list[aria-expanded] .choices__item--selectable {
  display: flex; align-items: center; padding: 8px 10px; font-size: 13px; font-weight: 600; color: var(--ink-700);
}
/* Settled orders: the rider is a fact, not a control — photo + name, quiet. */
.qm-rider-done { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--ink-600); white-space: nowrap; }
.qm-rider-done .qm-choice-flag { margin-right: 0; }

/* Order moves: the advance arrow stays DIM until intent (hover) shows it in the
   brand; a delivered row's slot carries a green check instead — the journey is
   over, so the mark is a state, not a control. */
.qm-actions-cell .qm-icon-btn .fa-arrow-right { color: var(--ink-300); transition: color var(--t-fast); }
.qm-actions-cell .qm-icon-btn:hover .fa-arrow-right { color: inherit; }
.qm-actions-cell .qm-icon-btn[data-status-done] {
  color: var(--ok); border-color: color-mix(in srgb, var(--ok) 32%, transparent);
  background: var(--ok-bg); opacity: 1; cursor: default;
}

/* Full-page screenshot in a short window — the homepage-layout chooser in Settings.
   A homepage capture is 5-9x taller than it is wide, so a thumbnail shows the hero and nothing
   else, which is the part every layout has in common. The card holds the top of the page and
   pans down on hover or keyboard focus, so an operator can read the whole layout without
   leaving Settings. translateY is on the IMAGE, not the window, so the frame stays put.
   Reduced-motion users get a scrollable window instead of an animation. */
.qm-shot { --qm-shot-pan: 22s; position: relative; height: 190px; overflow: hidden; border-radius: var(--r-sm); background: var(--ink-050); }
.qm-shot img { position: absolute; inset-inline-start: 0; top: 0; width: 100%; height: auto; display: block; transition: transform var(--qm-shot-pan) linear; }
/* > .qm-shot, not a descendant selector: the option cards sit INSIDE the settings card, so
   `.qm-card:focus-within .qm-shot` matched the outer card too and selecting one layout panned
   all six at once. The direct child keeps it to the card that actually has focus. */
.qm-shot:hover img,
.qm-shot:focus-visible img,
.qm-card:has(input:focus-visible) > .qm-shot img { transform: translateY(calc(190px - 100%)); }
/* Defensive, for translation: a flex item whose min-width is auto refuses to wrap, so a layout
   name longer than its card would push the tip out rather than take a second line. English fits
   today; Spanish already runs half as long again. Scoped to cards holding a screenshot, not to
   every switch row in the product. */
.qm-card:has(> .qm-shot) .qm-switch-row > span,
.qm-card:has(> .qm-shot) .qm-switch-row strong { min-width: 0; }
@media (prefers-reduced-motion: reduce) {
  .qm-shot { overflow-y: auto; }
  .qm-shot img { position: static; transition: none; }
  .qm-shot:hover img, .qm-shot:focus-visible img, .qm-card:has(input:focus-visible) > .qm-shot img { transform: none; }
}

/* A picker inside a dialog. .modal .modal-body carries its own overflow-y so long dialogs can
   scroll — which also CLIPS an open dropdown at the body's edge, so the list disappeared behind
   the footer buttons. It reads as a z-index fault and is not one: the list is absolutely
   positioned inside a scrolling ancestor. While a picker is open the body stops clipping and the
   list is lifted over the footer; both revert on close, so long dialogs keep their scroll.
   Scoped with :has(), the same approach .qm-ops-head already uses. */
.modal .modal-body:has(.choices.is-open) { overflow: visible; }
.modal .modal-body .choices.is-open { z-index: 5; }
.modal .modal-body .choices__list--dropdown { z-index: 6; }

/* Custom scrollbar (subtle) */
* { scrollbar-width: thin; scrollbar-color: #d6dadb transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: #d6dadb; border-radius: var(--r-pill); border: 2px solid var(--bg); }
*::-webkit-scrollbar-thumb:hover { background: #c2c7c7; }

/* ---------- Motion utilities (progressive enhancement) ---------- */
[data-reveal] { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s cubic-bezier(.2,.7,.2,1); will-change: opacity, transform; }
[data-reveal].is-revealed { opacity: 1; transform: none; }
/* Horizontal slide-in is desktop-only: on a narrow screen a full-width reveal
   element translated sideways pushes the page into horizontal scroll. Mobile keeps
   the vertical (translateY) reveal. `:not(.is-revealed)` means the offset never
   competes with the revealed reset above, so it always settles to `transform:none`. */
@media (min-width: 768px) {
  [data-reveal="left"]:not(.is-revealed) { transform: translateX(-22px); }
  [data-reveal="right"]:not(.is-revealed) { transform: translateX(22px); }
}
.qm-stagger > * { transition-delay: calc(var(--i, 0) * 70ms); }

.qm-skeleton { position: relative; overflow: hidden; background: #eef0f2; border-radius: var(--r-md); }
.qm-skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255,255,255,.6), transparent); animation: qm-shimmer 1.3s infinite; }
@keyframes qm-shimmer { 100% { transform: translateX(100%); } }

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .qm-skeleton::after { animation: none; }
  * { scroll-behavior: auto !important; }
}

/* ---------- Layout utilities (keeps markup free of static inline styles) ---------- */
.qm-hidden { display: none; }
.qm-nowrap { white-space: nowrap; }
.qm-cur-default { cursor: default; }
.qm-fs-13 { font-size: var(--fs-400); }
.qm-fs-12 { font-size: 12px; }
.qm-fs-85 { font-size: .85rem; }
.qm-fs-82 { font-size: .82rem; }
.qm-fw-600 { font-weight: 600; }
.qm-fw-400 { font-weight: 400; }
.qm-overline { font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.qm-c-star { color: var(--star); }
.qm-ml-6 { margin-left: 6px; }
.qm-maxw-480 { max-width: 480px; }
.qm-maxw-420 { max-width: 420px; }
.qm-maxw-380 { max-width: 380px; }
.qm-maxw-320 { max-width: 320px; }
.qm-img-22 { width: 22px; height: 22px; object-fit: contain; }
/* Decorative absolutely-positioned accents (hero illustration dots) */
.qm-dot-a { left: 86.7%; top: 87.5%; }
.qm-dot-b { left: 11.7%; top: 39%; }
/* Dynamic runtime values supplied as CSS custom properties (no inline style rules) —
--qm-sw a buyer-/data-chosen colour, --qm-bg a record's cover image, --qm-fill a meter %. */
.qm-swatch { background: var(--qm-sw, var(--line)); }
.qm-bgimg { background-image: var(--qm-bg); background-size: cover; background-position: center; }
.qm-bgimg-scrim { background-image: linear-gradient(180deg, rgba(31,11,18,.55), rgba(31,11,18,.82)), var(--qm-bg); background-size: cover; background-position: center; }
.qm-fill { width: var(--qm-fill, 0); }

/* ============================================================================
HEADER (smart sticky)
========================================================================== */
/* Soft shadow (no hard border) gives header definition without a hairline seam where a
coloured page-head band sits directly below it. The band tucks flush to the header
bottom (see #main-content padding-top) so the two meet with no gap. */
.header { position: fixed; top: 0; left: 0; right: 0; z-index: 1030; background: var(--surface); box-shadow: 0 2px 8px rgba(78,8,29,.07); transition: box-shadow var(--t), background var(--t), left var(--t-slow); }
.header.is-stuck { box-shadow: var(--e-2); }
/* Vertically centre the nav within the fixed header band (template CSS leaves it top-aligned). */
.header .container-fluid { display: flex; align-items: center; }
.qm-nav { display: flex; align-items: center; gap: 20px; height: 68px; width: 100%; position: relative; }
.qm-nav-logo { display: inline-flex; align-items: center; max-width: 150px; flex-shrink: 0; }
.qm-nav-logo img { width: auto; max-width: 148px; max-height: 40px; height: auto; display: block; }
.qm-nav-collapse { display: flex; flex: 1; align-items: center; gap: 18px; min-width: 0; }
.qm-nav-collapse .main-search { flex: 1; min-width: 0; }
/* Structural wrapper only in the mobile drawer (search + Cuisines burger
share a row there, see the <=991.98px block below) - everywhere wider it
collapses out of the layout so .main-search behaves exactly like a direct
.qm-nav-collapse flex child, same as before this wrapper existed. */
.qm-drawer-search-row { display: contents; }
/* Same pill input + icon prefix on every breakpoint - previously desktop-only,
which is why the mobile drawer's search box looked like a bare default input. */
.qm-nav-collapse .search-box { position: relative; }
.qm-nav-collapse .search-box .form-control { min-height: 46px; padding-left: 44px; border-radius: var(--r-pill); border: 1px solid color-mix(in srgb, var(--qm-primary) 24%, transparent); background: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,.8); }
.qm-nav-collapse .search-box::before { content: "\f002"; font-family: "Font Awesome 5 Free"; font-weight: 900; position: absolute; left: 26px; top: 50%; transform: translateY(-50%); color: color-mix(in srgb, var(--qm-primary) 72%, transparent); font-size: 14px; pointer-events: none; z-index: 2; }
/* Push Pages + Order Now to the right of the bar, just before the action icons.
Targets .qm-drawer-nav (Pages' wrapper) rather than .qm-explore itself, since Pages
is now nested one level deeper inside the mobile drawer's link-list wrapper - the
auto margin only "pushes" on the element that's a direct flex item of this row. */
.qm-nav-collapse .qm-drawer-nav { margin-left: auto; }
.qm-nav-actions { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.qm-nav-actions > .dropdown > .qm-account-btn, .qm-nav-actions > .qm-cuis-toggle, .qm-nav-actions > .qm-icon-pill { min-height: 40px; }
/* Bare hamburger button, no pill/background - matches .qm-side-toggle and the
mobile drawer's .qm-search-cuis-toggle (see TEMPLATE HEADER CHROME below) so
all three "burger" triggers read as one consistent, modern icon-button family
despite opening three different things. Brand-red at rest (never the grayish
.qm-nav-toggle used to color a Font Awesome glyph) - the tinted-circle hover
is presentational only, so state (open/closed) never depends on the mouse
still being over the button, which is what caused the color to stick until a
stray click elsewhere: see .qm-burger below, driven by aria-expanded instead. */
.qm-nav-toggle { display: none; align-items: center; justify-content: center; width: 40px; height: 40px; background: transparent; border: 0; border-radius: 50%; color: var(--qm-primary); cursor: pointer; padding: 0; line-height: 1; transition: background var(--t-fast); }
.qm-nav-toggle:hover { background: var(--qm-primary-50); }
.header .right-side { display: flex; align-items: center; gap: 18px; }
.qm-topnav { display: flex; gap: 20px; }
.qm-topnav a { color: var(--ink-800); font-weight: 600; font-size: 14px; position: relative; }
.qm-topnav a::after { content: ""; position: absolute; left: 0; right: 100%; bottom: -4px; height: 2px; background: var(--qm-primary); border-radius: 2px; transition: right var(--t); }
.qm-topnav a:hover, .qm-topnav a.active { color: var(--qm-primary); }
.qm-topnav a:hover::after, .qm-topnav a.active::after { right: 0; }
.delivery-add .form-control { border: 0; background: transparent; padding-left: 26px; height: auto; box-shadow: none; }

/* Service mode toggle (delivery / pickup) */
.service-mode-toggle { display: inline-flex; background: var(--line-2); border-radius: var(--r-pill); padding: 4px; gap: 2px; }
.service-mode-toggle .mode-btn { border: 0; background: transparent; border-radius: var(--r-pill); padding: 8px 18px; font-size: var(--fs-400); font-weight: 700; color: var(--ink-500); cursor: pointer; transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast); }
.service-mode-toggle .mode-btn:hover { color: var(--qm-primary-600); }
.service-mode-toggle .mode-btn.active { background: var(--surface); color: var(--qm-primary); box-shadow: var(--e-1); }
.service-mode-toggle .mode-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
/* status dot before each label (template parity) — filled red when active, muted otherwise */
.service-mode-toggle .mode-btn::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; vertical-align: middle; background: var(--ink-300); transition: background var(--t-fast); }
.service-mode-toggle .mode-btn.active::before { background: var(--qm-primary); }

.qm-cart-link { position: relative; color: var(--ink-800); font-size: var(--fs-800); }
.qm-cart-link:hover { color: var(--qm-primary); }
.qm-cart-count { position: absolute; top: -8px; right: -10px; background: var(--qm-primary-600); color: #fff; font-size: 11px; font-weight: 700; min-width: 18px; height: 18px; line-height: 18px; text-align: center; border-radius: var(--r-pill); padding: 0 4px; box-shadow: var(--e-primary); }
.qm-account-btn { background: transparent; border: 0; display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 14px; color: var(--ink-800); cursor: pointer; }
.qm-account-btn i.fa-user-circle { font-size: var(--fs-900); color: var(--qm-primary); }
.qm-account .dropdown-menu { font-size: 14px; border: 0; box-shadow: var(--e-3); border-radius: var(--r-md); padding: 8px; }
.qm-account .dropdown-item { padding: 8px 14px; font-weight: 500; border-radius: var(--r-sm); }
.qm-account .dropdown-item:hover { background: var(--qm-primary-50); color: var(--qm-primary); }

/* Offset the content below the fixed public header so the first element (a page-head
band / restaurant hero) sits flush against it with NO gap. The header is exactly
68px tall (the .qm-nav; the old 1px bottom border was dropped in favour of a soft
shadow), so the band tucks right under it — no hairline, no sub-pixel sliver.
Scoped to .index-page so the dashboard/auth layouts (no fixed public header) are
unaffected. */
.index-page #main-content { padding-top: 68px; }
@media (max-width: 991.98px) {
  .qm-nav-toggle { display: inline-flex; }
  .qm-nav-logo { margin-right: auto; }
  .qm-nav-collapse { display: none; position: absolute; top: 68px; left: 0; right: 0; flex-direction: column; align-items: stretch; gap: 14px; background: var(--surface); padding: 16px; border-top: 1px solid var(--line); box-shadow: var(--e-3); max-height: calc(100vh - 68px); overflow: auto; }
  .qm-nav-collapse.open { display: flex; }
  .qm-nav-collapse .main-search { flex: none; width: 100%; }
  /* Compact, natural-width preference control - NOT stretched full-width like a
  CTA (the drawer stretches children to 100% by default via align-items:stretch). */
  .qm-nav-mode--mobile { width: auto; align-self: flex-start; }
  .qm-nav-collapse .qm-topnav { flex-direction: column; gap: 12px; }
  .index-page #main-content { padding-top: 68px; }
}

/* ---- Announcement band (Appearance -> Header) ----------------------------------------
The one line of chrome above the header: a closure, a holiday, a promotion. It is part of
the FIXED stack rather than a strip that scrolls away, because an announcement worth making
is worth still being there three screens down.

It renders only while the operator has switched it on AND written something, so the two
offsets below are keyed off its presence with a sibling combinator rather than a body class:
no element, no offset, and the 68px header geometry above is untouched on every page that
does not carry one. Both rules deliberately come AFTER the two `.index-page #main-content`
declarations above — same specificity, so source order is what decides. */
.qm-announce {
  position: fixed; top: 0; left: 0; right: 0; z-index: 1030;
  height: var(--qm-announce-h, 36px);
  display: flex; align-items: center;
  background: var(--qm-primary); color: #fff;
  transition: left var(--t-slow);
}
.qm-announce + .header { top: var(--qm-announce-h, 36px); }
.qm-announce ~ #main-content { padding-top: calc(68px + var(--qm-announce-h, 36px)); }
.qm-announce .container-fluid { min-width: 0; }
/* One line, always. A message long enough to wrap would double the band's height and push
the whole page down, so it is clipped at the width it has — the operator writes a sentence,
not a paragraph, and the field is capped to match. */
.qm-announce-msg {
  margin: 0; display: flex; align-items: center; gap: 8px; min-width: 0;
  font-size: 13.5px; line-height: 1.2; white-space: nowrap; overflow: hidden;
}
.qm-announce-msg > span { overflow: hidden; text-overflow: ellipsis; }
.qm-announce-msg > .fa-bullhorn { flex: 0 0 auto; font-size: 12px; opacity: .85; }
.qm-announce-link {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  color: #fff; font-weight: 700; text-decoration: underline; text-underline-offset: 3px;
}
.qm-announce-link:hover, .qm-announce-link:focus-visible { color: #fff; text-decoration: none; }
.qm-announce-link .fa-arrow-right { font-size: 11px; }
@media (max-width: 575.98px) { .qm-announce-msg { font-size: 12.5px; gap: 6px; } }

/* Medium tier (laptop-ish widths, just under the full desktop bar): search,
Pages, Cuisines and Order Now all stay inline here - there's still room for
them, just not for the full-text identity chrome too. Only the account name
and language code compact to icon-only (see further down); the search itself
gets a smaller, still-usable cap instead of the desktop 300-340px budget.
The icon-only styling for language/cuisines/location/order-now/pages lives
further down, after their own base rules in TEMPLATE HEADER CHROME, so those
overrides actually win the cascade instead of losing to a same-specificity
base rule that happens to sit later in the file. */
@media (max-width: 1599.98px) and (min-width: 992px) {
  .qm-nav-collapse .main-search { max-width: 220px; }
}

/* ============================================================================
SECTIONS
========================================================================== */
.qm-section { padding: var(--section-pad) 0; }
@media (max-width: 700px){ .qm-section { padding: var(--sp-8) 0; } }
.qm-section.pt-0 { padding-top: 0; }
.qm-section.pb-0 { padding-bottom: 0; }
.qm-eyebrow { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; color: var(--qm-primary-600); margin-bottom: 8px; }
.qm-section-title { font-size: clamp(22px, 2.4vw, 30px); font-weight: 700; color: var(--ink-900); margin-bottom: 6px; line-height: 1.2; letter-spacing: -.01em; }
.qm-section-sub { color: var(--ink-500); font-size: var(--fs-600); margin-bottom: 26px; }
.qm-section-head { text-align: center; max-width: 620px; margin: 0 auto 34px; }
.qm-section-head .qm-section-sub { margin-bottom: 0; }

.qm-badge-veg { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--badge-veg); border-radius: 3px; position: relative; }
.qm-badge-veg::after { content: ""; position: absolute; inset: 2px; background: var(--badge-veg); border-radius: 50%; }
.qm-badge-nonveg { display: inline-block; width: 14px; height: 14px; border: 2px solid var(--badge-nonveg); border-radius: 3px; position: relative; }
.qm-badge-nonveg::after { content: ""; position: absolute; inset: 2px; background: var(--badge-nonveg); border-radius: 50%; }
/* Dynamic dish-attribute icon strip (eggs / chef / spicy / zesty) - driven by menu_items flags */
.qm-attr-row { display: flex; flex-wrap: wrap; gap: 5px; margin: 8px 0 2px; }
.qm-attr { width: 24px; height: 24px; border-radius: 7px; background: var(--qm-primary-50); border: 1px solid var(--line); display: inline-flex; align-items: center; justify-content: center; padding: 3px; }
.qm-attr img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* The same badge beside its name in the vendor's editor, so the switch is picked by the artwork a
   customer will see rather than by a word. Sized here because it sits outside .qm-attr's box. */
.qm-attr-img { width: 20px; height: 20px; object-fit: contain; flex: none; }

/* ============================================================================
BUTTONS
========================================================================== */
/* Single canonical button system (gradient pill — template-fidelity, no duplicate block). */
.qm-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; background: var(--qm-grad); color: #fff; font-weight: 600; font-size: 14px; padding: 11px 22px; min-height: 44px; border-radius: var(--r-pill); border: 1px solid transparent; cursor: pointer; text-decoration: none; transition: transform var(--t-fast), box-shadow var(--t), background var(--t-fast); line-height: 1.2; box-shadow: 0 12px 22px color-mix(in srgb, var(--qm-primary) 22%, transparent); }
.qm-btn:hover { background: linear-gradient(135deg, var(--qm-primary-700) 0%, var(--qm-primary-600) 100%); color: #fff; transform: translateY(-2px); box-shadow: 0 14px 26px color-mix(in srgb, var(--qm-primary) 34%, transparent); }
.qm-btn:active { transform: translateY(0); }
.qm-btn-outline { background: #fff; color: var(--qm-primary); border-color: var(--line); box-shadow: none; }
/* A STATE WEARING THE BUTTON'S SHAPE. Where a row cannot offer an action, the column says why
   instead — and a bare word beside a pill-shaped button left the column ragged, with the eye
   unable to scan one edge down the table. These take the button's box exactly (same padding,
   radius and height, set by .qm-btn) and only change the face, so the column keeps one silhouette
   whether a row can be acted on or not. They are not buttons and carry no hover or pointer: there
   is nothing to press. Colours come from the pill palette, so the same state reads the same here
   as it does anywhere else in the product. */
.qm-btn-state { cursor: default; box-shadow: none; border-color: transparent; }
.qm-btn-state:hover { transform: none; box-shadow: none; }
.qm-btn-state--amber { background: var(--warn-bg); color: var(--warn); }
.qm-btn-state--green { background: var(--ok-bg); color: var(--ok); }
.qm-btn-outline:hover { background: #fff; border-color: var(--qm-primary); color: var(--qm-primary); }
/* Selected state for segmented view toggles (e.g. the reservations List/Calendar pair). */
.qm-btn-outline.active { background: var(--qm-grad); color: #fff; border-color: transparent; box-shadow: 0 12px 22px color-mix(in srgb, var(--qm-primary) 22%, transparent); }
.qm-btn-outline.active:hover { color: #fff; }
.qm-btn-ghost { background: transparent; color: var(--ink-700); border-color: var(--line); box-shadow: none; }

/* Filter-bar Reset — shared by every filtered list. Always visible and styled to match
   the filter inputs beside it (same --r-sm radius + --field-border), so it reads as part
   of the bar rather than a floating button. It stays a neutral grey while nothing is
   filtered, and turns to the brand red (.is-active) once a filter is applied, signalling
   it will actually clear something. */
.qm-filter-reset { display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 10px 18px; font-size: 14px; font-weight: 600; line-height: 1.5; color: var(--ink-500); background: var(--surface); border: 1px solid var(--field-border); border-radius: var(--r-sm); text-decoration: none; cursor: pointer; white-space: nowrap; transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast); }
.qm-filter-reset:hover { color: var(--ink-700); border-color: var(--ink-400); }
.qm-filter-reset.is-active { color: var(--qm-primary); border-color: var(--qm-primary); }
/* When active (filters applied → turns brand colour), lead the label with a refresh glyph. */
.qm-filter-reset.is-active::before { content: "\f2f1"; font-family: "Font Awesome 5 Free"; font-weight: 900; font-size: 12px; }
.qm-filter-reset.is-active:hover { color: #fff; background: var(--qm-primary); border-color: var(--qm-primary); }
.qm-filter-reset:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-btn-ghost:hover { background: var(--qm-primary-50); border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-btn-light { background: #fff; color: var(--qm-primary); box-shadow: var(--e-2); }
.qm-btn-light:hover { background: #fff; color: var(--qm-primary-700); }
.qm-btn-lg { padding: 14px 28px; font-size: var(--fs-600); border-radius: var(--r-pill); }
.qm-btn-sm { padding: 7px 14px; font-size: var(--fs-400); min-height: 0; }
/* One button size for dashboard ACTIONS: any button used as a header or subheader action
   renders at the standard qm-btn size, so primary/secondary actions are consistent whether the
   page authored qm-btn or qm-btn-sm. Scoped to .qm-ops-head / .qm-dash-subhead, so storefront
   and in-cell/compact small buttons keep their intended size. */
.qm-ops-head .qm-btn.qm-btn-sm, .qm-dash-subhead .qm-btn.qm-btn-sm { padding: 11px 22px; font-size: 14px; min-height: 44px; }
.qm-btn-xs { padding: 6px 14px; font-size: 12.5px; min-height: 32px; }
.qm-btn[disabled] { opacity: .55; cursor: default; transform: none; box-shadow: none; }

/* ============================================================================
HERO (light, airy, gradient-mesh)
========================================================================== */
.qm-hero { position: relative; background: linear-gradient(135deg, #fff5f2 0%, #fff8f0 45%, #ffffff 100%); border: 1px solid var(--line); border-radius: var(--r-xl); padding: clamp(34px, 5vw, 64px); overflow: hidden; box-shadow: var(--e-1); }
.qm-hero::before { content: ""; position: absolute; width: 460px; height: 460px; right: -120px; top: -160px; background: radial-gradient(circle, color-mix(in srgb, var(--qm-primary) 28%, transparent), transparent 62%); pointer-events: none; }
.qm-hero::after { content: ""; position: absolute; width: 360px; height: 360px; left: -120px; bottom: -180px; background: radial-gradient(circle, rgba(47,158,68,.16), transparent 62%); pointer-events: none; }
.qm-hero-inner { position: relative; z-index: 1; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: 36px; align-items: center; }
.qm-hero-copy { max-width: 640px; }
/* The hero-title ramp. Every hero headline in the product resolves here — the
   marketplace slider's <h1> and its promo slides' <h2>, and the classic layout's
   card hero — so one edit moves them together and none can drift apart again. */
.qm-hero h1, .qm-hero-headline, .qmc-hero-copy h1, .qm-kiosk-hero h1 { font-size: clamp(32px, 4.4vw, 52px); font-weight: 700; line-height: 1.08; letter-spacing: -.02em; color: var(--ink-900); margin: 12px 0 14px; }
/* Brand highlight inside any hero title. Scoping this to `.qm-hero h1` left it
   dead on every headline that was not an <h1> inside `.qm-hero` — the promo
   slides, the classic hero and the cuisine hero all rendered it as plain ink.
   Kept low-specificity so a hero on artwork can override it (see .qm-herohg-title). */
.qm-hl { color: var(--qm-primary); }
.qm-hero p, .qmc-hero-copy > p { font-size: clamp(15px, 1.5vw, 18px); color: var(--ink-600); margin-bottom: 26px; max-width: 540px; }
.qm-hero .qm-hero-search { background: #fff; border-radius: var(--r-md); padding: 8px; display: flex; gap: 8px; max-width: 560px; box-shadow: var(--e-3); border: 1px solid var(--line); }
/* min-width:0 is what lets the field actually give ground. `flex:1` leaves min-width at
   `auto`, so the automatic minimum size floors the input at its min-content width — 206px
   under the Latin stack but 336px under the Arabic one, wider than the whole pill on a
   phone. The flex line then overflows, and because .qm-hero clips (overflow:hidden above)
   the search button is not merely pushed out of view but unreachable: in RTL it spills
   past x=0 and has no clickable pixels left below ~430px. The two sibling heroes already
   declare it (.qm-herohg-search input, .qmc-hero-search input) — which is exactly why
   /hungry-man never broke. */
.qm-hero .qm-hero-search input { border: 0; box-shadow: none; flex: 1; min-width: 0; }
.qm-hero .qm-hero-search input:focus { box-shadow: none; }
.qm-hero-trust { display: flex; flex-wrap: wrap; gap: 22px; margin-top: 24px; }
.qm-hero-trust span { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 600; color: var(--ink-700); }
.qm-hero-trust i { color: var(--qm-primary); }
.qm-hero-art { position: relative; }
.qm-hero-art img, .qm-hero-art video { width: 100%; border-radius: var(--r-lg); box-shadow: var(--e-4); object-fit: cover; aspect-ratio: 4/3; }
.qm-hero-chip { position: absolute; background: #fff; border-radius: var(--r-md); box-shadow: var(--e-3); padding: 10px 14px; display: flex; align-items: center; gap: 10px; font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); }
.qm-hero-chip i { color: var(--qm-primary); font-size: var(--fs-800); }
.qm-hero-chip.one { left: -14px; bottom: 28px; }
.qm-hero-chip.two { right: -10px; top: 26px; }
@media (max-width: 860px) {
  .qm-hero { padding: clamp(22px, 5vw, 40px); }
  .qm-hero-inner { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  /* Keep the slide artwork visible on mobile — stacked beneath the copy — and
     pull the floating chips inside the frame so they read cleanly at any width. */
  .qm-hero-art img, .qm-hero-art video { aspect-ratio: 16 / 10; }
  .qm-hero-chip.one { left: 10px; bottom: 10px; }
  .qm-hero-chip.two { right: 10px; top: 10px; }
  /* Icon-only search button - the label pushed the pill wider than the input
  could give up, sending the button off-canvas on narrow phones. */
  .qm-hero-search-btn span { display: none; }
  .qm-hero-search-btn { padding: 11px 16px; }
}

/* ============================================================================
CUISINE CAROUSEL (CSS scroll-snap, vanilla controls)
========================================================================== */
.qm-carousel { position: relative; }
/* touch-action:pan-x tells the browser up front that this element handles
horizontal panning itself via native scroll - without it, some mobile
browsers hesitate over whether a touch starting here should scroll the page
or the rail and the swipe can stall or never engage; -webkit-overflow-
scrolling keeps momentum scrolling on older iOS Safari. */
.qm-carousel-track { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(140px, 1fr); gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; padding: 6px 2px 14px; -ms-overflow-style: none; scrollbar-width: none; cursor: grab; touch-action: pan-x; -webkit-overflow-scrolling: touch; }
.qm-carousel-track::-webkit-scrollbar { display: none; width: 0; height: 0; }
.qm-carousel-track.qm-dragging { cursor: grabbing; scroll-snap-type: none; scroll-behavior: auto; }
.qm-carousel-track.qm-dragging a, .qm-carousel-track.qm-dragging .qm-rail-item { pointer-events: none; }
.qm-carousel-track > * { scroll-snap-align: start; }
.qm-carousel-btn { width: 38px; height: 38px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--e-1); transition: all var(--t-fast); }
.qm-carousel-btn:hover { background: var(--qm-primary); color: #fff; border-color: var(--qm-primary); }
.qm-carousel-btn:disabled { opacity: .35; cursor: default; box-shadow: none; }
.qm-carousel-btn:disabled:hover { background: var(--surface); color: var(--ink-700); border-color: var(--line); }
.qm-carousel.qm-carousel-static .qm-carousel-btn { display: none; }

/* Rail arrows: ONE pill holding both, split by a hairline, instead of two separate
   circles. Only for adjacent prev/next pairs — arrows that flank a rail (the craving
   strip) stay circular via the standalone .qm-carousel-btn rules above. */
.qm-carousel-nav { display: inline-flex; align-items: center; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); box-shadow: var(--e-1); overflow: hidden; }
.qm-carousel-nav .qm-carousel-btn { width: 44px; height: 38px; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
.qm-carousel-nav .qm-carousel-btn + .qm-carousel-btn { border-left: 1px solid var(--line); }
.qm-carousel-nav .qm-carousel-btn:hover:not(:disabled) { background: var(--qm-primary); color: #fff; }
.qm-carousel-nav .qm-carousel-btn:disabled,
.qm-carousel-nav .qm-carousel-btn:disabled:hover { background: transparent; color: var(--ink-700); }
.qm-carousel-nav .qm-carousel-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: -2px; }
/* A static rail hides its arrows — hide the pill too, or an empty outline is left behind. */
.qm-carousel.qm-carousel-static .qm-carousel-nav { display: none; }
.qm-cuisbrowse.qm-carousel-static .qm-carousel-nav { display: inline-flex; }

.qm-cuisine-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(116px, 1fr)); gap: 16px; }
/* "Browse by cuisine" tile: filled --cc tint, centered title, large low-opacity
watermark icon behind it, micro-animation on hover. --cc is set inline per tile. */
.qm-cuisine { position: relative; overflow: hidden; isolation: isolate; display: flex; align-items: center; justify-content: center; text-align: center; min-height: 128px; padding: 16px 14px; border-radius: var(--r-lg); border: 1px solid color-mix(in srgb, var(--cc, #ff003d) 22%, #fff); background: color-mix(in srgb, var(--cc, #ff003d) 13%, #fff); color: color-mix(in srgb, var(--cc, #ff003d) 64%, #1f0b12); font-weight: 700; font-size: 14px; box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t), border-color var(--t), background var(--t); }
.qm-cuisine:hover { transform: translateY(-5px); background: color-mix(in srgb, var(--cc, #ff003d) 19%, #fff); border-color: color-mix(in srgb, var(--cc, #ff003d) 44%, #fff); box-shadow: 0 18px 34px color-mix(in srgb, var(--cc, #ff003d) 24%, transparent); }
.qm-cuisine-bg { position: absolute; right: -8px; bottom: -18px; z-index: 0; font-size: 96px; line-height: 1; color: var(--cc, var(--qm-primary)); opacity: .15; transform: rotate(-10deg); transition: transform .5s cubic-bezier(.34,1.45,.5,1), opacity var(--t); pointer-events: none; }
.qm-cuisine:hover .qm-cuisine-bg { transform: rotate(2deg) scale(1.18) translateY(-6px); opacity: .26; }
.qm-cuisine-label { position: relative; z-index: 1; }

/* ---------- Quick-category strip ---------- */
/* Symmetric padding, deliberately overriding the shared track's 6px/14px: the rail centres
   the arrows on the whole track box, so uneven padding sits them off the chips' midline.
   The size is set by the chips' hover state - they lift 3px and cast a 0 12px 22px shadow
   reaching 23px below and 11px to each side. overflow-x:hidden makes overflow-y compute to a
   clipping value, so anything past this padding is sheared off mid-shadow. scroll-padding-inline
   is load-bearing, not decoration: mandatory snap otherwise pulls the first chip flush against
   the scrollport (parking the rail at scrollLeft: 12) and cancels the side padding outright. */
.qm-craving-strip.qm-carousel-track { display: flex; grid-auto-flow: unset; grid-auto-columns: unset; gap: 12px; padding: 20px 12px; scroll-padding-inline: 12px; }
/* "What are you craving?" - compact above-the-fold strip, chips wrap (no scroll) */
.qm-craving { padding: var(--sp-5) 0 var(--sp-4); }
/* Pull the hero up so the strip blends straight into it (was a ~100px gap). */
.qm-craving + .qm-section { padding-top: var(--sp-5); }
.qm-craving-title { font-size: clamp(20px, 2.4vw, 26px); margin: 0 0 var(--sp-4); }
.qm-craving-emoji { font-size: .92em; }
.qm-craving-name { color: var(--qm-primary-600); }
.qm-craving-rail { display: flex; align-items: center; gap: 12px; }
/* A real slider, but arrow-driven only: clipped (no scrollbar, no free drag/wheel scroll). */
.qm-craving-track { flex: 1 1 auto; min-width: 0; overflow-x: hidden; }
.qm-craving-arrow { flex: 0 0 auto; }
.qm-craving-chip { display: inline-flex; align-items: center; gap: 10px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 7px 16px 7px 7px; box-shadow: var(--e-1); color: var(--ink-800); font-weight: 600; font-size: 13.5px; white-space: nowrap; transition: transform .2s cubic-bezier(.34,1.56,.64,1), border-color .18s ease, color .18s ease, box-shadow .2s ease; }
.qm-craving-chip:hover { transform: translateY(-3px); color: var(--ink-900); border-color: color-mix(in srgb, var(--cc, var(--qm-primary)) 55%, #fff); box-shadow: 0 12px 22px color-mix(in srgb, var(--cc, var(--qm-primary)) 26%, transparent); }
.qm-craving-chip:active { transform: translateY(-1px); }
.qm-craving-ico { width: 34px; height: 34px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0; font-size: 14px; transition: transform .2s cubic-bezier(.34,1.56,.64,1); }
.qm-craving-ico img { width: 100%; height: 100%; object-fit: cover; }
.qm-craving-chip:hover .qm-craving-ico { transform: scale(1.12) rotate(-7deg); }
.qm-craving-count { background: var(--line-2); color: var(--ink-500); font-size: 11px; font-weight: 700; border-radius: var(--r-pill); padding: 2px 8px; }

/* ---------- Cuisine icon tiles (colorful, icon-based A–Z) ---------- */
/* --cc carries the cuisine's accent (set inline via accent_tint). */
.qm-cuis-ico { color: var(--cc, var(--qm-primary)); }
.qm-cuis-ico { background: var(--qm-primary-50); background: color-mix(in srgb, var(--cc, #ff003d) 15%, #fff); }
.qm-craving-ico.qm-cuis-ico, .qm-side-ico.qm-cuis-ico, .qm-cuis-card-ico.qm-cuis-ico { box-shadow: inset 0 0 0 1.5px var(--qm-primary-100); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--cc, #ff003d) 28%, #fff); }
.qm-cuis-card-ico.qm-cuis-ico { display: inline-flex; align-items: center; justify-content: center; overflow: visible; font-size: 19px; }
/* ---------- Tag chips (granular descriptors on restaurant cards / rows) ---------- */
.qm-rest-tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 2px; }
/* THE food-type pill. This is the single source of size, shape and colour for every
food-type tag in the product — listing rows, the card photo overlay and the restaurant
hero all render this class. The context-specific classes beside it (.qm-rest-tag-media,
.qm-rd-tag) add only placement, shadow or link behaviour; none of them may redeclare
font-size, padding or background, or the tag stops matching itself across pages.
Per-tag colour comes from the shared .qm-tagc* scale via tag_color_class(). */
.qm-rest-tag { display: inline-block; font-size: 12.5px; font-weight: 600; line-height: 1; color: #fff; background: var(--qm-primary-600); border: 0; border-radius: var(--r-pill); padding: 6px 13px; white-space: nowrap; }
/* Detail hero: tag pills (light, clickable, sit on the dark hero) */
/* Restaurant-detail food-type tags. These are LINKS, but they must read as the same
pill as every other food-type tag, so the view renders .qm-rest-tag alongside and this
rule adds only the link-specific bits. It sits on the hero photo, so it carries the same
ring + shadow the homepage hero tags use rather than a background of its own — a
background here would override the shared .qm-tagc* colour and make these the only
food-type tags on the site that are not colour-coded. */
.qm-rd-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.qm-rd-tag { text-decoration: none; box-shadow: 0 0 0 1px rgba(255, 255, 255, .38), 0 2px 8px rgba(0, 0, 0, .3); transition: box-shadow var(--t-fast), transform var(--t-fast); }
.qm-rd-tag:hover { color: #fff; transform: translateY(-1px); box-shadow: 0 0 0 1px rgba(255, 255, 255, .6), 0 4px 10px rgba(0, 0, 0, .34); }
/* Vendor settings: tag picker (checkbox pills that highlight when selected) */
.qm-tag-picker { display: flex; flex-wrap: wrap; gap: 8px; }
.qm-tag-check { position: relative; margin: 0; cursor: pointer; }
/* The pill IS the visual control, so the checkbox is fully hidden. Higher specificity
   than the dashboard "checkbox -> toggle switch" rule so the pills sit flush and the
   hidden input never reserves a 46px toggle's worth of space between them. */
.qm-tag-picker .qm-tag-check input[type="checkbox"] { position: absolute; opacity: 0; width: 1px; height: 1px; min-width: 0; margin: 0; padding: 0; border: 0; background: none; box-shadow: none; appearance: none; -webkit-appearance: none; }
.qm-tag-check span { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); font-weight: 600; color: var(--ink-700); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 7px 14px; transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast); }
.qm-tag-check span i { font-size: 12px; color: var(--ink-500); }
.qm-tag-check:hover span { border-color: var(--qm-primary-100); color: var(--qm-primary); }
.qm-tag-check input:checked + span { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-tag-check input:checked + span i { color: #fff; }
.qm-tag-check input:focus-visible + span { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
/* Stacked tag dimensions (store type, food type, protein, dietary, course): a hairline
   rule + breathing room between each so the groups read as distinct, aligned rows. */
.qm-tag-group + .qm-tag-group { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--line); }

/* ---------- Section rails (horizontal restaurant scrollers) ---------- */
.qm-rail-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
@media (max-width: 768px) {
  .qm-rail-head { flex-direction: column; align-items: stretch; gap: 12px; }
  /* Stacked under the heading, the control row now spans the full width. Keep "View all" at
     the start and push the arrow pill to the end - the auto margin lands the arrows on the
     right whether or not a View all link is present, and it is inert on desktop (the row is
     content-width there, so there is no free space for it to absorb). */
  .qm-rail-controls .qm-carousel-nav { margin-inline-start: auto; }
}
.qm-rail-head .qm-section-sub { margin-bottom: 0; }
.qm-rail-controls { display: inline-flex; align-items: center; gap: 10px; }
/* Wider cards so one fewer shows per screen on a wide desktop (was ~4 + a peek,
now ~3 + a peek) - the rest still scroll, nothing is removed from the rail. */
.qm-rail-track.qm-carousel-track { grid-auto-columns: minmax(376px, 420px); }
.qm-rail-item { height: 100%; }
/* Below ~480px the 376px minimum card is wider than the visible track (viewport
minus .container-fluid's own side padding), so it rendered cut off on both
edges instead of fully visible - one full card per view here, same as the
Featured Restaurants grid, still swipeable to the next. */
@media (max-width: 480px) {
  .qm-rail-track.qm-carousel-track { grid-auto-columns: 100%; }
}

/* ---------- Reorder cards ---------- */
.qm-reorder { display: flex; gap: 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: 12px; height: 100%; transition: transform var(--t), box-shadow var(--t); }
.qm-reorder:hover { transform: translateY(-3px); box-shadow: var(--e-2); }
.qm-reorder-media { width: 76px; height: 76px; border-radius: var(--r-md); overflow: hidden; flex-shrink: 0; }
.qm-reorder-media img { width: 100%; height: 100%; object-fit: cover; }
.qm-reorder-info { min-width: 0; display: flex; flex-direction: column; }
.qm-reorder-rest { font-weight: 700; color: var(--ink-900); font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-reorder-meta { font-size: 12.5px; color: var(--ink-400); margin-bottom: auto; }
.qm-reorder-form { display: flex; gap: 8px; margin-top: 10px; }

/* ============================================================================
RESTAURANT CARDS
========================================================================== */
.qm-rest-card-wrap { position: relative; height: 100%; }
.qm-rest-card { background: var(--card-bg); border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; box-shadow: var(--e-2); height: 100%; transition: transform var(--t), box-shadow var(--t); display: block; }
.qm-rest-card:hover { transform: translateY(-5px); box-shadow: var(--e-4); }
.qm-rest-media { position: relative; aspect-ratio: 17/10; overflow: hidden; background: var(--line-2); border-radius: 16px 16px 0 0; }
.qm-rest-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-rest-card:hover .qm-rest-media img { transform: scale(1.06); }
.qm-rest-logo { position: absolute; left: 14px; bottom: -22px; width: 54px; height: 54px; border-radius: var(--r-md); border: 3px solid #fff; object-fit: cover; background: #fff; box-shadow: var(--e-2); }
.qm-rest-badges { position: absolute; top: 12px; left: 12px; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; z-index: 2; }
/* Featured mirrors the deal stack's top:12px exactly - same row, same inset.
It only shifts sideways (never down) when the favourite heart button is
actually rendered, clearing its 36px width with a 10px gap; guests (no
heart) get it flush at the corner, matching the deal badge on the left. */
.qm-rest-badges-right { position: absolute; top: 12px; right: 12px; display: flex; flex-direction: column; align-items: flex-end; gap: 6px; z-index: 2; }
.qm-rest-badges-right.has-fav { right: 56px; }
.qm-rest-feat { background: var(--ink-900); color: #fff; font-size: 11px; font-weight: 700; padding: 4px 11px; border-radius: var(--r-pill); }
/* Food-type tags on the lower edge of the card photo — same pill shape as the Featured
   badge above, revealed on hover/focus. Colour comes from tag_color_class() so a given
   tag is always the same colour; white on a solid pill stays legible over any photo. */
.qm-rest-tags-media { position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 2; display: flex; flex-wrap: wrap; gap: 8px; opacity: 0; transform: translateY(8px); transition: opacity var(--t), transform var(--t); pointer-events: none; }
.qm-rest-card:hover .qm-rest-tags-media,
.qm-rest-card:focus-visible .qm-rest-tags-media { opacity: 1; transform: none; }
/* Placement + legibility only — size, shape and colour come from .qm-rest-tag. */
.qm-rest-tag-media { box-shadow: 0 2px 8px rgba(0, 0, 0, .28); }
/* Food-type tag colour scale — deliberately NOT scoped to one tag variant, so the same
palette drives the on-photo overlay (.qm-rest-tag-media) and the in-flow pill
(.qm-rest-tag) and a given food type keeps its colour across every surface. Which index
a name gets is decided once, in tag_color_class(). Declared after both variants so it
overrides their default background. */
.qm-tagc1 { background: #c2410c; }
.qm-tagc2 { background: var(--qm-primary-600); }
.qm-tagc3 { background: #6d28d9; }
.qm-tagc4 { background: #1565c0; }
.qm-tagc5 { background: #0f766e; }
.qm-tagc6 { background: #a16207; }
.qm-tagc7 { background: #4338ca; }
.qm-tagc8 { background: #15803d; }
@media (prefers-reduced-motion: reduce) { .qm-rest-tags-media { transition: none; transform: none; } }
.qm-rest-deal { background: var(--qm-grad); color: #fff; font-size: 11px; font-weight: 800; letter-spacing: .3px; padding: 4px 12px; border-radius: var(--r-pill); display: inline-flex; align-items: center; gap: 5px; box-shadow: var(--e-primary); }
.qm-fav-form { position: absolute; top: 10px; right: 10px; z-index: 3; margin: 0; }
.qm-fav-btn { width: 36px; height: 36px; border: 0; border-radius: 50%; background: rgba(255,255,255,.96); color: var(--qm-primary-100); box-shadow: var(--e-2); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-600); transition: transform var(--t-fast), color var(--t-fast); }
.qm-fav-btn:hover { transform: scale(1.12); color: var(--qm-primary); }
.qm-fav-btn.is-fav { color: var(--qm-primary); }
.qm-fav-btn:disabled { opacity: .6; cursor: default; }
.qm-rest-body { padding: 16px; }
.qm-rest-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; margin-bottom: 3px; }
/* Long names stay on one line (ellipsis + native title tooltip) so every card in a
rail/grid keeps the same height - a wrapped name pushes everything below it down
and breaks alignment against the other cards. */
.qm-rest-name { font-size: var(--fs-700); font-weight: 700; color: var(--ink-900); margin: 0; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* Color-coded rating rectangle */
.qm-rest-rating { color: #fff; font-weight: 800; font-size: 12.5px; padding: 3px 9px; border-radius: var(--r-sm); flex-shrink: 0; line-height: 1.2; }
.qm-rest-rating.is-great { background: var(--rating-great); color: #fff; } /* 4.6:1 */
.qm-rest-rating.is-good { background: var(--gold); color: #5a3b00; } /* dark on gold */
.qm-rest-rating.is-ok { background: var(--rating-ok); color: var(--rating-ok-text); } /* 4.6:1 */
.qm-rest-rating.is-low { background: var(--danger); color: #fff; }
.qm-rest-cuisine { font-size: 12.5px; color: var(--ink-400); margin: 0 0 10px; }
.qm-rest-pt { display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 12px; color: var(--ink-600); padding-top: 10px; border-top: 1px dashed var(--line); }
.qm-rest-pt i { color: var(--qm-primary); }
.qm-rest-pt span:first-child { font-weight: 600; color: var(--ink-800); white-space: nowrap; }
/* Service-mode reactivity: the header Delivery/Pickup toggle sets [data-service-mode]
   on <html>; in pickup, delivery-only facts hide and the pickup framing shows. */
.qm-pickup-only { display: none; }
[data-service-mode="pickup"] .qm-deliv-only { display: none; }
[data-service-mode="pickup"] .qm-pickup-only { display: inline; }
.qm-rest-rate-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.qm-stars { color: var(--star); font-size: 12px; letter-spacing: 2px; }
.qm-rest-count { font-size: 11.5px; color: var(--ink-400); }
.qm-diet { display: flex; gap: 6px; margin-top: 12px; }
.qm-sq { width: 26px; height: 26px; border-radius: 7px; background: var(--qm-primary-50); border: 1px solid var(--line); display: inline-flex; align-items: center; justify-content: center; }
.qm-sq img { width: 15px; height: 15px; object-fit: contain; }

/* ============================================================================
POPULAR DISH CARDS
========================================================================== */
.qm-pop-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-1); height: 100%; display: flex; flex-direction: column; transition: transform var(--t), box-shadow var(--t); }
.qm-pop-card:hover { transform: translateY(-6px); box-shadow: var(--e-3); }
.qm-pop-media { position: relative; display: block; aspect-ratio: 1/1; overflow: hidden; background: var(--line-2); }
.qm-pop-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-pop-card:hover .qm-pop-media img { transform: scale(1.06); }
.qm-pop-tag { position: absolute; top: 10px; left: 10px; background: var(--qm-primary-600); color: #fff; font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: var(--r-pill); }
/* Diet badge overlaid top-right of the image, mirroring .qm-pop-tag's top-left placement -
a white backdrop keeps the small veg/non-veg square legible against any photo. */
.qm-pop-diet { position: absolute; top: 10px; right: 10px; width: 26px; height: 26px; border-radius: 50%; background: #fff; box-shadow: var(--e-1); display: inline-flex; align-items: center; justify-content: center; }
/* Restaurant identity overlaps the image's bottom edge like a tab poking out of
the card body below - rounded only on top (against the photo), square on the
bottom so it reads as fused to the body, not a floating pill. */
.qm-pop-rest-tab { position: relative; z-index: 2; align-self: flex-start; display: inline-flex; align-items: center; gap: 7px; min-height: 56px; max-width: calc(100% - 32px); margin: -56px 16px -2px; padding: 4px 12px; background: var(--surface); border-radius: 12px 12px 0 0; box-shadow: 0 -2px 10px rgba(15, 23, 42, .08); font-size: 12.5px; font-weight: 600; color: var(--ink-700); text-decoration: none; }
.qm-pop-rest-tab:hover { color: var(--qm-primary); }
.qm-pop-rest-tab span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qm-pop-body { padding: 18px 16px 16px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.qm-pop-name-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; padding-bottom: 9px; border-bottom: 1px solid var(--line-2); }
.qm-pop-name { font-size: var(--fs-600); font-weight: 700; color: var(--ink-900); margin: 0; }
.qm-pop-price { flex-shrink: 0; font-size: var(--fs-700); font-weight: 700; color: var(--ink-900); }
.qm-pop-add { width: 100%; margin-top: auto; }
.qm-pop-add.added { background: var(--qm-primary-600); color: #fff; }

/* ============================================================================
TESTIMONIALS
========================================================================== */
.qm-testimonials { background: linear-gradient(180deg, var(--surface-2), #fff); }
.qm-quote { position: relative; overflow: hidden; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-6); box-shadow: var(--e-1); height: 100%; display: flex; flex-direction: column; transition: transform var(--t), box-shadow var(--t); }
.qm-quote:hover { transform: translateY(-4px); box-shadow: var(--e-3); }
/* Decorative quote mark in the brand colour — a solid accent sitting in the free space
   beside the stars, not a washed-out watermark. Text still layers above it (z-index 1). */
.qm-quote-mark { position: absolute; top: 16px; right: 18px; z-index: 0; font-size: 40px; line-height: 1; color: var(--qm-primary); transform: rotate(-8deg); pointer-events: none; }
.qm-quote-stars, .qm-quote-text, .qm-quote-by { position: relative; z-index: 1; }
.qm-quote-stars { color: var(--star); font-size: var(--fs-400); margin-bottom: 12px; }
.qm-quote-text { color: var(--ink-700); font-size: 14.5px; line-height: 1.7; margin: 0 0 18px; flex: 1; }
.qm-quote-by { display: flex; align-items: center; gap: 12px; }
.qm-quote-by img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }
.qm-quote-name { font-weight: 700; color: var(--ink-900); font-size: 14px; }
.qm-quote-role { font-size: 12.5px; color: var(--ink-400); }

/* Testimonial marquee — two full-bleed rows drifting in opposite directions. Each track
   holds the card set TWICE, so animating to -50% lands exactly on the repeat and the loop
   is seamless. Spacing is a per-item margin (not flex gap) so both halves measure the
   same and -50% is exact. Pauses on hover/focus and honours reduced-motion. */
.qm-marquee { position: relative; overflow: hidden; padding: 6px 0; -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); }
.qm-marquee + .qm-marquee { margin-top: 18px; }
.qm-marquee-track { display: flex; width: max-content; animation: qm-marquee-rtl 64s linear infinite; }
.qm-marquee--reverse .qm-marquee-track { animation-name: qm-marquee-ltr; }
.qm-marquee-item { flex: 0 0 340px; max-width: 84vw; margin-right: 20px; }
.qm-marquee:hover .qm-marquee-track,
.qm-marquee:focus-within .qm-marquee-track { animation-play-state: paused; }
/* Once JS takes over, the drift is driven by scrollLeft instead of the keyframes, which is
   what makes the same strip swipeable on touch and draggable with a mouse. The CSS
   animation above stays as the no-JS fallback. */
.qm-marquee.is-swipe { overflow-x: auto; scroll-behavior: auto; overscroll-behavior-x: contain;
  scrollbar-width: none; -ms-overflow-style: none; cursor: grab; }
.qm-marquee.is-swipe::-webkit-scrollbar { width: 0; height: 0; display: none; }
.qm-marquee.is-swipe .qm-marquee-track { animation: none; }
.qm-marquee.is-swipe.is-dragging { cursor: grabbing; }
@keyframes qm-marquee-rtl { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes qm-marquee-ltr { from { transform: translateX(-50%); } to { transform: translateX(0); } }
@media (max-width: 575.98px) { .qm-marquee-item { flex-basis: 280px; margin-right: 14px; } }
@media (prefers-reduced-motion: reduce) { .qm-marquee-track { animation: none; } }

/* ============================================================================
STATS BAND
========================================================================== */
/* Now floats over the drone band's bottom edge (see .qm-stats-overlap) - the
box-shadow lifts it off the photo behind it, and it's inset narrower (not
full-width) so the imagery still reads as the focal point. */
.qm-stats { background: var(--qm-grad-dark); border-radius: var(--r-xl); padding: var(--sp-8) var(--sp-7); color: #fff; position: relative; overflow: hidden; box-shadow: var(--e-3); max-width: 78%; margin-left: auto; margin-right: auto; }
.qm-stats::before { content: ""; position: absolute; right: -80px; top: -80px; width: 320px; height: 320px; background: radial-gradient(circle, color-mix(in srgb, var(--qm-primary) 35%, transparent), transparent 60%); }
.qm-stats-grid { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; text-align: center; }
.qm-stat-num { font-size: clamp(30px, 4vw, 46px); font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.qm-stat-num .qm-stat-suffix { color: var(--qm-primary); }
.qm-stat-label { margin-top: 8px; font-size: 13.5px; color: rgba(255,255,255,.72); font-weight: 500; }
@media (max-width: 700px) { .qm-stats-grid { grid-template-columns: repeat(2, 1fr); gap: 30px 16px; } }

/* ============================================================================
FEATURE TRIO ("Why QuickMunch")
========================================================================== */
.qm-feature { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-6) var(--sp-5); height: 100%; transition: transform var(--t), box-shadow var(--t); }
.qm-feature:hover { transform: translateY(-5px); box-shadow: var(--e-3); }
.qm-feature-icon { width: 56px; height: 56px; border-radius: var(--r-md); display: inline-flex; align-items: center; justify-content: center; font-size: 22px; color: var(--qm-primary); background: var(--qm-primary-50); margin-bottom: 16px; }
.qm-feature :is(h1,h2,h3,h4,h5,h6) { font-size: 17px; font-weight: 700; color: var(--ink-900); margin-bottom: 7px; }
.qm-feature p { font-size: 14px; color: var(--ink-500); margin: 0; line-height: 1.65; }

/* ============================================================================
NEWSLETTER
========================================================================== */
/* Light surface card — the partner CTA directly above already carries the brand
   gradient, so this one stays quiet and reads as the secondary action. Same
   eyebrow -> title -> sub rhythm every other section uses. */
/* Two columns: copy reads left-to-right, the form sits at the end — the same
   text-then-action rhythm as the partner CTA band. */
.qm-newsletter { background: var(--qm-primary-50); border: 1px solid var(--line); border-radius: var(--r-xl); padding: clamp(30px, 4vw, 52px); position: relative; overflow: hidden; box-shadow: var(--e-1);
  display: flex; align-items: center; justify-content: space-between; gap: clamp(24px, 4vw, 56px); flex-wrap: wrap; text-align: start; }
/* Envelope watermark, re-tinted for the light card (it was white-on-gradient before). */
.qm-newsletter::before { content: "\f0e0"; font-family: "Font Awesome 5 Free"; font-weight: 900; position: absolute; font-size: 220px; line-height: 1; inset-inline-end: -24px; top: -40px; color: var(--qm-primary); opacity: .07; z-index: 0; pointer-events: none; }
.qm-newsletter-copy { position: relative; z-index: 1; flex: 1 1 340px; min-width: 0; }
.qm-newsletter :is(h1,h2,h3,h4,h5,h6) { color: var(--ink-900); font-weight: 800; font-size: clamp(22px, 2.6vw, 30px); margin: 10px 0 8px; }
.qm-newsletter-sub { color: var(--ink-400); font-size: var(--fs-600); margin: 0; max-width: 46ch; }
.qm-newsletter-form { position: relative; z-index: 1; flex: 1 1 380px; max-width: 460px; margin: 0; }
/* Fused input group: one pill holding the icon, field and button, so it reads as a
   single control instead of two detached boxes. */
.qm-newsletter-field { display: flex; align-items: center; gap: 10px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 6px; box-shadow: var(--e-1); transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.qm-newsletter-field:focus-within { border-color: var(--qm-primary); box-shadow: 0 0 0 4px var(--qm-primary-100); }
/* The leading glyph is a proper brand chip, not a bare default icon — it picks up the
   focus state with the field so the whole control reacts as one. */
.qm-newsletter-field > i { flex-shrink: 0; display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary-600); font-size: 14px; transition: background var(--t-fast), color var(--t-fast); }
.qm-newsletter-field:focus-within > i { background: var(--qm-primary-100); color: var(--qm-primary-700); }
.qm-newsletter-field input { flex: 1; min-width: 0; border: 0; background: transparent; padding: 12px 4px; font-size: 14.5px; color: var(--ink-900); }
.qm-newsletter-field input:focus { outline: none; }
.qm-newsletter-field input::placeholder { color: var(--ink-400); }
.qm-newsletter-field .qm-btn { border-radius: var(--r-pill); white-space: nowrap; flex-shrink: 0; }
/* Below the two-column threshold the copy and form stack, still left-aligned. */
@media (max-width: 860px) {
  .qm-newsletter { gap: var(--sp-5); }
  .qm-newsletter-copy, .qm-newsletter-form { flex-basis: 100%; max-width: 100%; }
  .qm-newsletter::before { font-size: 160px; opacity: .05; }
}
/* Stacked on small screens: the pill becomes a rounded block, button full-width. */
@media (max-width: 520px) {
  .qm-newsletter-field { flex-direction: column; align-items: stretch; gap: 10px; border-radius: var(--r-lg); padding: 10px; }
  .qm-newsletter-field > i { display: none; }
  .qm-newsletter-field input { padding: 10px 12px; }
  .qm-newsletter-field .qm-btn { width: 100%; justify-content: center; }
}

/* ============================================================================
HOW IT WORKS
========================================================================== */
.qm-how { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-xl); padding: var(--sp-8) var(--sp-7); }
.qm-how-step { text-align: center; padding: 0 10px; position: relative; }
.qm-how-num { display: inline-flex; align-items: center; justify-content: center; width: 70px; height: 70px; border-radius: 50%; background: #fff; color: var(--qm-primary); font-size: var(--fs-h2); box-shadow: var(--e-2); margin-bottom: 16px; position: relative; }
.qm-how-num::after { content: attr(data-step); position: absolute; top: -6px; right: -6px; width: 24px; height: 24px; background: var(--qm-primary-600); color: #fff; border-radius: 50%; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.qm-how-step :is(h1,h2,h3,h4,h5,h6) { font-size: 17px; font-weight: 700; color: var(--ink-900); margin-bottom: 6px; }
.qm-how-step p { font-size: 13.5px; color: var(--ink-500); margin: 0; }

/* ============================================================================
PARTNER CTA BAND
========================================================================== */
/* The gradient stays as the base layer: it shows through wherever the photo has not
   loaded (or was removed), so the copy is never white-on-white. */
/* Photo backdrop + floating CTA card — same construction as .qm-drone-section with the
   .qm-stats card over it: the photo band sits behind (z-index 1) and the CTA is its own
   section in front (z-index 2), pulled up so it straddles the photo's bottom edge. */
.qm-cta-photo-sec { position: relative; z-index: 1; }
.qm-cta-photo { position: relative; overflow: hidden; border-radius: var(--r-xl); min-height: 520px; box-shadow: var(--e-3); background: var(--qm-grad-promo); }
/* Deliberately taller than its frame so the parallax travel never exposes an edge. */
.qm-cta-bg { position: absolute; left: 0; right: 0; top: -14%; width: 100%; height: 128%; object-fit: cover; z-index: 0; will-change: transform; pointer-events: none; }
/* Grounds the floating card without hiding the photo (the drone scrim stays one-sided;
   here the card sits low, so the tint is weighted to the bottom instead). */
.qm-cta-scrim { position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, transparent 35%, rgba(31,11,18,.55) 100%); }
/* The card: centred ON the photo's bottom edge, matching .qm-stats' 78% width.
   The lift = the photo section's 84px bottom padding + half the card's ~145px height,
   so the card straddles that edge instead of hanging below it. Fixed px follows the
   existing .qm-drone-section / .qm-stats-overlap precedent. */
.qm-cta-overlap { position: relative; z-index: 2; margin-top: -156px; }
.qm-cta-band { background: var(--qm-grad-promo); color: #fff; border-radius: var(--r-xl); padding: var(--sp-7); display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5); flex-wrap: wrap; box-shadow: var(--e-3); position: relative; overflow: hidden; max-width: 78%; margin-left: auto; margin-right: auto; }
.qm-cta-band::before { content: ""; position: absolute; right: -60px; bottom: -80px; width: 280px; height: 280px; background: radial-gradient(circle, rgba(255,255,255,.18), transparent 60%); z-index: 0; pointer-events: none; }
.qm-cta-text { position: relative; z-index: 1; flex: 1 1 380px; }
.qm-cta-text :is(h1,h2,h3,h4,h5,h6) { font-size: clamp(22px, 2.6vw, 28px); font-weight: 700; color: #fff; margin-bottom: 6px; position: relative; }
.qm-cta-text p { font-size: 14.5px; margin: 0; max-width: 640px; color: #fff; opacity: .95; position: relative; }
/* CTA-band buttons must be white-on-red (override the later .qm-btn gradient). */
.qm-cta-band .qm-cta-btn, .qm-cta-band .qm-btn { background: #fff; color: var(--qm-primary-600); white-space: nowrap; position: relative; z-index: 3; border-color: transparent; box-shadow: var(--e-2); }
.qm-cta-band .qm-cta-btn:hover, .qm-cta-band .qm-btn:hover { background: #fff; color: var(--qm-primary-700); }
/* Small screens keep the desktop composition — the card still sits OVER the photo's
   lower edge, just narrower. `justify-content: flex-start` is the important bit: in
   column direction the inherited `space-between` would push the button away from the
   subtitle and open a large gap. */
@media (max-width: 860px) {
  .qm-cta-photo { min-height: 360px; }
  /* Same idea, taller card (stacked text + full-width button ~180px): 84 + ~90. */
  .qm-cta-overlap { margin-top: -174px; }
  .qm-cta-band { max-width: 92%; flex-direction: column; align-items: stretch; justify-content: flex-start; gap: var(--sp-4); padding: var(--sp-5); }
  .qm-cta-text { flex: 0 0 auto; }
  .qm-cta-text p { margin: 0; }
  .qm-cta-band .qm-cta-btn, .qm-cta-band .qm-btn { width: 100%; justify-content: center; }
}

/* ==========================================================================
DRONE DELIVERY BAND
========================================================================== */
/* ---------- Homepage video band ---------- */
/* Fixed 900px so the band reads as a deliberate full-stop between the restaurant
   grids either side of it. The dark backdrop only shows while the first frame
   decodes, and behind letterboxing if a buyer swaps in a differently-shaped clip. */
/* --p is the scroll progress home.js writes each frame (0 contained -> 1 centred in the
   viewport); --gap is the measured distance to the nearer viewport edge. Widening by the
   SMALLER of the two side gaps keeps the band centred and makes overflowing the page
   impossible, including when the docked rail pads one side of the body. */
.qm-videoband { --p: 0; --gap: 0px; position: relative; height: 900px; margin-inline: calc(var(--gap) * var(--p) * -1); border-radius: calc(var(--r-xl) * (1 - var(--p))); overflow: hidden; box-shadow: var(--e-3); background: var(--ink-900); }
.qm-videoband-media { display: block; width: 100%; height: 100%; object-fit: cover; }
.qm-videoband-toggle { position: absolute; right: 24px; bottom: 24px; width: 56px; height: 56px; border-radius: 50%; border: none; background: var(--qm-primary); color: #fff; font-size: 17px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--e-2); transition: background var(--t-fast), transform var(--t-fast); }
.qm-videoband-toggle:hover { background: var(--qm-primary-600); transform: scale(1.06); }
.qm-videoband-toggle:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 3px; }
/* Sponsored media click-through: the pointer is replaced by a branded "Order now" disc that
   tracks the mouse. Reusable — any position:relative host (video band, hero ad slide) that
   carries [data-ad-cursor] gets it. The band's play/pause control sits above and keeps a
   normal pointer. */
.qm-ad-cta { position: absolute; inset: 0; z-index: 2; cursor: none; }
.qm-ad-cta:focus-visible { outline: 2px solid #fff; outline-offset: -6px; border-radius: inherit; }
.qm-videoband-toggle { z-index: 4; }
/* Two layers on purpose: the OUTER element only ever carries the pointer position and has
   no transition, so it tracks the mouse exactly (one composited transform per frame). The
   INNER disc owns the scale/fade transition. Combining both on one element makes the
   position itself animate, which reads as lag. */
.qm-ad-cursor { position: absolute; z-index: 3; top: 0; left: 0; width: 108px; height: 108px;
  pointer-events: none; will-change: transform; transform: translate3d(0, 0, 0) translate(-50%, -50%); }
.qm-ad-cursor-in { display: grid; place-items: center; width: 100%; height: 100%; padding: 10px;
  border-radius: 50%; text-align: center; background: var(--qm-grad); color: #fff; font-size: 12px;
  font-weight: 800; line-height: 1.25; letter-spacing: .06em; text-transform: uppercase;
  box-shadow: 0 12px 30px rgba(0,0,0,.28); opacity: 0; transform: scale(.6);
  transition: opacity var(--t-fast), transform var(--t-med, .25s) cubic-bezier(.2,.9,.3,1.4); }
.is-cta-hover .qm-ad-cursor-in { opacity: 1; transform: scale(1); }
/* The hero slide's frame is smaller than the full-width band, so the disc scales with it. */
.qm-hero-art .qm-ad-cursor { width: 88px; height: 88px; }
.qm-hero-art .qm-ad-cursor-in { font-size: 11px; }
/* Touch devices have no hover: keep a normal tap target and drop the follower entirely. */
@media (hover: none) {
  .qm-ad-cta { cursor: pointer; }
  .qm-ad-cursor { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .qm-ad-cursor-in { transition: opacity var(--t-fast); transform: scale(1); }
}
/* 900px would be nearly three screens on a phone; keep the band proportional there. */
@media (max-width: 767.98px) {
  .qm-videoband { height: 60vh; min-height: 380px; border-radius: var(--r-lg); }
  .qm-videoband-toggle { right: 16px; bottom: 16px; width: 48px; height: 48px; font-size: var(--fs-600); }
}
@media (prefers-reduced-motion: reduce) { .qm-videoband-toggle { transition: none; } }

.qm-drone-band { position: relative; overflow: hidden; border-radius: var(--r-xl); min-height: 660px; display: flex; align-items: center; box-shadow: var(--e-3); }
/* The band sits behind its neighbours: its top tucks under the bottom half of
the "Everything you need" cards, and its own bottom holds the counter strip -
both neighbours get a higher z-index so they always render in front. Disabled
on mobile, where the cards stack in a single column with no room to overlap. */
.qm-drone-section { position: relative; z-index: 1; margin-top: -170px; margin-bottom: -150px; }
/* The -150px bottom pull is a handshake with the stats band that overlaps this
   image. When stats does not render (fresh install, zero restaurants), the next
   band must not inherit the tuck. */
.qm-drone-section:not(:has(+ .qm-stats-overlap)) { margin-bottom: 0; }
/* Content bands open with pt-0 because the band above them pads the seam. When
   a layout's hero renders nothing (fresh install with no spotlight data, or the
   hero switched off), the FIRST band has no band above it and hugged the header.
   Heroes are the only sections that pair pt-0 with pb-0, so "first section
   without pb-0" is exactly "first content band on a page with no hero". */
#main-content > section.qm-section.pt-0:first-of-type:not(.pb-0) { padding-top: clamp(22px, 3vw, 38px) !important; }
.qm-features-overlap { position: relative; z-index: 2; }
/* A gentle inset (relative to the container-fluid, not a fixed pixel block) so
the drone photo shows at the edges and the cards read as secondary - without
looking like a disconnected floating island. */
.qm-features-overlap .row { max-width: 90%; margin-left: auto; margin-right: auto; }
.qm-stats-overlap { position: relative; z-index: 2; }
@media (max-width: 767.98px) {
  .qm-drone-section { margin-top: 0; margin-bottom: 0; }
  /* The 90% inset only earns its keep in front of the wide drone photo behind
  it - with the overlap disabled above (cards stack full-width, no band behind
  them), a narrowed row just wastes screen width on a small phone. */
  .qm-features-overlap .row { max-width: 100%; }
}
/* Background photo fills the band exactly like a normal cover-fit image (no
skewed aspect ratio), then slowly pans via a uniform scale + translateX so it
never reveals an edge - like a camera drifting over the city. */
.qm-drone-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; transform-origin: center center; animation: qm-drone-pan 28s ease-in-out infinite alternate; }
@keyframes qm-drone-pan { from { transform: scale(1.3) translateX(-2%); } to { transform: scale(1.3) translateX(2%); } }
/* Tint only fades in from the left (behind the copy) and clears by mid-band,
so the photo, clouds and drones on the right stay clearly visible. */
.qm-drone-scrim { position: absolute; inset: 0; background: linear-gradient(90deg, var(--qm-primary-700) 0%, var(--qm-primary) 32%, transparent 62%); opacity: .55; z-index: 1; }
.qm-drone-copy { position: relative; z-index: 3; flex: 1 1 420px; max-width: 480px; padding: var(--sp-8) var(--sp-7); color: #fff; }
.qm-drone-copy .qm-eyebrow { color: rgba(255,255,255,.85); }
.qm-drone-copy :is(h1,h2,h3,h4,h5,h6) { font-size: clamp(28px, 3.6vw, 42px); font-weight: 800; color: #fff; line-height: 1.1; letter-spacing: -.01em; margin: var(--sp-2) 0 var(--sp-3); }
.qm-drone-copy p { font-size: clamp(14px, 1.4vw, 16px); color: rgba(255,255,255,.92); max-width: 420px; margin: 0; }
.qm-drone-art { position: relative; z-index: 3; flex: 1 1 380px; min-height: 380px; display: flex; align-items: center; justify-content: center; }
/* A handful of soft cloud puffs at different sizes/positions, each drifting
on its own loop, so the sky reads as alive rather than one static graphic. */
.qm-drone-clouds { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; }
.qm-drone-cloud { position: absolute; opacity: .8; }
.qm-drone-cloud.c1 { width: 200px; top: 10%; left: 6%; animation: qm-cloud-a 19s ease-in-out infinite; }
.qm-drone-cloud.c2 { width: 260px; top: 58%; left: 50%; animation: qm-cloud-b 25s ease-in-out infinite; }
.qm-drone-cloud.c3 { width: 160px; top: 72%; left: 16%; opacity: .65; animation: qm-cloud-a 22s ease-in-out infinite reverse; }
.qm-drone-cloud.c4 { width: 230px; top: 6%; left: 60%; animation: qm-cloud-b 17s ease-in-out infinite reverse; }
.qm-drone-cloud.c5 { width: 150px; top: 40%; left: 78%; opacity: .6; animation: qm-cloud-a 21s ease-in-out infinite; }
@keyframes qm-cloud-a { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(26px); } }
@keyframes qm-cloud-b { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-34px); } }
/* Two smaller craft (same cutout) sweeping the background at different
speeds/heights for depth, behind the hero craft. */
.qm-drone-fly { position: absolute; z-index: 2; pointer-events: none; }
.qm-drone-fly-a { width: 84px; opacity: .65; animation: qm-fly-a 17s linear infinite; }
.qm-drone-fly-b { width: 56px; opacity: .45; animation: qm-fly-b 25s linear infinite; }
@keyframes qm-fly-a { 0% { left: -15%; top: 20%; } 100% { left: 112%; top: 26%; } }
@keyframes qm-fly-b { 0% { left: -15%; top: 70%; } 100% { left: 112%; top: 64%; } }
/* The hero craft: a real transparent cutout now, so it floats free with a
drop-shadow instead of being framed as a photo card. */
.qm-drone-craft { position: relative; z-index: 3; width: 460px; max-width: 85%; filter: drop-shadow(0 20px 30px rgba(0,0,0,.35)); animation: qm-drone-float 4s ease-in-out infinite; }
@keyframes qm-drone-float { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-16px) rotate(-1.5deg); } }
@media (max-width: 767.98px) {
  .qm-drone-band { flex-direction: column; text-align: center; min-height: 0; }
  .qm-drone-copy { max-width: 100%; padding: var(--sp-6) var(--sp-4) 0; }
  .qm-drone-copy p { margin-left: auto; margin-right: auto; }
  .qm-drone-art { min-height: 260px; padding: var(--sp-5) 0 var(--sp-6); }
  .qm-drone-craft { width: 300px; }
  .qm-drone-fly, .qm-drone-cloud.c5 { display: none; }
  /* Stacked layout puts the copy over the photo, so the tint has to carry the headline.
     Alpha is baked into the stops instead of using a blanket layer opacity: dense only
     at the very top (behind the eyebrow + title), fully clear by 58% so the aerial shot
     and the craft below stay untinted. Mobile only — the desktop scrim is unchanged. */
  .qm-drone-scrim { opacity: 1; background: linear-gradient(180deg,
      color-mix(in srgb, var(--qm-primary-700) 92%, transparent) 0%,
      color-mix(in srgb, var(--qm-primary) 62%, transparent) 34%,
      transparent 58%); }
}

/* ============================================================================
PAGE HEAD / BREADCRUMB
========================================================================== */
.qm-page-head { position: relative; overflow: hidden; padding: clamp(38px, 5vw, 56px) 0; margin-bottom: clamp(24px, 3vw, 36px); color: #fff;
  background:
  linear-gradient(90deg, var(--qm-primary), var(--qm-accent)) bottom / 100% 4px no-repeat,
  radial-gradient(circle at 1px 1px, rgba(255,255,255,.06) 1px, transparent 0) 0 0 / 24px 24px,
  linear-gradient(125deg, #28091a 0%, #4d0f23 50%, var(--qm-primary-700) 100%); }
.qm-page-head::before { content: ""; position: absolute; right: -80px; top: -150px; width: 460px; height: 460px;
  background: radial-gradient(circle, color-mix(in srgb, var(--qm-primary) 55%, transparent), transparent 62%); pointer-events: none; }
.qm-page-head::after { content: ""; position: absolute; left: -60px; bottom: -160px; width: 380px; height: 380px;
  background: radial-gradient(circle, color-mix(in srgb, var(--qm-primary) 40%, transparent), transparent 64%); pointer-events: none; }
.qm-page-head .container-fluid { position: relative; z-index: 1; }
.qm-page-head .qm-eyebrow { color: #ffb3c6; display: inline-block; margin-bottom: 8px; }
.qm-page-head :is(h1,h2,h3,h4,h5,h6) { font-size: clamp(26px, 3.4vw, 38px); font-weight: 800; margin: 0 0 10px; color: #fff; letter-spacing: -.02em; line-height: 1.1; }
.qm-page-head > .container-fluid > p, .qm-page-head .qm-page-sub { margin: 12px 0 0; color: rgba(255,255,255,.84); font-size: var(--fs-600); max-width: 580px; }
.qm-page-head .breadcrumb { margin: 10px 0 0; background: transparent; padding: 0; font-size: var(--fs-400); font-weight: 600; }
.qm-page-head .breadcrumb a { color: rgba(255,255,255,.78); transition: color var(--t-fast); }
.qm-page-head .breadcrumb a:hover { color: #fff; }
.qm-page-head .breadcrumb .mx-2 { color: rgba(255,255,255,.45); }
.qm-page-head .breadcrumb .active { color: #ffd2de; }
@media (max-width: 767.98px) { .qm-page-head .qm-page-sub { font-size: 14px; } }
/* Optional background image/video behind either sub-header style. When present the photo is
   the decoration, so the gradient blobs step aside; a scrim keeps the text legible. */
.qm-page-head .qm-subhead-media, .qm-blogm-head .qm-subhead-media { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.qm-page-head .qm-subhead-media > :is(img,video,iframe), .qm-blogm-head .qm-subhead-media > :is(img,video,iframe) { width: 100%; height: 100%; object-fit: cover; border: 0; display: block; }
.qm-page-head .qm-subhead-media::after, .qm-blogm-head .qm-subhead-media::after { content: ""; position: absolute; inset: 0; background: linear-gradient(125deg, rgba(28,9,26,.82), rgba(28,9,26,.55)); }
.qm-page-head.has-media::before, .qm-page-head.has-media::after, .qm-blogm-head.has-media::before, .qm-blogm-head.has-media::after { display: none; }

/* ============================================================================
FILTERS
========================================================================== */
.qm-filter-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-5); position: sticky; top: calc(68px + var(--sp-6)); }
/* Title row: heading start, Reset pinned to the opposite end (logical, so RTL flips it). */
.qm-filter-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 18px; }
.qm-filter-title { font-size: 17px; font-weight: 700; margin: 0; color: var(--ink-900); }
.qm-filter-reset { font-size: var(--fs-400); font-weight: 600; color: var(--qm-primary-600); text-decoration: none; flex-shrink: 0; transition: color var(--t-fast); }
.qm-filter-reset:hover { color: var(--qm-primary-700); text-decoration: underline; }
/* The Apply button is a no-JS fallback; qm-table-tools.js adds .is-auto once it binds. */
.qm-filter-card.is-auto .qm-filter-apply { display: none; }
.qm-filter-group { margin-bottom: 20px; }
.qm-filter-group > label, .qm-filter-label { display: block; font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); margin-bottom: 8px; }
.qm-filter-checks { display: flex; flex-direction: column; gap: 7px; max-height: 220px; overflow-y: auto; }
.qm-radio { font-size: 13.5px; font-weight: 400; color: var(--ink-600); display: flex; align-items: center; gap: 8px; margin: 0; cursor: pointer; }
.qm-radio input { accent-color: var(--qm-primary); }
.qm-results-count { color: var(--ink-500); font-size: 14px; margin-bottom: 18px; }

/* ============================================================================
FOOTER
========================================================================== */
.footer-col-title { font-size: var(--fs-600); font-weight: 700; margin-bottom: 16px; color: #fff; }
.footer-logo img { max-height: 42px; width: auto; }
footer.section .footer-contact p { color: rgba(255,255,255,.7); font-size: 13.5px; line-height: 1.7; }
footer.section .footer-links a { color: rgba(255,255,255,.7); font-size: 13.5px; transition: color var(--t-fast), padding var(--t-fast); }
footer.section .footer-links a:hover { color: var(--qm-primary); padding-left: 4px; }
footer.section .ft-social-media a { color: #fff; margin-right: 8px; font-size: var(--fs-600); width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(255,255,255,.08); transition: all var(--t-fast); }
footer.section .ft-social-media a:hover { color: #fff; background: var(--qm-primary); transform: translateY(-2px); }

/* ============================================================================
AUTH
========================================================================== */
.qm-auth-body { background: radial-gradient(circle at 18% 20%, color-mix(in srgb, var(--qm-primary) 18%, transparent), transparent 42%), radial-gradient(circle at 85% 80%, color-mix(in srgb, var(--qm-primary) 12%, transparent), transparent 45%), linear-gradient(135deg, var(--ink-900) 0%, #34121d 100%); min-height: 100vh; font-family: var(--f-sans); }
.qm-auth-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 36px 16px; }
.qm-auth-card { background: var(--surface); border-radius: var(--r-xl); box-shadow: var(--e-4); padding: 48px 56px; width: 100%; max-width: 560px; }
.qm-auth-logo { display: block; text-align: center; margin-bottom: 26px; }
.qm-auth-logo img { max-height: 44px; width: auto; }
.qm-auth-title { font-size: 26px; font-weight: 700; text-align: center; margin: 0 0 5px; color: var(--ink-900); letter-spacing: -.01em; }
.qm-auth-sub { text-align: center; color: var(--ink-500); font-size: 14px; margin-bottom: 30px; }
.qm-auth-form .form-label { font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); }
.qm-auth-form .form-control { padding: 12px 14px; border-radius: var(--r-sm); }
/* The utility rhythm reads cramped inside the tall auth card; open the field
   gaps and give the submit its own beat. Both need !important to outrank the
   Bootstrap utility they adjust. */
.qm-auth-form .mb-3 { margin-bottom: 1.25rem !important; }
.qm-auth-form .qm-btn[type="submit"] { margin-top: 4px; }
/* Flash inside the auth card: one-line messages centre against the icon disc,
   with clear air between disc, text and the dismiss control. */
.qm-auth-card .alert { align-items: center; gap: 14px; padding: 14px 18px; }
.qm-auth-card .qm-alert-body { padding-top: 0; padding-inline-end: 8px; }
.qm-auth-form .form-select { padding: 11px 40px 11px 13px; border-radius: var(--r-sm); }
.qm-form-section { font-size: 14px; font-weight: 700; color: var(--ink-900); margin: 20px 0 12px; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
/* Settings tab panels style their own headings (19px), which would otherwise win
   over the rule above on the section heads inside them. */
.qm-settings-panel .qm-form-section { font-size: 14px; margin: 20px 0 12px; }
/* The sign-in offers are a GROUP set apart from the form above them: a clear gap over the
   first line, a smaller one between the lines. At 14px apart from both, they used to read as
   one paragraph of small print rather than two separate things a visitor can act on. */
.qm-auth-foot { text-align: center; font-size: 14px; line-height: 1.55; color: var(--ink-500); margin: 26px 0 0; }
.qm-auth-foot + .qm-auth-foot { margin-top: 12px; }
.qm-auth-foot a { color: var(--qm-primary); font-weight: 600; }
/* "or" divider + Google sign-in button (shown only when Google Sign-in is configured) */
.qm-auth-or { display: flex; align-items: center; gap: 12px; margin: 16px 0; color: var(--ink-500); font-size: var(--fs-400); }
.qm-auth-or::before, .qm-auth-or::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--line); }
.qm-btn-google i { color: #ea4335; margin-right: 8px; }
/* Google's mark sits on the text baseline wherever it appears — a button, a tab, a card head.
   Sized in px by google_mark(), so it never inherits a surrounding font-size. */
.qm-google-mark { display: inline-block; vertical-align: -0.18em; flex: 0 0 auto; }
/* Centred and on its own line as an empty-state illustration, like the icon it replaces. */
.qm-empty > .qm-google-mark { display: block; margin-left: auto; margin-right: auto; }
/* GDPR cookie-consent banner (opt-in; accent follows the branding primary colour) */
.qm-cookie { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 1050;
  max-width: 660px; width: calc(100% - 32px); display: flex; align-items: center; gap: 16px;
  background: var(--surface, #fff); border: 1px solid var(--line); border-left: 4px solid var(--qm-primary);
  border-radius: var(--r-md, 12px); box-shadow: 0 12px 34px rgba(20, 25, 40, .16); padding: 14px 18px; }
.qm-cookie-text { margin: 0; font-size: 14px; color: var(--ink-700); line-height: 1.5; }
.qm-cookie-text a { color: var(--qm-primary); font-weight: 600; }
.qm-cookie-accept { flex: 0 0 auto; }
@media (max-width: 560px) { .qm-cookie { flex-direction: column; align-items: stretch; text-align: center; } }
/* Works on an <a> and on a <button>. The two "back to sign in" controls are buttons — they
   POST a cancel — and each carried `btn btn-link p-0 align-baseline` to undo the button chrome,
   which was the only reason bare Bootstrap .btn/.btn-link appeared anywhere in this product.
   The reset belongs to the component; the properties are inert on an anchor. */
.qm-auth-link { font-size: 12.5px; color: var(--qm-primary); font-weight: 600; text-decoration: none; background: none; border: 0; padding: 0; font-family: inherit; vertical-align: baseline; cursor: pointer; }
.qm-auth-link:hover { text-decoration: underline; }
/* Demo-account launcher (login) — single source of truth, horizontal grouped cards.
Token-based. */
.qm-demo { margin-top: var(--sp-6); }
.qm-demo-divider { text-align: center; position: relative; margin: var(--sp-4) 0 var(--sp-4); }
.qm-demo-divider::before { content: ""; position: absolute; top: 50%; left: 0; right: 0; height: 1px; background: var(--line); }
.qm-demo-divider span { position: relative; background: var(--surface); padding: 0 var(--sp-3); font-size: 12px; color: var(--ink-400); text-transform: uppercase; letter-spacing: .5px; }
/* minmax(0, 1fr) not a bare 1fr: a plain 1fr track's automatic minimum is its
content's min-content size, so a nowrap-truncated role description several
levels down (.qm-demo-btn-desc) was forcing this whole column - and every
button in it - wider than the card and getting clipped by .qm-auth-split's
own overflow:hidden instead of the description ellipsis-ing as designed. */
.qm-demo-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-2); }
.qm-demo-btn { width: 100%; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-3); display: flex; flex-direction: row; align-items: center; gap: var(--sp-3); font-size: 13.5px; font-weight: 600; color: var(--ink-800); cursor: pointer; text-align: left; transition: all var(--t-fast); }
.qm-demo-btn:hover { border-color: var(--qm-primary); color: var(--qm-primary); background: var(--qm-primary-50); transform: translateY(-2px); }
.qm-demo-btn i { font-size: var(--fs-800); width: 38px; height: 38px; flex-shrink: 0; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; }
.qm-demo-btn .qm-demo-desc { font-size: 11.5px; font-weight: 500; color: var(--ink-500); display: block; }
.qm-demo-note { text-align: center; font-size: 12px; color: var(--ink-400); margin: var(--sp-3) 0 0; }
/* Split-screen marketing panel for auth (login/register) — brand gradient aside. */
.qm-auth-split { display: grid; grid-template-columns: 1fr 1fr; max-width: 920px; width: 100%; background: var(--surface); border-radius: var(--r-xl); overflow: hidden; box-shadow: var(--e-4); }
.qm-auth-aside { background: var(--qm-grad-promo); color: #fff; padding: var(--sp-7); display: flex; flex-direction: column; justify-content: center; position: relative; overflow: hidden; }
.qm-auth-aside::before { content: ""; position: absolute; right: -60px; top: -80px; width: 240px; height: 240px; background: radial-gradient(circle, rgba(255,255,255,.16), transparent 60%); pointer-events: none; }
.qm-auth-aside > * { position: relative; z-index: 1; }
.qm-auth-aside .qm-auth-aside-eyebrow { font-size: 12px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; opacity: .92; margin-bottom: var(--sp-2); }
.qm-auth-aside :is(h1,h2,h3,h4,h5,h6,.qm-auth-aside-title) { color: #fff; font-weight: 700; font-size: var(--fs-h2); margin-bottom: var(--sp-3); line-height: 1.2; }
.qm-auth-aside p { color: rgba(255,255,255,.92); font-size: 14px; margin: 0; }
.qm-auth-aside ul { list-style: none; padding: 0; margin: var(--sp-4) 0 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.qm-auth-aside li { display: flex; align-items: center; gap: var(--sp-3); font-size: 14px; font-weight: 500; }
.qm-auth-aside li i { background: rgba(255,255,255,.2); width: 28px; height: 28px; flex-shrink: 0; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; }
.qm-auth-split .qm-auth-card { box-shadow: none; border-radius: 0; max-width: none; }
/* 3-column login: LEFT explanation | CENTER form | RIGHT one-click demo access (preview-only).
With demo off the controller passes neither aside nor right, so only the centered card renders. */
.qm-auth-3col { grid-template-columns: 1fr minmax(360px, 420px) 1.05fr; max-width: 1240px; }
.qm-auth-right { background: var(--surface-2); border-left: 1px solid var(--line); padding: var(--sp-6) var(--sp-6); display: flex; flex-direction: column; justify-content: center; }
.qm-auth-right .qm-demo { margin-top: 0; }
.qm-demo-head { margin-bottom: var(--sp-4); }
.qm-demo-eyebrow { display: block; font-size: 12px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--qm-primary-600); margin-bottom: var(--sp-1); }
.qm-demo-title { font-size: var(--fs-900); font-weight: 800; color: var(--ink-900); margin: 0 0 var(--sp-1); }
.qm-demo-lead { font-size: var(--fs-400); color: var(--ink-600); margin: 0; }
@media (max-width: 991.98px) {
  .qm-auth-3col { grid-template-columns: 1fr; max-width: 480px; }
  .qm-auth-3col .qm-auth-aside { display: none; } /* explanation is marketing — drop first on small screens */
  .qm-auth-right { border-left: 0; border-top: 1px solid var(--line); }
  /* Without this, a grid item's default min-width:auto lets .qm-demo-btn-desc's
  own white-space:nowrap dictate the whole column's width instead of shrinking
  to fit - the card was overflowing .qm-auth-split's overflow:hidden and getting
  silently clipped on narrow phones instead of the description ellipsis-ing. */
  .qm-auth-right { min-width: 0; }
}
@media (max-width: 760px) { .qm-auth-split { grid-template-columns: 1fr; } .qm-auth-split > .qm-auth-aside { display: none; } }

/* ============================================================================
DASHBOARD CHROME
========================================================================== */
.qm-dash { display: flex; min-height: 100vh; font-family: var(--f-sans); /* offset for content that sticks BELOW the sticky chrome (top bar + subheader). It must clear the FULL chrome: the titled band is ~165px tall AND the breadcrumb pill hangs ~7px past its lower edge (bottom ~172px). 192px lands sticky content ~20px under the pill — the same breadcrumb→content gap flow content gets — so a sticky panel (settings tab-nav, live preview) never tucks under the pill on scroll. */ --qm-dash-stick-top: 192px; }
.qm-dash-side { width: 276px; background: var(--ink-900); color: #cfd3d6; flex-shrink: 0; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; height: 100dvh; }
.qm-dash-brand { padding: 22px 22px; border-bottom: 1px solid rgba(255,255,255,.07); }
.qm-dash-brand img { max-height: 34px; width: auto; }
.qm-dash-role { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--qm-primary); margin-top: 6px; font-weight: 700; }
.qm-dash-nav { padding: 14px 12px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
.qm-dash-nav > a, .qm-dash-nav > .qm-dash-group { flex-shrink: 0; }
/* The sidebar's FOOT: a fixed band on the sidebar's own bottom edge (a sibling
   of the scrolling nav — flush, no margins, nothing below it). The band sits a
   shade DEEPER than the sidebar so it reads as the rail's base, and the link
   inside is the same soft tile the product's dark rails use for controls. */
.qm-dash-navfoot {
  flex-shrink: 0;
  padding: 12px;
  background: color-mix(in srgb, #000 28%, var(--ink-900));
  border-top: 1px solid rgba(255, 255, 255, .08);
}
/* Self-contained: the link lives OUTSIDE .qm-dash-nav now, so it carries the
   full tile itself rather than inheriting the nav's link rules. */
.qm-dash-navfoot .qm-dash-backsite {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin: 0; padding: 11px 14px;
  font-size: 14px; font-weight: 600; text-decoration: none;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .07);
  color: #cfd3d6;
  transition: background var(--t-fast), color var(--t-fast);
}
.qm-dash-navfoot .qm-dash-backsite i { width: 18px; text-align: center; }
.qm-dash-navfoot .qm-dash-backsite:hover { background: rgba(255, 255, 255, .14); color: #fff; }
.qm-dash-nav a { display: flex; align-items: center; gap: 12px; padding: 11px 14px; color: #aeb4b9; font-size: 14px; font-weight: 500; border-radius: var(--r-sm); margin-bottom: 2px; transition: all var(--t-fast); }
.qm-dash-nav a i { width: 18px; text-align: center; }
.qm-dash-nav a:hover { background: rgba(255,255,255,.06); color: #fff; }
.qm-dash-nav a.active { background: var(--qm-primary-600); color: #fff; box-shadow: var(--e-primary); }
/* Nested collapsible nav groups */
.qm-dash-group { margin-bottom: 2px; }
.qm-dash-group-btn { display: flex; align-items: center; gap: 12px; width: 100%; padding: 11px 14px; background: none; border: 0; cursor: pointer; color: #cfd3d6; font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; border-radius: var(--r-sm); transition: all var(--t-fast); }
.qm-dash-group-btn:hover { background: rgba(255,255,255,.06); color: #fff; }
.qm-dash-group-icon { width: 18px; text-align: center; color: var(--qm-primary); font-size: var(--fs-400); }
.qm-dash-group-label { flex: 1; text-align: left; }
.qm-dash-chevron { font-size: 10px; color: #8c9298; transition: transform var(--t); }
.qm-dash-group[data-open="1"] .qm-dash-chevron { transform: rotate(180deg); }
.qm-dash-group.has-active { background: rgba(255,255,255,.04); border-radius: var(--r-md); box-shadow: inset 3px 0 0 var(--qm-primary); }
.qm-dash-group.has-active .qm-dash-group-btn { color: #fff; }
.qm-dash-group-items { padding-left: 12px; margin: 2px 0 4px 13px; border-left: 1px solid rgba(255,255,255,.08); }
.qm-dash-sublink { font-size: 13.5px; }
.qm-dash-sublink.active { box-shadow: inset 2px 0 0 #fff, var(--e-primary); }
.qm-dash-nav::-webkit-scrollbar { width: 6px; }
.qm-dash-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.12); border-radius: 3px; }
/* Modern dashboard polish */
.qm-dash-side { background: linear-gradient(180deg, var(--ink-900) 0%, var(--ink-800) 100%); border-right: 1px solid rgba(255,255,255,.06); }
.qm-kpi::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--qm-grad); opacity: 0; transition: opacity var(--t); }
.qm-kpi:hover::before { opacity: 1; }
.qm-dash-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* Modal scroll-lock guard: a modal sets {overflow:hidden} on the scroller, which drops
   the sticky sidebar's scroll context and makes it jump upward on a scrolled page.
   While a modal is open, pin the sidebar to the viewport and hold the main column's
   256px offset so nothing behind the backdrop shifts. Covers both the Bootstrap modals
   (body.modal-open) and the media picker (body.qm-media-open). Desktop only — the sidebar
   is an off-canvas overlay on mobile, and it has no reserved width when collapsed. */
@media (min-width: 861px) {
    body.modal-open .qm-dash-side,
    body.qm-media-open .qm-dash-side { position: fixed; top: 0; inset-inline-start: 0; }
    body.modal-open:not(.qm-side-collapsed):not(.qm-fullscreen) .qm-dash-main,
    body.qm-media-open:not(.qm-side-collapsed):not(.qm-fullscreen) .qm-dash-main { margin-inline-start: 276px; }
}
/* Dashboard top-bar — glassmorphism (translucent white background + blur). */
/* Sticky chrome group — the top bar + the subheader band stick to the viewport top together, so
   the breadcrumb (in the subheader) stays pinned on scroll exactly as it did when it lived inside
   the top bar. One sticky wrapper avoids fragile per-breakpoint offset math between two bars. */
/* z-index 30, not 6: page-content bars (the floor plan's sticky toolbar, chart
   toolbars) sit in the 1–10 band, and at 6 the chrome TIED with them — later
   DOM order then painted the content bar over the avatar dropdown, slicing it.
   The app chrome outranks all content layers; modals (1050+) still clear it. */
.qm-dash-chrome { position: sticky; top: 0; z-index: 30; }
/* Deeper glass: the page genuinely reads through the chrome as it scrolls, and
   the bottom edge is a brand-tinted seam rather than a flat grey rule. */
.qm-dash-top { background: rgba(255,255,255,.78); backdrop-filter: blur(18px) saturate(1.6); -webkit-backdrop-filter: blur(18px) saturate(1.6); padding: 12px 28px; gap: 24px; min-height: 64px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 12px 32px -20px rgba(31, 6, 16, .25); position: relative; z-index: 2; }
.qm-dash-top::after { content: ''; position: absolute; inset-inline: 0; bottom: 0; height: 1px; background: linear-gradient(90deg, var(--line), color-mix(in srgb, var(--qm-primary) 30%, var(--line)) 45%, var(--line)); }
.qm-dash-top h1 { font-size: 19px; font-weight: 700; margin: 0; color: var(--ink-900); }
/* Right-side controls (language · notifications · account) grouped together,
evenly spaced, and pinned to the far right next to the profile avatar. */
.qm-dash-top-actions { display: flex; align-items: center; gap: 12px; margin-left: auto; }
/* Dashboard top-bar action icons — clean white pills with a subtle shadow and a
   soft branded hover, so the fullscreen / language / notification controls read
   as one polished toolbar set rather than flat tinted circles. Scoped to the
   dashboard so the storefront header is untouched. */
.qm-dash-top-actions .qm-icon-pill { background: rgba(255, 255, 255, .72); border-color: var(--line); color: var(--ink-600); box-shadow: var(--e-1); }
.qm-dash-top-actions .qm-icon-pill:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-600); transform: translateY(-1px); box-shadow: var(--e-2); }
.qm-dash-top-actions .qm-icon-pill:active { transform: translateY(0) scale(.96); }
/* Compact "current location" pill — the multi-branch switcher trigger. Deliberately distinct from
   the round account avatar: a bordered token with a brand location-pin + the active branch name.
   Collapses to the pin alone from the tablet tier down (see the topbar media queries). */
.qm-locpill { display: inline-flex; align-items: center; gap: 8px; height: 40px; max-width: 210px; padding: 0 13px 0 6px; border: 1px solid var(--line); border-radius: var(--r-pill); background: rgba(255, 255, 255, .72); box-shadow: var(--e-1); color: var(--ink-700); font-weight: 600; font-size: 13.5px; line-height: 1; cursor: pointer; transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); }
.qm-locpill:hover { transform: translateY(-1px); box-shadow: var(--e-2); }
.qm-locpill:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-600); }
/* Active-branch logo inside the pill — 28px round (compound selector so it beats .qm-brandmark--sm's 32px), a touch smaller than the 40px person avatar so the two never read as twins. */
.qm-locpill .qm-locpill-logo { width: 28px; height: 28px; font-size: 11px; flex-shrink: 0; }
.qm-locpill-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qm-locpill-caret { font-size: 11px; opacity: .55; flex-shrink: 0; }
/* Keep every topbar dropdown caret the same subtle weight (location pill, language, account) — the
   account caret had no opacity and otherwise read darker than the other two. */
.qm-dash-top .qm-account-btn i.fa-caret-down { opacity: .55; }
/* The person is a CONTROL like its neighbours: a quiet chip that washes on
   hover, the name over a small-caps role line. */
.qm-dash-top .qm-account-btn { padding: 4px 10px 4px 4px; border-radius: var(--r-pill); transition: background var(--t-fast); }
.qm-dash-top .qm-account-btn:hover { background: var(--qm-primary-50); }
.qm-dash-top .qm-account-name { display: block; font-size: 13.5px; font-weight: 700; color: var(--ink-900); line-height: 1.2; }
.qm-dash-top .qm-account-role { display: block; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-400); line-height: 1.3; }
/* Hairline separator between the location context and the utility icons in the topbar action row. */
.qm-topbar-div { flex-shrink: 0; width: 1px; height: 22px; background: var(--line); }
/* Modern sidebar-toggle hamburger — three crisp bars with a staggered middle line that squares up
   on hover. Scoped + named apart from the storefront .qm-burger so it stays a static hamburger
   (it collapses a persistent sidebar; it does not flip to a close-X). */
.qm-dash-navtoggle .qm-dash-burger { position: relative; display: block; width: 20px; height: 14px; }
.qm-dash-navtoggle .qm-dash-burger span { position: absolute; left: 0; height: 2px; border-radius: 2px; background: currentColor; transition: width var(--t-fast); }
.qm-dash-navtoggle .qm-dash-burger span:nth-child(1) { top: 0; width: 100%; }
.qm-dash-navtoggle .qm-dash-burger span:nth-child(2) { top: 6px; width: 62%; }
.qm-dash-navtoggle .qm-dash-burger span:nth-child(3) { top: 12px; width: 100%; }
.qm-dash-navtoggle:hover .qm-dash-burger span:nth-child(2) { width: 100%; }
/* The account toggle is a plain avatar + name + caret (transparent, no box), so it must
   NOT carry the pill shadow — on a border-less, radius-0, transparent button that shadow
   has nothing to hug and reads as a detached square shadow. Kept shadow-less to match the template. */
.qm-dash-body { padding: 28px; flex: 1; }

/* Subheader band — a slim strip under the top bar carrying the breadcrumb (left) and the page's
   primary action (right, under the avatar). A hairline beneath separates it from the content.
   justify-content:flex-end: the breadcrumb is absolutely positioned (out of flow), so the action
   is the only in-flow child and sits right; min-height keeps the band the same height whether or
   not a page fills the action slot, so every page's subheader matches. */
.qm-dash-subhead { position: relative; display: flex; align-items: center; justify-content: flex-end; gap: 16px; min-height: 64px; padding: 15px 28px; background: var(--surface, #fff); border-bottom: 1px solid var(--line); }
.qm-dash-subhead-action { display: inline-flex; align-items: center; gap: 10px; }
@media (max-width: 767.98px) { .qm-dash-subhead { padding: 12px 14px; min-height: 58px; } }
/* Breadcrumb pill — pulled UP (translateY(-50%) on the band's top edge) so it still straddles the
   line between the top bar and the subheader, the exact same visual position as before. Its z-index
   beats the sticky top bar (6), so its upper half sits over the bar's lower edge. Left-aligned to the
   content gutter. Only when a page supplies $breadcrumbs, so immersive screens stay clean. */
.qm-dash-crumbs { position: absolute; left: 28px; top: 0; transform: translate(0, -50%); z-index: 7; display: inline-flex; align-items: center; gap: 8px; margin: 0; padding: 7px 18px; max-width: calc(100% - 40px); background: var(--line-2, #ffeaf0); border: 0.5px solid var(--line); border-radius: var(--r-pill, 999px); box-shadow: 0 2px 8px rgba(31, 11, 18, .12); font-size: .85rem; line-height: 1.2; white-space: nowrap; }
@media (max-width: 767.98px) { .qm-dash-crumbs { left: 14px; padding: 6px 14px; } }
.qm-dash-crumbs a { color: var(--ink-500); text-decoration: none; transition: color var(--t-fast, .15s); }
.qm-dash-crumbs a:hover { color: var(--qm-primary); }
/* Separator = the app's live pulsing dot (reuses the KPI-live @keyframes qm-kpi-pulse), in the
   primary colour, instead of a slash. --kc drives both the dot fill and the expanding ring. */
.qm-dash-crumbs .qm-crumb-sep { width: 6px; height: 6px; border-radius: 50%; background: var(--qm-primary); --kc: var(--qm-primary); animation: qm-kpi-pulse 1.8s ease-out infinite; flex: 0 0 auto; }
.qm-dash-crumbs .qm-crumb-current { color: var(--ink-900); font-weight: 600; }

/* --- Chrome variant: page title in the subheader band -----------------------------
   The H1 + breadcrumb drop out of the top bar into the subheader, which becomes the page
   header: breadcrumb over the title on the start side, primary action on the end. Spacing
   uses the 8pt scale so it stays in step with the rest of the chrome. */
/* Title + action are one centered row (symmetric padding keeps the row on the band's vertical
   centre); the breadcrumb hangs off the lower edge, under the title. padding-bottom carries a
   touch more room so the hanging pill clears the content below. */
/* box-sizing:border-box + a fixed min-height keep the band the SAME height whether or not the
   page supplies a primary action — 44px (one action button) + 2×20px padding + 1px border = 85px —
   so title-only subheaders never look shorter/cramped next to ones that carry a button. */
.qm-dash-chrome--titled .qm-dash-subhead { box-sizing: border-box; min-height: 85px; justify-content: space-between; align-items: center; gap: var(--sp-4); padding: var(--sp-5) 28px; overflow: visible; }
.qm-dash-chrome--titled + .qm-dash-body { padding-top: calc(28px + var(--sp-2)); }
.qm-dash-chrome--titled .qm-dash-subhead-h1 { min-width: 0; margin: 0; font-size: 22px; font-weight: 700; line-height: 1.25; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-dash-chrome--titled .qm-dash-subhead-action { flex: 0 0 auto; }
/* With the title gone from the top bar it only carries the toggle + global actions, so slim it
   down — it now reads as a utility strip above the taller page-header subheader. */
.qm-dash-chrome--titled .qm-dash-top { padding-block: var(--sp-3); min-height: 0; }
/* Breadcrumb hangs on the LOWER edge of the subheader, under the title — the same "pill on the
   divider" treatment it had on the top bar, moved down (top:0 → bottom:0, pull-up → pull-down).
   It keeps the base pill styling + left:28px; the -50% pull leaves half of it below the line. */
.qm-dash-chrome--titled .qm-dash-crumbs { top: auto; bottom: 0; transform: translate(0, 50%); }
@media (max-width: 767.98px) {
  /* Title (shrinks with an ellipsis) + action stay on ONE row; the breadcrumb keeps hanging.
     Inline padding drops to 10px to line the title/breadcrumb up with the content cards (the
     body is padding:20px 10px here), and the bottom margin bumps to 16px to offset the body's
     smaller mobile top padding — so the breadcrumb→content gap stays the ~20px it is on desktop. */
  .qm-dash-chrome--titled .qm-dash-subhead { padding-inline: 10px; }
  .qm-dash-chrome--titled + .qm-dash-body { padding-top: calc(20px + var(--sp-4)); }
  .qm-dash-chrome--titled .qm-dash-subhead-h1 { font-size: 19px; }
  .qm-dash-chrome--titled .qm-dash-crumbs { left: 10px; }
  /* On mobile the primary action collapses to an icon-only 44px circle — removes the label
     clutter beside the title + hanging breadcrumb while keeping a full touch target. The label
     stays in the DOM (font-size:0, not display:none) so the accessible name is unchanged; :has()
     scopes this to icon-led buttons only, so any text-only action keeps its label. */
  .qm-dash-chrome--titled .qm-dash-subhead-action .qm-btn:has(> i, > svg) { width: 44px; min-width: 44px; height: 44px; min-height: 44px; padding: 0; gap: 0; font-size: 0; }
  .qm-dash-chrome--titled .qm-dash-subhead-action .qm-btn:has(> i, > svg) > i,
  .qm-dash-chrome--titled .qm-dash-subhead-action .qm-btn:has(> i, > svg) > svg { font-size: var(--fs-700); margin: 0 !important; }
}
/* Tighter side gutters on mobile so cards claim the narrow width, but keep a little more breathing
   room top and bottom; desktop keeps the roomy 28px all round.

   12px, not 10: a Bootstrap `.row` pulls itself out by half its gutter (g-4 = 1.5rem, so -12px each
   side) on the promise that its container carries that much padding to absorb it. At 10 the rows on
   34 dashboard views hung 2px past both edges and the page scrolled sideways — visible as a phone
   that drifts left when you swipe. Desktop's 28px always absorbed it, which is why it only ever
   showed on mobile. The container has to be >= the gutter it holds; never mask this with
   overflow-x: hidden, which leaves the layout broken underneath. */
@media (max-width: 767.98px) { .qm-dash-body { padding: 20px 12px; } }
/* Simple dashboard footer: a slim copyright bar at the bottom of the main column. The body above
   flex-grows, so it sits at the fold on short pages and after the content on long ones. Hidden in
   full-screen focus mode alongside the top bar. */
.qm-dash-foot { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 16px; padding: 16px 28px; border-top: 1px solid var(--line); color: var(--ink-400); font-size: var(--fs-400); }
.qm-dash-foot-link { color: var(--ink-500); font-weight: 600; text-decoration: none; }
.qm-dash-foot-link:hover { color: var(--qm-primary); }
body.qm-fullscreen .qm-dash-foot { display: none !important; }
@media (max-width: 575.98px) { .qm-dash-foot { padding: 14px 16px; } }
/* Snapshot KPI strip — a fixed number of cards per view (5 desktop / 3 tablet /
   1 mobile), so every strip is one clean row in either sidebar state; surplus KPIs
   are reached by dragging the strip. No visible scrollbar: swipe on touch, and
   click-and-drag with a mouse (kpi-slider.js sets .is-dragging). The small vertical
   padding gives the cards' hover-lift room so the scroll box never sprouts a stray
   vertical scrollbar. */
.qm-kpi-grid { display: flex; gap: 24px; margin-bottom: 22px; padding: 4px 0;
  overflow-x: auto; scroll-snap-type: x proximity;
  scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch; }
.qm-kpi-grid::-webkit-scrollbar { width: 0; height: 0; display: none; }
.qm-kpi-grid.is-draggable { cursor: grab; }
.qm-kpi-grid.is-dragging { cursor: grabbing; scroll-snap-type: none; user-select: none; }
.qm-kpi-grid > * { flex: 0 0 calc((100% - 4 * 24px) / 5); min-width: 0; scroll-snap-align: start; }
@media (max-width: 1199.98px) { .qm-kpi-grid > * { flex-basis: calc((100% - 3 * 24px) / 4); } }
@media (max-width: 991.98px) { .qm-kpi-grid > * { flex-basis: calc((100% - 2 * 24px) / 3); } }
@media (max-width: 575.98px) { .qm-kpi-grid > * { flex-basis: 100%; } }
.qm-kpi { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-5); box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t); position: relative; overflow: hidden; }
.qm-kpi:hover { transform: translateY(-3px); box-shadow: var(--e-3); }
/* Uppercase card label — always a single line (a run of cards in the same grid
row must never have some labels wrap to 2 lines and others sit on 1, which
throws every value number below out of alignment with its neighbours). A
label long enough to still overflow at this size fades with an ellipsis
rather than wrapping or spilling out of the card. */
.qm-kpi-label { font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px; color: var(--ink-400); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-kpi-value { font-size: 30px; font-weight: 800; color: var(--ink-900); margin-top: 6px; letter-spacing: -.02em; }
.qm-kpi-value.qm-kpi-accent { color: var(--qm-primary); }
/* Vibrant KPI cards (dashboard) — accent-tinted with a springy watermark icon,
mirroring the storefront cuisine cards. The whole card links to its section and
carries a small movement pill for an at-a-glance, analytical read. */
a.qm-kpi { text-decoration: none; color: inherit; display: block; }
/* .qm-kpi-grid-5 is folded into the slider above — every strip now shows 5 per view. */
.qm-kpi-rich { position: relative; overflow: hidden; isolation: isolate; display: flex; flex-direction: column; padding: var(--sp-5); border-radius: var(--r-lg);
  background: radial-gradient(135% 130% at 100% 0, color-mix(in srgb, var(--kc) 15%, #fff) 0, var(--surface) 58%);
  border: 1px solid color-mix(in srgb, var(--kc) 22%, #fff); box-shadow: var(--e-1);
  transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-kpi-rich::before { content: none; }
.qm-kpi-rich:hover { transform: translateY(-5px); border-color: color-mix(in srgb, var(--kc) 42%, #fff); box-shadow: 0 18px 34px color-mix(in srgb, var(--kc) 24%, transparent); }
.qm-kpi-bg { position: absolute; right: -8px; bottom: -14px; z-index: 0; font-size: 76px; line-height: 1; color: var(--kc); opacity: .12; transform: rotate(-9deg); transition: transform .5s cubic-bezier(.34,1.45,.5,1), opacity var(--t); pointer-events: none; }
.qm-kpi-rich:hover .qm-kpi-bg { transform: rotate(3deg) scale(1.16) translateY(-5px); opacity: .2; }
/* Fixed-height label zone (room for 2 lines) so cards in the same grid row
line up their big value number at the same Y-position whether their own
label happens to wrap to one line or two - a short label wrapping to a
single line must not sit any higher than a neighbour whose label wraps twice. */
.qm-kpi-rich .qm-kpi-label { position: relative; z-index: 1; display: block; max-width: 100%; }
.qm-kpi-rich .qm-kpi-value { position: relative; z-index: 1; font-size: 32px; margin-top: 3px; line-height: 1.04; }
.qm-kpi-meta { position: relative; z-index: 1; display: flex; align-items: center; gap: 8px; margin-top: 9px; flex-wrap: wrap; min-height: 22px; }
.qm-kpi-cap { font-size: 11.5px; color: var(--ink-400); font-weight: 500; }
.qm-kpi-trend { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 700; padding: 2px 9px; border-radius: var(--r-pill); }
.qm-kpi-trend.is-up { color: var(--ok); background: var(--ok-bg); }
.qm-kpi-trend.is-down { color: #c0341d; background: #fdecec; }
.qm-kpi-trend.is-flat { color: var(--ink-500); background: var(--line-2); }
.qm-kpi-live { display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; font-weight: 600; color: color-mix(in srgb, var(--kc) 64%, var(--ink-700)); }
.qm-kpi-live::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--kc); animation: qm-kpi-pulse 1.8s ease-out infinite; }
@keyframes qm-kpi-pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--kc) 55%, transparent); } 70% { box-shadow: 0 0 0 7px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
.qm-kpi--blue { --kc: #2484c6; }
.qm-kpi--amber { --kc: #d98c1c; }
.qm-kpi--green { --kc: #1f9d4d; }
.qm-kpi--violet { --kc: #8a45b8; }
.qm-kpi--rose { --kc: var(--qm-primary); }
.qm-kpi--teal { --kc: #0f9b8e; }
.qm-kpi--orange { --kc: #e8590c; }
.qm-kpi--pink { --kc: #e64980; }
.qm-kpi--slate { --kc: #5b6675; }
/* Sparkline (trend line) along the bottom of a KPI card — full-bleed to the card
edges; the watermark icon shifts to the top corner so the two don't collide. */
.qm-kpi-spark { position: relative; z-index: 1; margin: var(--sp-3) calc(var(--sp-5) * -1) calc(var(--sp-5) * -1); height: 42px; line-height: 0; }
.qm-kpi-rich.has-spark .qm-kpi-bg { top: -16px; bottom: auto; right: -4px; font-size: 58px; opacity: .1; }
/* Inline country flag beside a cuisine name in dashboard tables. */
.qm-cuisine-flag { width: 21px; height: 14px; border-radius: 3px; object-fit: cover; vertical-align: -2px; margin-right: 7px; box-shadow: 0 0 0 1px rgba(0,0,0,.07); }
/* Cuisine flag overlaid as a corner badge on a restaurant logo (restaurant_brandmark). */
.qm-idflag { position: relative; display: inline-flex; flex-shrink: 0; }
/* WHICH BRANCH YOU ARE CURRENTLY IN — a dot on the corner of its logo, not a word.
   The row already carries a pill saying whether that venue is trading, and labelling the current
   one "Active" too made one word answer two different questions: an owner reading it could not
   tell whether it meant "this branch is live" or "this is the one you are looking at". The badge
   sits where the cuisine flag sits (same corner, same ring against the surface), so the switcher
   keeps one anatomy. Green because it is a healthy state — the same reading the pill palette
   already gives green. */
.qm-idflag-here { position: absolute; right: -2px; top: -2px; width: 10px; height: 10px; border-radius: 50%; background: var(--success, #16a34a); box-shadow: 0 0 0 2px var(--surface); }
.qm-idflag > .qm-idflag-badge { position: absolute; right: -3px; bottom: -3px; width: 18px; height: 12px; margin: 0; border-radius: 3px; box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px rgba(0,0,0,.08); }
/* Coloured status/role dot before a Choices.js dropdown option (data-choices-dots),
   mirroring the storefront service-mode dot. Colour keyed to the option value from
   the same tokens the role pills use — one source of truth. */
.qm-optdot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; vertical-align: middle; flex-shrink: 0; background: var(--ink-300); }
.qm-optdot--super_admin   { background: var(--qm-primary-700); }
.qm-optdot--vendor        { background: var(--info); }
.qm-optdot--manager_staff { background: var(--violet); }
.qm-optdot--kitchen_staff { background: var(--orange); }
.qm-optdot--host_staff    { background: var(--teal); }
.qm-optdot--waiter_staff  { background: var(--pink); }
.qm-optdot--driver        { background: var(--indigo); }
.qm-optdot--customer      { background: var(--slate); }
.qm-optdot--active    { background: var(--ok); }
.qm-optdot--pending   { background: var(--warn); }
.qm-optdot--suspended { background: var(--danger); }
/* Filter selects sit in a narrow field, where Choices.js' default word-break:break-all
   shatters long labels ("Restaurant Manager") character-by-character. Keep each option
   on one line — the dropdown grows to fit — and truncate the selected value instead. */
.qm-of-field .choices__list--single .choices__item { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; word-break: normal; }
.qm-of-field .choices__list--dropdown .choices__item,
.qm-of-field .choices__list[aria-expanded] .choices__item { white-space: nowrap; word-break: normal; }
.qm-of-field .choices__list--dropdown { min-width: 100%; width: max-content; }
/* One field height across EVERY filter bar (admin + vendor) so the whole set is consistent, whether
   its selects are Choices-enhanced (users, staff) or plain native selects (orders, restaurants,
   reservations, reviews, customers). Choices boxes render ~48px and shouldn't shrink, so every plain
   select, search input and reset is matched up to 48px. On Choices bars the search placeholder also
   picks up the dimmed Choices placeholder tone beside it. */
.qm-order-filters .form-select,
.qm-order-filters .form-control,
.qm-order-filters .qm-filter-reset { min-height: 48px; }
.qm-order-filters:has(.choices) .form-control::placeholder { color: var(--ink-700); opacity: .5; }
/* Panel KPI card — a cleaner 2-column layout: a left rail (sparkline on top, a
"View all" link above a faint lower-left icon) + a right column (title, number,
description). Same accent system as the other cards. */
.qm-kpi-panel { display: flex; flex-direction: column; padding: 16px 18px; }
.qm-kpi-panel.has-spark { padding-bottom: 0; }
.qm-kpi-panel .qm-kpi-main { position: relative; z-index: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; text-align: left; }
.qm-kpi-panel .qm-kpi-value { font-size: 30px; line-height: 1.04; margin-top: 1px; }
.qm-kpi-panel .qm-kpi-meta { justify-content: flex-start; min-height: 0; margin-top: 1px; }
.qm-kpi-panel .qm-kpi-viewall { align-self: flex-start; margin-top: 9px; font-size: 11.5px; font-weight: 700; color: var(--kc); text-decoration: none; display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.qm-kpi-panel .qm-kpi-viewall:hover { text-decoration: underline; }
.qm-kpi-panel .qm-kpi-viewall i { font-size: 10px; transition: transform var(--t-fast); }
.qm-kpi-panel:hover .qm-kpi-viewall i { transform: translateX(3px); }
/* Sparkline as a full-bleed strip along the card's BOTTOM edge — pinned there
   with an auto top margin, so a card without a "View all" link draws its line
   at the same height as its neighbours that have one (grid-stretched cards
   otherwise left the shorter card's spark floating mid-card). The main column
   keeps the 12px breathing gap the fixed margin used to provide. */
.qm-kpi-panel.has-spark .qm-kpi-main { padding-bottom: 12px; }
.qm-kpi-panel .qm-kpi-spark { position: static; margin: auto -18px 0; height: 40px; }
.qm-kpi-rich.qm-kpi-panel .qm-kpi-bg { top: 14px; bottom: auto; right: -6px; left: auto; font-size: 48px; opacity: .1; }
/* Rich panel card (rows/pills present, e.g. the Dashboard's Platform-snapshot
row) — rows need the FULL card width, so there is no side rail here; the
sparkline (when present) moves into a compact header line instead, alongside
the label and the "Full report" link, so it never competes with the number. */
.qm-kpi-panel.qm-kpi-panel--rich { display: block; padding: var(--sp-5); min-height: 0; }
.qm-kpi-panel--rich .qm-kpi-rich-head { position: relative; z-index: 1; display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.qm-kpi-panel--rich .qm-kpi-label { flex: 1; min-width: 0; min-height: 0; }
.qm-kpi-panel--rich .qm-kpi-mini-spark { flex: 0 0 auto; width: 54px; height: 22px; line-height: 0; }
.qm-kpi-panel--rich .qm-kpi-viewall { flex: 0 0 auto; margin: 0; font-size: 11.5px; font-weight: 700; color: var(--kc); text-decoration: none; display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.qm-kpi-panel--rich .qm-kpi-viewall:hover { text-decoration: underline; }
.qm-kpi-panel--rich .qm-kpi-viewall i { font-size: 10px; transition: transform var(--t-fast); }
.qm-kpi-panel--rich:hover .qm-kpi-viewall i { transform: translateX(3px); }
.qm-kpi-panel--rich .qm-kpi-value { display: block; font-size: 27px; margin-top: 2px; }
.qm-kpi-panel--rich .qm-kpi-meta { margin-top: 6px; }
.qm-kpi-panel--rich .qm-kpi-rows { margin-top: 14px; }
.qm-kpi-panel--rich .qm-kpi-pillrow { margin-top: 13px; }
@media (prefers-reduced-motion: reduce) { .qm-kpi-bg, .qm-kpi-rich { transition: none; } .qm-kpi-live::before { animation: none; } }
/* Optional breakdown rows + status-pill footer for a 'panel' KPI card - the
SAME .qm-kpi-panel shell used everywhere else doubles as a richer rollup card
(e.g. the Dashboard's "Platform snapshot" row) by switching on these two extra
sections; no second card type to keep in sync. */
.qm-snap-section-title { display: flex; align-items: center; gap: 9px; font-size: var(--fs-700); font-weight: 700; color: var(--ink-900); margin: 2px 0 14px; }
/* Solid brand-colour accent bar before the Platform snapshot heading. */
.qm-snap-title-bar { flex: 0 0 auto; width: 4px; height: 18px; border-radius: var(--r-pill); background: var(--qm-primary); }
.qm-snap-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); gap: 20px; margin-bottom: 26px; }
.qm-kpi-rows { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.qm-kpi-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 12.5px; color: var(--ink-600); }
.qm-kpi-row-label { display: flex; align-items: center; gap: 7px; min-width: 0; }
.qm-kpi-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: var(--kc); }
.qm-kpi-row-val { font-weight: 700; color: var(--ink-900); white-space: nowrap; }
.qm-kpi-pillrow { position: relative; z-index: 1; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; padding-top: var(--sp-3); border-top: 1px solid color-mix(in srgb, var(--kc) 14%, var(--line)); }
/* Shared header icon-chip — carries the snapshot row's "chip + title" motif down
into the chart-card and table-card headers below it, so the page reads as one
consistent design instead of a fancier section sitting above a flatter one.
Standalone (fixed hex, not the --kc token) since these headers sit outside any
.qm-kpi--* accent scope. Opt-in via .qm-card-title-ico on the heading itself. */
.qm-card-title-ico { display: inline-flex; align-items: center; gap: 9px; }
.qm-head-chip { width: 26px; height: 26px; border-radius: 8px; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; flex: 0 0 auto; }
.qm-head-chip--blue { background: color-mix(in srgb, var(--acc-blue) 16%, #fff); color: var(--acc-blue); }
.qm-head-chip--green { background: color-mix(in srgb, var(--acc-green) 16%, #fff); color: var(--acc-green); }
.qm-head-chip--violet { background: color-mix(in srgb, var(--acc-violet) 16%, #fff); color: var(--acc-violet); }
.qm-head-chip--teal { background: color-mix(in srgb, var(--acc-teal) 16%, #fff); color: var(--acc-teal); }
.qm-head-chip--rose { background: color-mix(in srgb, var(--qm-primary) 16%, #fff); color: var(--qm-primary); }
.qm-head-chip--amber { background: color-mix(in srgb, var(--acc-amber) 16%, #fff); color: var(--acc-amber); }
.qm-head-chip--orange { background: color-mix(in srgb, var(--acc-orange) 16%, #fff); color: var(--acc-orange); }
.qm-head-chip--pink { background: color-mix(in srgb, var(--acc-pink) 16%, #fff); color: var(--acc-pink); }
.qm-head-chip--slate { background: color-mix(in srgb, var(--acc-slate) 16%, #fff); color: var(--acc-slate); }
/* Dashboard charts (ApexCharts, bundled locally) */
.qm-charts-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 24px; margin-bottom: 26px; }
.qm-chart-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 20px 20px 8px; box-shadow: var(--e-1); min-width: 0; }
/* Standalone chart card (outside .qm-charts-grid) — match the .qm-card rhythm so
the next section doesn't butt up against it. */
.qm-chart-card-solo { margin-bottom: var(--sp-5); }
/* A row of equal-height cards has the same problem for a different reason: .h-100 sizes the
card to a column whose height is already settled, so the card's own bottom margin falls
outside the row box and the following section sits flush against it. Carry the rhythm on the
row itself, where nothing can swallow it. */
.qm-card-row { margin-bottom: var(--sp-5); }
/* Heading on one side, the period switcher on the other. Centre (not baseline)
   alignment keeps the pill centred against a two-line heading, and the larger
   gap/margin stops the switcher crowding the heading or the plot. Some cards also
   caption their chart; that caption is drawn by ApexCharts on the plot's own
   toolbar row, so it never appears in this head. */
.qm-chart-card-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px; row-gap: 6px; margin-bottom: 16px; }
.qm-chart-card-head h2, .qm-chart-card-head h3 { font-size: var(--fs-700); font-weight: 700; color: var(--ink-900); margin: 0; line-height: 1.2; }
/* Period switcher in a chart-card head — a segmented pill toggle that mirrors the
storefront's Delivery/Pickup control (the active pill uses the brand gradient). */
.qm-period { display: inline-flex; flex-wrap: wrap; flex-shrink: 0; max-width: 100%; background: var(--line-2); border-radius: var(--r-pill); padding: 3px; gap: 2px; }
.qm-period-btn { border: 0; background: transparent; border-radius: var(--r-pill); padding: 5px 13px; font-size: 12px; font-weight: 700; color: var(--ink-500); cursor: pointer; line-height: 1.4; white-space: nowrap; transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast); }
.qm-period-btn:hover { color: var(--qm-primary-600); }
.qm-period-btn.is-active { background: var(--qm-grad); color: #fff; box-shadow: var(--e-primary); }
.qm-period-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
/* Compact chart range picker: the active pill shows the current window (mirrors the page-level
   master) and the caret pill opens a styled menu of the other ranges. Reuses the period-bar look
   so it reads like the master, but stays two pills wide - it fits even the narrow chart cards. */
.qm-chart-period .qm-period-btn.is-active { cursor: default; }
.qm-period-drop { position: relative; display: inline-flex; }
.qm-period-drop-toggle { display: inline-flex; align-items: center; padding-left: 9px; padding-right: 9px; }
.qm-period-drop-toggle[aria-expanded="true"] { color: var(--qm-primary-600); }
/* On phones the current-range pill makes the control too wide for the chart header - a growing
   label (7D -> 14D) spills it onto the next row - so show only the dropdown caret. The menu
   still marks the current range when it is opened. */
@media (max-width: 575.98px) {
  .qm-chart-period .qm-cp-tab { display: none; }
}
/* Per-chart PILL bar collapses to the caret dropdown on phones (dashboards, payouts, plans and
   item analytics). Desktop shows the pills; the sibling caret dropdown stays hidden until then. */
.qm-period-mobile { display: none; }
@media (max-width: 575.98px) {
  .qm-period-pills { display: none; }
  .qm-period-mobile { display: inline-flex; }
}
.qm-period-menu { position: absolute; inset-inline-end: 0; top: calc(100% + 6px); z-index: 30; min-width: 132px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-2); display: flex; flex-direction: column; gap: 2px; }
.qm-period-menu-item { display: block; width: 100%; padding: 8px 11px; border: 0; border-radius: var(--r-sm); background: none; color: var(--ink-700); font-size: var(--fs-400); font-weight: 600; text-align: start; cursor: pointer; transition: background var(--t-fast), color var(--t-fast); }
.qm-period-menu-item:hover { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-period-menu-item.is-active { background: var(--qm-grad); color: #fff; }
/* Segmented view toggle (e.g. List | Calendar) — one pill container, gradient
   active state; matches .qm-period and the calendar's own view switcher, and is
   the height of a .qm-btn so it aligns in a toolbar row. */
.qm-seg { display: inline-flex; align-items: center; gap: 3px; padding: 4px; min-height: 44px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); }
.qm-seg-btn { display: inline-flex; align-items: center; gap: 7px; border: 0; background: transparent; border-radius: var(--r-pill); padding: 8px 18px; font-size: 14px; font-weight: 600; line-height: 1.2; color: var(--ink-500); white-space: nowrap; cursor: pointer; transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast); }
.qm-seg-btn:hover { color: var(--qm-primary-600); }
.qm-seg-btn.active { background: var(--qm-grad); color: #fff; box-shadow: var(--e-primary); }
.qm-seg-btn.active:hover { color: #fff; }
.qm-seg-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-seg-btn i { font-size: var(--fs-400); }
.qm-chart-col-12 { grid-column: span 12; }
.qm-chart-col-10 { grid-column: span 10; }
/* Two columns is 273px on a 1706px grid - the restaurant card's own width, so the
   card keeps its normal proportions when it sits beside a chart row. */
.qm-chart-col-2 { grid-column: span 2; }
.qm-chart-col-8 { grid-column: span 8; }
.qm-chart-col-6 { grid-column: span 6; }
.qm-chart-col-5 { grid-column: span 5; }
.qm-chart-col-4 { grid-column: span 4; }
.qm-chart-col-3 { grid-column: span 3; }
.qm-chart { min-height: 0; }
/* ApexCharts export menu (the single "Menu" icon enabled in dashboard-charts.js) —
restyled from the library's raw grey/black defaults to match the app's tokens. */
.qm-chart-figure { position: relative; margin: 0; }
.apexcharts-toolbar { top: -2px !important; right: 2px !important; z-index: 3; }
/* THE CHART IS A BOX, NOT A LAYER OF THE PAGE.
   ApexCharts' own sheet puts the toolbar at z-index 11 and wins over the 3 above, and nothing
   between it and <body> established a stacking context — .apexcharts-canvas and .qm-chart-figure
   are both position:relative with z-index:auto. So an 11 competed at the root against the sticky
   dashboard chrome's 6 and painted the burger menu over the subheader and top bar on every page
   carrying a chart.

   isolation:isolate makes the canvas its own stacking context: whatever ApexCharts numbers its
   internals, none of it can escape the chart's own box. It changes no layout, needs no !important,
   and keeps the toolbar's menu correctly above the plot inside the chart. */
.apexcharts-canvas { isolation: isolate; }
.apexcharts-menu-icon { padding: 4px; border-radius: var(--r-md); transition: background var(--t-fast); }
.apexcharts-menu-icon:hover { background: var(--surface-2); }
.apexcharts-menu-icon svg { fill: var(--ink-400); transition: fill var(--t-fast); }
.apexcharts-menu-icon:hover svg { fill: var(--qm-primary); }
.apexcharts-menu { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-3); padding: 4px; min-width: 150px; top: 26px !important; }
.apexcharts-menu-item { padding: 7px 12px; font-size: 12.5px; font-family: inherit; color: var(--ink-700); border-radius: 6px; }
.apexcharts-menu-item:hover { background: var(--qm-primary-50); color: var(--qm-primary-600); }
/* Chart no-data placeholder — rendered inside a .qm-chart mount (or in place of it)
when a chart has zero rows, so fresh/zero-data dashboards never show a blank box.
Pairs with the .qm-chart-figure a11y wrapper below. */
.qm-chart-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; height: 260px; color: var(--ink-400); font-size: .9rem; text-align: center; }
.qm-chart-empty i { font-size: 30px; color: var(--ink-300); }
/* Chart a11y wrapper — wrap each .qm-chart mount in <figure class="qm-chart-figure">
with a <figcaption> text alternative summarising the trend for assistive tech. */
/* Visually-hidden caption variant (when the heading already names the chart and the
figcaption only needs to exist for screen readers). */
.qm-chart-figure .qm-sr-cap { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
@media (max-width: 1100px) { .qm-chart-col-12, .qm-chart-col-8, .qm-chart-col-6, .qm-chart-col-5, .qm-chart-col-4, .qm-chart-col-3 { grid-column: span 12; } }
.qm-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-5); margin-bottom: var(--sp-5); }
.qm-card :is(h1,h2,h3,h4,h5,h6) { font-size: var(--fs-700); font-weight: 700; margin: 0 0 16px; color: var(--ink-900); }
/* Card anatomy — header / body / footer split by a hairline. The header carries the
   title (+ any status pill) with the enable toggle riding to its right; the footer
   pins the primary button to the inline-end. Each divider is drawn on the block that
   FOLLOWS it (border-top), so a collapsed body simply drops its own divider with it. */
.qm-card-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3); }
.qm-card-head h2 { margin: 0; }
/* The header toggle is a compact inline control, not a full-width settings row — the
   .qm-settings-panel .qm-check rule stretches it to 100% otherwise, dropping it below
   the title. Keep it auto-width so it rides at the header's inline-end, centred.
   SPECIFICITY, not just presence: that panel rule matches with the same weight and sits
   further down this file, so it won on source order and this override never applied. It only
   looked as though it had, because the panel rule is `justify-content: space-between` — which
   shoved the input to the far end of a full-width label, exactly where a right-aligned control
   belongs. Remove the label's text and the illusion goes with it: a lone input in a 100%-wide
   flex row lands at the START. The second selector below outweighs the panel rule, so the label
   really does shrink to its control and .qm-card-head's align-items centres it. */
.qm-card-head .qm-check,
.qm-settings-panel .qm-card-head .qm-check { width: auto; }
.qm-card-body { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.qm-card-body > :last-child { margin-bottom: 0; }
.qm-card-foot { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); display: flex; justify-content: flex-end; gap: var(--sp-2); }
/* Bootstrap's .table-responsive only declares overflow-x: auto, but per the CSS
overflow spec that forces the OTHER axis to compute as auto too - so a table
left even 1px taller than its wrapper by sub-pixel row rounding silently grows a
vertical scrollbar nobody asked for. Declaring `visible` cannot undo this: the
spec computes `visible` back to `auto` for exactly this pairing, so the axis has
to be stated as hidden to be honoured. This clips nothing that `auto` did not
already clip - it only drops the scrollbar. Horizontal scroll on narrow screens
stays the one behaviour this class provides. */
.table-responsive { overflow-y: hidden; }
/* `overflow-x: auto` makes this a horizontal scroll container, yet a table wider than the wrapper
   (e.g. the Navigation builder's six editable columns on a phone) STILL contributes its full
   intrinsic width to the document's scrollable width - so the page itself grows a phantom strip of
   empty space to the right on narrow screens, even though the table scrolls inside its own box.
   `contain: layout` isolates the wrapper's inner layout so that intrinsic width can no longer inflate
   the page, while leaving paint uncontained - the row-action menus that open outside the table are
   not clipped. The horizontal scroll is unchanged. */
.table-responsive { contain: layout; }
/* Let out of its clip while a row menu is open (qm-table-tools.js adds the class only when
   nothing scrolls sideways), so the last row's menu is not swallowed by the wrapper. */
.table-responsive.qm-rowmenu-open { overflow: visible; contain: none; }
/* A rule above the header and one above the footer bracket the table, so head, body and footer
   read as three bands rather than one run of rows. The header already carries a heavier 2px rule
   beneath it, which keeps the body distinct from the labels above it. */
.qm-table { width: 100%; border-collapse: collapse; font-size: 14px; border-top: 1px solid var(--line-2); }
.qm-table th { text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: .5px; color: var(--ink-600); padding: 11px 12px; border-bottom: 2px solid var(--line-2); }
.qm-table td { padding: 13px 12px; border-bottom: 1px solid var(--line-2); color: var(--ink-700); vertical-align: middle; }
.qm-table tbody tr { transition: background var(--t-fast); }
.qm-table tbody tr:hover { background: var(--surface-2); }
.qm-table tr:last-child td { border-bottom: 0; }
/* Sortable table headers (assets/js/qm-sortable-table.js opts in via
data-sortable on the <table> + data-sort="text|number|date" per <th>). */
.qm-table th[data-sort] { cursor: pointer; user-select: none; white-space: nowrap; transition: color var(--t-fast); }
.qm-table th[data-sort]:hover { color: var(--qm-primary); }
.qm-table th[data-sort]:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-sort-ico { margin-left: 6px; font-size: 10px; color: var(--ink-400); opacity: .55; }
.qm-table th[data-sort].is-sorted { color: var(--qm-primary); }
.qm-table th[data-sort].is-sorted .qm-sort-ico { color: var(--qm-primary); opacity: 1; }
.qm-pill { display: inline-block; font-size: 11px; font-weight: 700; padding: 3px 10px; border-radius: var(--r-pill); }
.qm-pill-green { background: var(--ok-bg); color: var(--ok); }
.qm-pill-amber { background: var(--warn-bg); color: var(--warn); }
.qm-pill-red { background: var(--danger-bg); color: var(--danger); }
.qm-pill-blue { background: var(--info-bg); color: var(--info); }
.qm-pill-grey { background: #eef0f2; color: #5c6268; } /* #5c6268 on #eef0f2 ≈ 5.4:1 — WCAG AA */

/* Per-role badge colours (reuse .qm-pill sizing). One distinct hue per role, kept clear
   of the status pills (green/amber/red) they sit beside in the same tables. Emitted by
   role_pill() / role_pill_class() in core/Helpers.php — the single source of truth. */
.qm-role-super_admin   { background: var(--qm-primary-50); color: var(--qm-primary-700); }
.qm-role-vendor        { background: var(--info-bg);   color: var(--info); }
.qm-role-manager_staff { background: var(--violet-bg); color: var(--violet); }
.qm-role-kitchen_staff { background: var(--orange-bg); color: var(--orange); }
.qm-role-host_staff    { background: var(--teal-bg);   color: var(--teal); }
.qm-role-waiter_staff  { background: var(--pink-bg);   color: var(--pink); }
.qm-role-driver        { background: var(--indigo-bg); color: var(--indigo); }
.qm-role-customer      { background: var(--slate-bg);  color: var(--slate); }

/* Subscription plan-tier tags — ascending premium, one colour per tier (keyed on slug).
   Emitted by plan_tag() / plan_tier_class(). */
.qm-plan-tag { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; padding: 2px 9px; border-radius: var(--r-pill); }
.qm-plan-tier-starter    { background: var(--slate-bg);     color: var(--slate); }
.qm-plan-tier-pro        { background: var(--info-bg);      color: var(--info); }
.qm-plan-tier-business   { background: var(--violet-bg);    color: var(--violet); }
.qm-plan-tier-enterprise { background: var(--tier-gold-bg); color: var(--tier-gold); }
.qm-plan-tier-neutral    { background: #eef0f2;             color: #5c6268; }
.qm-pill-violet { background: var(--violet-bg); color: var(--violet); }

/* --- Alerts: flash messages + inline notices aligned to the design tokens
(same semantic palette as the pills above — no default component look). ------ */
.alert { position: relative; display: flex; align-items: flex-start; gap: 12px; border: 1px solid var(--line); border-inline-start: 3px solid var(--ink-700); border-radius: var(--r-md); padding: 13px 16px; font-size: 14px; line-height: 1.5; background: var(--surface-2); color: var(--ink-900); box-shadow: var(--e-2); animation: qm-alert-in var(--t-fast) ease both; }
@keyframes qm-alert-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .alert { animation: none; } }
.alert-success { background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok) 20%, #fff); border-inline-start-color: var(--ok); }
.alert-warning { background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 20%, #fff); border-inline-start-color: var(--warn); }
.alert-danger { background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 20%, #fff); border-inline-start-color: var(--danger); }
.alert-info { background: var(--info-bg); border-color: color-mix(in srgb, var(--info) 20%, #fff); border-inline-start-color: var(--info); }
.alert a { color: inherit; font-weight: 700; }
.alert strong { color: inherit; }
/* Circular type icon badge for flash messages (partials/flash.php). Inline notices
   keep any contextual icon of their own; the card styling above applies to both. */
.qm-alert-ico { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-400); line-height: 1; color: #fff; }
.qm-alert-body { flex: 1 1 auto; min-width: 0; padding-top: 3px; }
.alert-success .qm-alert-ico { background: var(--ok); }
.alert-warning .qm-alert-ico { background: var(--warn); }
.alert-danger .qm-alert-ico { background: var(--danger); }
.alert-info .qm-alert-ico { background: var(--info); }
.qm-row-unread { background: #fffaf0; }
.qm-row-unread td:first-child { box-shadow: inset 3px 0 0 var(--qm-primary); }
@media (max-width: 860px) {
  .qm-dash-side { position: fixed; left: -290px; z-index: 1000; visibility: hidden; transition: left var(--t), visibility var(--t); }
  .qm-dash-side.open { left: 0; visibility: visible; }
}

/* Mobile drawer backdrop — sits below the open sidebar (z 1000) but above page
content. Mirrors .qm-panel-backdrop at a lower z-index; mobile-only, matching
the @media (max-width:860px) drawer breakpoint. JS toggles .show on open/close. */
.qm-dash-backdrop { display: none; }
@media (max-width: 860px) {
  .qm-dash-backdrop { display: block; position: fixed; inset: 0; background: rgba(31,11,18,.45); opacity: 0; visibility: hidden; transition: opacity var(--t); z-index: 999; }
  .qm-dash-backdrop.show { opacity: 1; visibility: visible; }
}

/* Before-title sidebar toggle: a drawer toggle on mobile, a collapse toggle on
desktop (see app.js). Borderless and background-free so it blends into the topbar
(no pill/circle); a subtle rounded-square tint + brand colour appear on hover. */
.qm-dash-navtoggle { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; padding: 0; background: none; border: 0; border-radius: var(--r-md); color: var(--ink-700); font-size: var(--fs-900); cursor: pointer; transition: color var(--t-fast), background var(--t-fast); }
.qm-dash-navtoggle:hover { color: var(--qm-primary); background: var(--qm-primary-50); }
.qm-dash-navtoggle:active { transform: scale(.94); }
.qm-dash-navtoggle:focus-visible { outline: 3px solid var(--qm-primary); outline-offset: 2px; }
.qm-dash-top h1 { min-width: 0; }
/* The account avatar in the dashboard topbar sits next to 40px action pills — size it up to match. */
.qm-dash-top .qm-avatar { width: 40px; height: 40px; }
/* Dashboard account: avatar + name/role + caret on wide screens; from the tablet tier down it
   collapses to just the 40px avatar — name AND role AND caret hidden together — so it is never
   left showing the role stuck to the avatar without its name or arrow. */
@media (max-width: 991.98px) {
  .qm-dash-top .qm-account-id,
  .qm-dash-top .qm-account-btn > i.fa-caret-down { display: none; }
  .qm-dash-top .qm-account-btn { gap: 0; padding: 0; width: 40px; height: 40px; justify-content: center; }
  /* Location pill collapses to the pin alone, matching the account avatar beside it. */
  .qm-locpill { max-width: none; width: 40px; padding: 0; justify-content: center; }
  .qm-locpill-name, .qm-locpill-caret { display: none; }
}

/* Phone-width topbar: the desktop action row (fullscreen + language + bell +
   named account button) is too wide for a 375px viewport and would push the
   dashboard into horizontal scroll. Compress it — the page title truncates, the
   large-display fullscreen toggle is dropped, and the account name / language
   label collapse to their avatar + flag (both still fully usable via the menus). */
@media (max-width: 575.98px) {
  .qm-dash-top { padding: 10px 14px; gap: 10px; }
  /* Let the title group shrink (min-width defaults to auto = content width, which
     would otherwise push the actions off-screen) so a long page heading truncates
     with an ellipsis instead of overflowing. */
  .qm-dash-top > .d-flex { min-width: 0; }
  .qm-dash-top h1 { font-size: 17px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .qm-dash-top .qm-dash-top-actions { gap: 8px; flex-shrink: 0; }
  .qm-dash-top .qm-dash-fullscreen { display: none; }
  .qm-dash-top .qm-account-btn > span { display: none; }
  .qm-dash-top .qm-lang-switch .qm-icon-pill > span { display: none; }
  .qm-topbar-div { display: none; }
}

/* Desktop collapse: the sidebar slides away to the left and the main column fills
the freed width. Scoped above the mobile drawer breakpoint so the two never fight. */
@media (min-width: 861px) {
  .qm-dash-side { transition: width var(--t), opacity var(--t), visibility var(--t); }
  body.qm-side-collapsed .qm-dash-side { width: 0; min-width: 0; opacity: 0; overflow: hidden; border-right: 0; pointer-events: none; visibility: hidden; }
}

/* Full-screen focus mode (Kitchen Pipeline / dine-in floor wall displays): hide the
sidebar + topbar so only the page content shows. Escape or the floating button exits. */
body.qm-fullscreen .qm-dash-side,
body.qm-fullscreen .qm-dash-chrome { display: none !important; }
body.qm-fullscreen .qm-dash-body { padding: 16px; }

/* Featured-image preview on the full-page blog editor. */
.qm-blogform-thumb { display: block; width: 100%; max-height: 170px; object-fit: cover; border-radius: var(--r-md); border: 1px solid var(--line); }

/* NOTE: the canonical .qm-tabs / .qm-tab / .qm-tab-count component already lives
below in the "Orders tabs + row actions" block (underline-style strip, used by
orders.php). It is the single source of truth: taxonomy, comments and the menu
ingredient picker all fold their old strips (.qm-cat-tabs, .qm-comment-tabs,
.qm-ing-tabs) onto it. Do NOT add a second definition here. */

/* ============================================================================
ADMIN — TAXONOMY
Taxonomy tabs now fold into the canonical .qm-tabs; only the group heads,
thumbnail and summary chips remain taxonomy-specific.
========================================================================== */
.qm-cat-group-head { display: flex; align-items: center; justify-content: space-between; margin: 26px 0 10px; }
.qm-cat-group-head:first-of-type { margin-top: 0; }
.qm-cat-group-head :is(h1,h2,h3,h4,h5,h6) { font-size: var(--fs-600); font-weight: 700; margin: 0; color: var(--ink-900); }
.qm-cat-group-head .qm-count { font-size: 12px; color: var(--ink-400); font-weight: 600; }
.qm-cat-thumb { width: 38px; height: 38px; border-radius: var(--r-sm); object-fit: cover; background: var(--surface-2); border: 1px solid var(--line); }
.qm-cat-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.qm-cat-summary .qm-chip { font-size: 12px; font-weight: 600; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 12px; color: var(--ink-600); }
.qm-cat-summary .qm-chip b { color: var(--qm-primary); }
/* Phones: lay the category-count chips out as an even 2-column grid (label left, count
   right) instead of a ragged wrap — every category stays visible and aligned. */
@media (max-width: 575.98px) {
  .qm-cat-summary { display: grid; grid-template-columns: 1fr 1fr; }
  .qm-cat-summary .qm-chip { display: flex; align-items: center; justify-content: space-between; }
}

/* ============================================================================
ADMIN — COMMENTS
The comment-status tab strip folds into .qm-tabs. The body cell colour is the
shared .qm-cell-body utility below.
========================================================================== */
.qm-cell-body { font-size: var(--fs-400); color: var(--ink-700); white-space: pre-line; }
/* pre-line above turns a template's own source line breaks into rendered ones, which
   is what makes multi-line cells work. It also sits later in this file than the
   single-class .qm-nowrap, so it silently won and a cell marked nowrap still wrapped.
   Two classes beat one: opting a body cell out of wrapping now actually takes. */
.qm-cell-body.qm-nowrap { white-space: nowrap; }

/* Subscriptions table column budget. Left to auto layout the restaurant identity cell
   claimed 29% while Amount got 8% - a 3.6x spread that reads as unbalanced. These are
   percentage hints, not a fixed layout, so a long restaurant name can still borrow
   space rather than being clipped. Widths sum to 100. */
.qm-table-subs th:nth-child(1) { width: 22%; }  /* Restaurant (avatar + name + email) */
.qm-table-subs th:nth-child(2) { width: 11%; }  /* Plan      */
.qm-table-subs th:nth-child(3) { width: 11%; }  /* Status    */
.qm-table-subs th:nth-child(4) { width: 18%; }  /* Period - widest text: two full dates */
.qm-table-subs th:nth-child(5) { width: 10%; }  /* Amount    */
.qm-table-subs th:nth-child(6) { width: 14%; }  /* Reference */
.qm-table-subs th:nth-child(7) { width: 14%; }  /* Actions   */
/* Muted 12px secondary line inside a table cell (special-requests, guest email,
owner email, restaurant address, customer phone, period "N mo", admin-note,
en-dash placeholder). Single source of truth for all dashboard sub-lines —
uses the AA-tuned --ink-400 muted token. Pairs with .qm-cell-body above. */
.qm-cell-sub { font-size: 12px; color: var(--ink-400); }
/* Caps the drop-off / address column width so long addresses wrap. */
.qm-cell-addr { max-width: 220px; }

/* ============================================================================
VENDOR MENU — INGREDIENT BUILDER
Tabs fold into .qm-tabs; the remaining builder/pane/row rules are tokenized —
cool greys → warm --ink ramp, on-state → --qm-primary-50/100, radii → --r-*.
========================================================================== */
#itemModal .modal-dialog { max-width: 1040px; }
.qm-ing-builder { border: 1px solid var(--line); border-radius: var(--r-md); padding: 14px 16px; background: var(--surface); }
.qm-ing-head { margin-bottom: 12px; }
.qm-ing-title { font-size: .98rem; font-weight: 800; color: var(--ink-900); margin: 0; display: flex; align-items: center; gap: .5rem; }
.qm-ing-title .fa-carrot { color: var(--qm-primary); }
.qm-ing-titlesub { font-weight: 500; font-size: .8rem; color: var(--ink-400); }
.qm-ing-hint { display: block; font-size: .8rem; color: var(--ink-500); margin-top: 5px; line-height: 1.45; }

/* Add / edit menu-item modal — a roomy two-column form (width set on
   #itemModal above) so the details sit on the left and attributes / allergens
   on the right, no long scroll. */
.qm-modal-section { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500); margin: 0 0 12px; padding: 0; }
.qm-modal-section + .qm-attr-grid { margin-top: 0; }
/* Attribute + allergen toggles laid out in an even two-column grid. */
.qm-attr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; }
.qm-attr-grid .qm-check { margin: 0; }
@media (max-width: 575px) { .qm-attr-grid { grid-template-columns: 1fr; } }
.qm-ing-panes { max-height: 52vh; overflow-y: auto; padding-right: 4px; }
.qm-ing-pane { display: none; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 18px; }
.qm-ing-pane.is-active { display: grid; }
.qm-ing-row { border: 1px solid var(--line-2); border-radius: var(--r-sm); padding: .5rem .6rem; }
.qm-ing-row.is-on { border-color: var(--qm-primary-100); background: var(--qm-primary-50); }
.qm-ing-pick { display: flex; align-items: center; gap: .55rem; cursor: pointer; margin: 0; font-size: .9rem; }
.qm-ing-pick input { accent-color: var(--qm-primary); }
.qm-ing-icon { object-fit: contain; flex: 0 0 auto; border-radius: 6px; }
.qm-ing-name { font-weight: 600; }
.qm-ing-controls { display: none; flex-wrap: wrap; align-items: center; gap: .6rem; padding-top: .5rem; margin-top: .45rem; border-top: 1px dashed var(--qm-primary-100); }
.qm-ing-row.is-on .qm-ing-controls { display: flex; }
.qm-ing-mini { font-size: .78rem; margin: 0; }
.qm-ing-price { display: inline-flex; align-items: center; gap: .3rem; font-size: .78rem; color: var(--ink-500); margin: 0; }
.qm-ing-price input { width: 80px; }
@media (max-width: 640px) { .qm-ing-pane { grid-template-columns: 1fr; } }

/* ============================================================================
VENDOR — CAMPAIGN FORM
Placement picker + total; pairs with the .qm-ad-map-* wireframe already in the
"Advertising" section below.
========================================================================== */
.qm-ad-fieldset { border: 0; padding: 0; margin: 0 0 16px; }
.qm-ad-place-list { display: grid; gap: 10px; }
.qm-ad-place { display: block; position: relative; margin: 0; cursor: pointer; }
.qm-ad-place input { position: absolute; top: 14px; left: 14px; accent-color: var(--qm-primary); }
.qm-ad-place-body { display: block; padding: 12px 14px 12px 40px; border: 1px solid var(--line); border-radius: var(--r-md); transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast); }
.qm-ad-place input:checked + .qm-ad-place-body { border-color: var(--qm-primary); box-shadow: var(--e-2); background: var(--qm-primary-50); }
.qm-ad-place-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.qm-ad-place-price { font-weight: 700; color: var(--qm-primary-600); white-space: nowrap; }
.qm-ad-place-per { font-weight: 600; color: var(--ink-500); font-size: .8em; }
.qm-ad-place-desc { display: block; margin-top: 4px; font-size: .82rem; color: var(--ink-600); }
/* The selected placement's own options, opening beneath its row. Indented to the 40px the row's
   text already starts at, so the panel reads as belonging to the choice above it rather than
   floating under the list. */
.qm-ad-place-panel { padding: 4px 14px 14px 40px; border: 1px solid var(--qm-primary); border-top: 0; border-radius: 0 0 var(--r-md) var(--r-md); background: var(--qm-primary-50); margin-top: -1px; }
.qm-ad-place-panel > * + * { margin-top: 12px; }
.qm-ad-place-row:has(.qm-ad-place-panel:not([hidden])) .qm-ad-place-body { border-radius: var(--r-md) var(--r-md) 0 0; }
.qm-ad-total { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.qm-ad-total strong { font-size: 1.15rem; color: var(--ink-900); }
/* It holds a priced breakdown now — subtotal, the platform's tax when one is charged, and the
   total that is actually taken — rather than a single figure, so the box stacks its lines and
   rules off the last one. */
.qm-ad-total { flex-direction: column; align-items: stretch; gap: 6px; }
.qm-ad-total-line { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: .92rem; color: var(--ink-600); }
.qm-ad-total-grand { border-top: 1px solid var(--line); padding-top: 8px; margin-top: 2px; color: var(--ink-900); font-weight: 600; }
.qm-ad-map-main { flex: 1 1 auto; min-width: 0; }
.qm-ad-map-main .qm-ad-map-body { padding: 0; margin-top: 6px; }
.qm-ad-map-side-tile { width: 30px; height: 30px; flex: 0 0 auto; }
.qm-ad-map-cap { text-align: center; font-size: .74rem; color: var(--ink-500); margin: 6px 0 0; }
.qm-ad-map-screen-sm { margin-top: 16px; }

/* ============================================================================
VENDOR — CAMPAIGNS
========================================================================== */
.qm-ad-payment-note { margin-top: 6px; font-size: .76rem; color: var(--ink-600); max-width: 280px; line-height: 1.35; }

/* Pagination */
.qm-pager { display: flex; gap: 6px; justify-content: center; align-items: center; margin-top: 22px; flex-wrap: wrap; }
.qm-pager-link { min-width: 38px; height: 38px; padding: 0 10px; border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); font-size: 14px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; transition: all var(--t-fast); }
.qm-pager-link:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-pager-link.is-active { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-pager-link.is-disabled { opacity: .4; pointer-events: none; }
.qm-pager-gap { color: var(--ink-400); padding: 0 4px; }

/* Table footer: record count + rows-per-page sharing a row with the pager
   (partials/table-toolbar.php). The pager keeps its own centred margin, so this only
   neutralises it once the three groups sit on one line. */
/* The count and the rows-per-page picker are ONE left-hand group; the pager is pushed to the
   far edge by its own auto margin below. Packing from the start is what makes that hold in
   every case: with `space-between`, a footer that has no pager is only two children, so the
   picker was flung to the right edge and the layout changed shape from table to table. */
.qm-table-foot { display: flex; align-items: center; justify-content: flex-start; gap: 14px; flex-wrap: wrap;
  margin-top: 0; padding-top: 14px; border-top: 1px solid var(--line-2); }
.qm-table-count { margin: 0; font-size: var(--fs-400); color: var(--ink-600); }
.qm-table-perpage { display: flex; align-items: center; gap: 8px; }
.qm-table-perpage-label { margin: 0; font-size: var(--fs-400); color: var(--ink-600); white-space: nowrap; }
/* The footer's size picker holds two or three digits, so it overrides .form-select's full-size
   metrics: a narrower chevron gutter and a 44px min-height meant for a form field, which made a
   two-character control read as an input box. Used only by partials/table-toolbar.php. */
.qm-select-sm { width: auto; min-width: 0; min-height: 0; padding: 4px 28px 4px 10px; font-size: var(--fs-400);
  background-position: right 9px center; background-size: 13px; }
.qm-table-foot-pager { margin-inline-start: auto; }
.qm-table-foot .qm-pager { margin-top: 0; }
@media (max-width: 640px) {
  /* The rows-per-page picker and record count span the full width of one row - picker flush
     left, count flush right, never centered. The pager wraps to its own full-width row beneath,
     aligned to the right (inline-end). */
  .qm-table-foot { justify-content: space-between; row-gap: 12px; }
  .qm-table-foot-pager { margin-inline-start: 0; width: 100%; }
  .qm-table-foot .qm-pager { justify-content: flex-end; }
}
/* Trim any pager to at most five controls on a phone: the previous/next arrows plus the current
   page and its immediate neighbours. Far numbers, the first/last jumps and the ellipses collapse
   away (see .qm-pager-near / .qm-pager-nav in partials/pagination.php) so the row never wraps. */
@media (max-width: 640px) {
  .qm-pager-gap { display: none; }
  .qm-pager-link:not(.qm-pager-near):not(.qm-pager-nav) { display: none; }
}

/* The single way an empty cell is drawn (helper: dash()). */
.qm-cell-empty { color: var(--ink-400); }

/* Row actions: one or two inline, the rest behind "..." (partials/row-actions.php). */
.qm-rowactions { display: inline-flex; align-items: center; gap: 4px; }
.qm-rowmenu { position: relative; display: inline-flex; }
.qm-rowmenu-list { position: absolute; inset-inline-end: 0; top: calc(100% + 6px); z-index: 30; min-width: 184px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-2); display: flex; flex-direction: column; gap: 2px; }
/* Flipped upward by the script when the row sits near the bottom of the viewport. */
.qm-rowmenu.is-up .qm-rowmenu-list { top: auto; bottom: calc(100% + 6px); }
.qm-rowmenu-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 11px; border: 0; border-radius: var(--r-sm); background: none; color: var(--ink-700); font-size: 14px; font-weight: 600; text-align: start; text-decoration: none; cursor: pointer; transition: background var(--t-fast), color var(--t-fast); }
.qm-rowmenu-item i { width: 16px; text-align: center; color: var(--ink-400); transition: color var(--t-fast); }
.qm-rowmenu-item:hover { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-rowmenu-item:hover i { color: var(--qm-primary); }
.qm-rowmenu-item.is-danger { color: var(--danger); }
.qm-rowmenu-item.is-danger i { color: var(--danger); }
.qm-rowmenu-item.is-danger:hover { background: var(--danger-bg); color: var(--danger); }
.qm-rowmenu-item.is-disabled { opacity: .45; pointer-events: none; }
/* display:contents keeps the POST wrapper from breaking the menu's flex column. */
.qm-rowmenu-form { display: contents; }

/* Scatter tooltip: a bare x/y pair cannot say which dish it is, so the point is named. */
.qm-chart-tip { padding: 8px 11px; display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-400); }
.qm-chart-tip strong { color: var(--ink-900); }
.qm-chart-tip span { color: var(--ink-600); }

/* ============================================================================
ACCOUNT NAV
========================================================================== */
.qm-acct-nav { display: flex; flex-direction: column; gap: 2px; }
.qm-acct-nav a, .qm-acct-nav button { text-align: left; background: none; border: 0; padding: 11px 14px; border-radius: var(--r-sm); color: var(--ink-600); font-weight: 600; font-size: 14px; cursor: pointer; transition: all var(--t-fast); }
.qm-acct-nav a:hover, .qm-acct-nav button:hover { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-acct-nav a.active { background: var(--qm-primary-600); color: #fff; }

/* ============================================================================
RESTAURANT DETAIL
========================================================================== */
.qm-rest-hero { background-size: cover; background-position: center; color: #fff; padding: 40px 0 30px; margin-top: 72px; position: relative; }
.qm-rest-hero::before { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(20,22,22,.35), rgba(20,22,22,.78)); }
.qm-rest-hero .container-fluid { position: relative; z-index: 1; }
.qm-rest-hero .breadcrumb { font-size: var(--fs-400); margin-bottom: 20px; }
.qm-rest-hero .breadcrumb a { color: rgba(255,255,255,.82); }
.qm-rest-hero .breadcrumb .active { color: #fff; }
.qm-rest-hero-card { display: flex; align-items: center; gap: 20px; }
.qm-rest-hero-logo { width: 92px; height: 92px; border-radius: var(--r-lg); object-fit: cover; border: 3px solid #fff; background: #fff; flex-shrink: 0; box-shadow: var(--e-3); }
.qm-rest-hero-name { font-size: clamp(26px, 3.4vw, 34px); font-weight: 700; margin: 0; color: #fff; letter-spacing: -.01em; }
.qm-rest-hero-tag { opacity: .92; margin: 4px 0 10px; color: #fff; }
.qm-rest-hero-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 13.5px; }
.qm-rest-hero-meta .qm-rest-rating { background: var(--rating-great); padding: 3px 10px; border-radius: 6px; }
#main-content .qm-rest-hero { margin-top: 0; }
.qm-section.pt-4 { padding-top: 24px; }
.qm-rest-about { color: var(--ink-600); font-size: 14.5px; line-height: 1.8; margin-bottom: 26px; }
/* Jump-list targets clear the fixed header, matching the offset the legal pages
   already use for their in-page anchors. */
.qm-menu-cat { margin-bottom: 34px; scroll-margin-top: calc(68px + var(--sp-6)); }
.qm-menu-cat-title { font-size: 21px; font-weight: 700; color: var(--ink-900); margin-bottom: 18px; padding-bottom: 10px; border-bottom: 2px solid var(--line); }
.qm-food-list { display: flex; flex-direction: column; gap: 16px; }
/* Three columns: photo, details, action. The photo leads the row (and mirrors to
   the trailing edge under dir="rtl" with no extra CSS), and Add is its own column
   rather than a badge floating over the image. */
.qm-food { display: flex; align-items: center; gap: 16px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: 16px; transition: transform var(--t), box-shadow var(--t); }
.qm-food:hover { transform: translateY(-3px); box-shadow: var(--e-2); }
.qm-food-info { flex: 1; min-width: 0; }
.qm-food-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.qm-food-name { font-size: var(--fs-700); font-weight: 600; color: var(--ink-900); margin: 0; }
/* Descriptor chip on menu items — brand-tinted (was low-contrast amber-on-cream). */
.qm-food-tag { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; background: var(--qm-primary-50); color: var(--qm-primary-700); border: 1px solid var(--qm-primary-100); padding: 2px 8px; border-radius: var(--r-pill); }
.qm-food-desc { font-size: var(--fs-400); color: var(--ink-400); margin: 0 0 8px; line-height: 1.6; }
.qm-food-price { font-size: var(--fs-600); font-weight: 700; color: var(--ink-900); }
.qm-food-cal { font-size: 12px; font-weight: 400; color: var(--ink-400); }
.qm-food-options { margin-top: 10px; max-width: 240px; }
.qm-food-media { width: 140px; flex-shrink: 0; }
.qm-food-media img { width: 140px; height: 140px; object-fit: cover; border-radius: var(--r-md); display: block; }
.qm-food-action { flex-shrink: 0; display: flex; align-items: center; }
.qm-food-add { background: #fff; color: var(--qm-primary); border: 1px solid var(--qm-primary); border-radius: var(--r-sm); padding: 8px 18px; font-size: var(--fs-400); font-weight: 700; cursor: pointer; box-shadow: var(--e-1); white-space: nowrap; transition: background var(--t-fast), color var(--t-fast), box-shadow var(--t-fast); }
.qm-food-add:hover, .qm-food-add.added { background: var(--qm-primary-600); color: #fff; box-shadow: var(--e-2); }
.qm-food-unavailable { background: var(--surface-2); color: var(--ink-400); border-radius: var(--r-sm); padding: 8px 14px; font-size: 12px; white-space: nowrap; }

/* Reviews */
.qm-reviews { margin-top: 36px; }
.qm-review { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-4) var(--sp-5); margin-bottom: var(--sp-4); }
.qm-review-head { display: flex; flex-wrap: wrap; gap: 6px; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.qm-review-stars { color: var(--star); font-size: var(--fs-400); letter-spacing: 2px; }
/* Interactive star rating input (radios styled as stars). row-reverse so :checked/:hover
   fills the star and every star to its left; keyboard-accessible via the radios. */
.qm-star-input { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: 5px; }
.qm-star-input input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.qm-star-input label { cursor: pointer; font-size: var(--fs-h2); line-height: 1; color: var(--line); transition: color .12s ease, transform .1s ease; }
.qm-star-input label:hover { transform: scale(1.12); }
.qm-star-input input:checked ~ label { color: var(--star); }
.qm-star-input label:hover, .qm-star-input label:hover ~ label { color: var(--star); }
.qm-star-input:hover input:checked ~ label { color: var(--line); }
.qm-star-input:hover label:hover, .qm-star-input:hover label:hover ~ label { color: var(--star); }
.qm-star-input input:focus-visible + label { outline: 2px solid var(--qm-primary); outline-offset: 2px; border-radius: 4px; }
.qm-review p { margin: 0 0 6px; color: var(--ink-600); font-size: 14px; }
.qm-review-date { font-size: 12px; color: var(--ink-400); }

/* Cart sidebar */
/* Style-2 category jump list. Shares the cart panel's card treatment and sticky
   offset so the two rails either side of the menu read as one system. */
.qm-rd-catnav { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: 16px 14px; position: sticky; top: var(--rd-stick-top, calc(68px + var(--sp-6))); }
.qm-rd-catnav-title { font-size: 11px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase; color: var(--qm-primary-600); margin: 0 0 10px; }
.qm-rd-catnav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-rd-catnav a { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 10px; border-radius: var(--r-sm); font-size: var(--fs-400); font-weight: 600; color: var(--ink-700); transition: background var(--t-fast), color var(--t-fast); }
.qm-rd-catnav a:hover { background: var(--qm-primary-50); color: var(--qm-primary-600); }
.qm-rd-catnav-count { font-size: 11px; font-weight: 700; color: var(--ink-400); background: var(--line-2); border-radius: var(--r-pill); padding: 1px 7px; flex-shrink: 0; }
.qm-rd-catnav a:hover .qm-rd-catnav-count { background: #fff; color: var(--qm-primary-600); }

.qm-cart-side { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-2); padding: var(--sp-5); position: sticky; top: var(--rd-stick-top, calc(68px + var(--sp-6))); z-index: 20; }
/* Brand-marked panel heading, converged on the kiosk's own "Your order" header
   (.qm-kiosk-order-hd h2): brand icon on the light card surface, ink label. The
   heading is not interactive, and the panel already ends in a brand-filled CTA
   (.qm-cart-side-checkout), so filling this bar as well would compete with that
   CTA and make a plain <h2> read as a button. The icon colour comes from the
   token, so a white-labelled brand colour follows automatically. */
.qm-cart-side-title { display: flex; align-items: center; gap: 10px; font-size: 17px; font-weight: 700; margin-bottom: 16px; color: var(--ink-900); }
.qm-cart-side-title i { width: 30px; height: 30px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; border-radius: 9px; color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 14%, #fff); }
.qm-cart-side-empty { text-align: center; color: var(--ink-400); padding: 24px 10px; }
.qm-cart-side-empty i { font-size: 32px; display: block; margin-bottom: 10px; color: var(--ink-300); }
.qm-cart-line { display: flex; gap: 10px; align-items: flex-start; padding: 12px 0; border-bottom: 1px solid var(--line-2); }
.qm-cart-line img { width: 46px; height: 46px; border-radius: var(--r-sm); object-fit: cover; flex-shrink: 0; }
.qm-cart-line-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
/* Name and line total read as one row, so the price sits with what it prices
   instead of floating at the line's mid-height. */
.qm-cart-line-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.qm-cart-line-name { font-size: 13.5px; font-weight: 600; color: var(--ink-900); min-width: 0; }
.qm-cart-line-opt { font-size: 11.5px; color: var(--ink-400); }
.qm-cart-qty { display: inline-flex; align-items: center; gap: 4px; }
/* 32px on desktop too: 24px was below a comfortable click target, and the row
   below the title has the width to spare. Phones already get 44px (see below). */
.qm-cart-qty button { width: 32px; height: 32px; border-radius: var(--r-sm); border: 1px solid var(--line); background: #fff; cursor: pointer; font-weight: 700; font-size: var(--fs-600); color: var(--ink-600); line-height: 1; transition: border-color var(--t-fast), color var(--t-fast); }
.qm-cart-qty button:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
/* Fixed width so the row does not shuffle when the count crosses 9. */
.qm-cart-qty span { min-width: 28px; text-align: center; font-size: 14px; font-weight: 700; color: var(--ink-900); }
.qm-cart-line-price { font-size: 13.5px; font-weight: 700; color: var(--ink-900); white-space: nowrap; }
.qm-cart-side-summary { margin-top: 14px; padding-top: 12px; border-top: 2px solid var(--line); display: flex; justify-content: space-between; font-weight: 700; font-size: var(--fs-600); }
.qm-cart-side-checkout { margin-top: 16px; }

/* ============================================================================
CART PAGE
========================================================================== */
.qm-cart-rest { display: flex; align-items: center; gap: 12px; }
.qm-cart-rest img { width: 46px; height: 46px; border-radius: var(--r-sm); object-fit: cover; }
.qm-cart-rest .qm-brandmark { width: 46px; height: 46px; border-radius: var(--r-sm); font-size: var(--fs-800); }
.qm-cart-table { width: 100%; border-collapse: collapse; }
.qm-cart-table td { padding: 14px 8px; border-bottom: 1px solid var(--line-2); vertical-align: middle; }
.qm-cart-td-img img { width: 62px; height: 62px; border-radius: var(--r-md); object-fit: cover; }
.qm-cart-td-name { font-weight: 600; color: var(--ink-900); }
.qm-cart-td-opt { font-size: 12px; color: var(--ink-400); }
.qm-cart-td-unit { font-size: 12px; color: var(--ink-400); }
.qm-qty-form { display: inline-flex; align-items: center; gap: 10px; }
.qm-qty-form button { width: 28px; height: 28px; border-radius: 7px; border: 1px solid var(--line); background: #fff; cursor: pointer; font-weight: 700; color: var(--ink-600); }
.qm-qty-form button:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-cart-td-total { font-weight: 700; color: var(--ink-900); text-align: right; }
.qm-cart-td-remove button { background: none; border: 0; color: var(--danger); cursor: pointer; font-size: var(--fs-600); }
.qm-cart-summary :is(h1,h2,h3,h4,h5,h6) { font-size: 17px; }
.qm-sum-row { display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 14px; color: var(--ink-700); }
.qm-cart-summary .qm-sum-row:first-of-type { font-weight: 600; }
/* CANONICAL min-order warning — same look on cart AND checkout (replaces the old centered-red rule). */
.qm-min-warn { display: flex; align-items: flex-start; gap: 7px; margin-top: var(--sp-3); padding: 9px var(--sp-3); font-size: var(--fs-400); font-weight: 600; color: var(--warn); background: var(--warn-bg); border: 1px solid color-mix(in srgb, var(--warn) 28%, transparent); border-radius: var(--r-md); text-align: left; }
.qm-min-warn i { margin-top: 2px; flex: 0 0 auto; }

/* ============================================================================
CHECKOUT
========================================================================== */
.qm-checkout-section { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-5); margin-bottom: var(--sp-5); }
.qm-checkout-section :is(h1,h2,h3,h4,h5,h6) { font-size: var(--fs-700); font-weight: 700; margin-bottom: 16px; }
.qm-pay-option { display: flex; gap: 12px; align-items: flex-start; border: 1px solid var(--line); border-radius: var(--r-md); padding: 14px; margin-bottom: 12px; cursor: pointer; transition: all var(--t-fast); }
.qm-pay-option:has(input:checked) { border-color: var(--qm-primary); background: var(--qm-primary-50); }
.qm-pay-option input { margin-top: 3px; accent-color: var(--qm-primary); }
/* THE WHOLE ROW IS THE CONTROL. The box carried cursor:pointer, which promises a click target the
   size of the row — but only the label's own text was one, so a press anywhere else did nothing and
   the radio dot became the only reliable way in. Letting the label grow to fill the row makes the
   promise true, and costs nothing: it is already the element bound to the input. */
.qm-pay-option > label { flex: 1 1 auto; cursor: pointer; min-width: 0; }
/* And the gateway widget belongs UNDER the row it belongs to, not beside it. The row is a flex
   line, so without this a mounted card field would sit next to the label and squeeze it. */
.qm-pay-option { flex-wrap: wrap; }
.qm-pay-option .qm-vpay-widget { flex: 1 0 100%; margin-top: 12px; }
.qm-pay-option .qm-pay-label { font-weight: 600; color: var(--ink-900); }
.qm-pay-option .qm-pay-desc { font-size: 12.5px; color: var(--ink-400); }
/* Inline card / PayPal widgets, shown in the payment section for the selected method. */
.qm-co-pay-method { margin-top: 14px; padding: 16px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--qm-primary-50); }
.qm-co-pay-method .form-label { font-weight: 600; font-size: var(--fs-400); margin-bottom: 8px; }
.qm-co-stripe-el { padding: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); }
.qm-co-secure { display: flex; align-items: center; gap: 6px; margin: 10px 0 0; font-size: 12px; color: var(--ink-400); }
.qm-co-secure i { color: var(--ok); }
.qm-co-paypal-btns { max-width: 420px; }
#place-order-btn.is-loading { opacity: .7; pointer-events: none; position: relative; }
.qm-order-line { display: flex; justify-content: space-between; font-size: 13.5px; padding: 6px 0; color: var(--ink-600); }
.qm-order-total { display: flex; justify-content: space-between; font-weight: 700; font-size: var(--fs-700); padding-top: 12px; margin-top: 8px; border-top: 2px solid var(--line); }

/* ============================================================================
ORDER TRACKING
========================================================================== */
.qm-track-map { position: relative; height: 280px; border-radius: var(--r-lg); overflow: hidden; background: #e9eef2; box-shadow: var(--e-1); margin-bottom: 22px; }
.qm-track-map svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.qm-track-marker { position: absolute; transform: translate(-50%, -100%); text-align: center; }
.qm-track-marker i { font-size: 24px; filter: drop-shadow(0 2px 3px rgba(0,0,0,.3)); }
.qm-track-marker.rest i { color: var(--qm-primary); }
.qm-track-marker.home i { color: var(--info); }
.qm-track-driver { position: absolute; width: 34px; height: 34px; border-radius: 50%; background: #fff; display: flex; align-items: center; justify-content: center; box-shadow: var(--e-2); transform: translate(-50%, -50%); }
.qm-track-driver i { color: var(--qm-primary); font-size: var(--fs-700); }
.qm-timeline { list-style: none; padding: 0; margin: 0; }
.qm-timeline li { position: relative; padding: 0 0 22px 30px; }
.qm-timeline li::before { content: ""; position: absolute; left: 7px; top: 4px; bottom: -4px; width: 2px; background: var(--line); }
.qm-timeline li:last-child::before { display: none; }
.qm-timeline li::after { content: ""; position: absolute; left: 0; top: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 3px solid var(--ink-300); }
.qm-timeline li.done::after { background: var(--qm-primary); border-color: var(--qm-primary); }
.qm-timeline li.current::after { border-color: var(--qm-primary); box-shadow: 0 0 0 4px color-mix(in srgb, var(--qm-primary) 18%, transparent); }
.qm-timeline .qm-tl-status { font-weight: 600; color: var(--ink-900); font-size: 14px; }
.qm-timeline .qm-tl-note { font-size: var(--fs-400); color: var(--ink-500); }
.qm-timeline .qm-tl-time { font-size: 12px; color: var(--ink-400); }

/* ============================================================================
VENDOR MENU
========================================================================== */
.qm-cat-list { list-style: none; padding: 0; margin: 0; }
.qm-cat-list li { display: flex; justify-content: space-between; align-items: center; padding: 11px 0; border-bottom: 1px solid var(--line-2); font-weight: 600; color: var(--ink-900); font-size: 14px; }
.qm-cat-list li:last-child { border-bottom: 0; }
.qm-cat-actions { display: inline-flex; gap: 4px; align-items: center; }
.qm-cat-actions form { display: inline; }
/* Row-action control. inline-flex + a fixed 32px box so it lines up with .qm-btn-xs
when a table's actions cell mixes icon buttons and labelled buttons — as a <button> and
an <a> they otherwise sit on different baselines at different heights. The coarse-pointer
rule below raises this to the 44px touch floor and still wins (it comes after). */
/* A soft surface chip, mirroring the .fc-refresh-button treatment so every
   icon-only control in the product reads the same. Square by design: equal
   width and height keep a run of row actions on an even rhythm. */
.qm-icon-btn { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; width: 32px; height: 32px; min-width: 32px; min-height: 32px; padding: 0; background: var(--surface); border: 1px solid var(--field-border); color: var(--ink-500); cursor: pointer; border-radius: var(--r-md); font-size: var(--fs-400); line-height: 1; transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); }
.qm-icon-btn:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-600); transform: translateY(-1px); box-shadow: 0 2px 6px color-mix(in srgb, var(--qm-primary) 14%, transparent); }
.qm-icon-btn:active { transform: translateY(0); box-shadow: none; }
.qm-icon-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-icon-btn.danger:hover { color: var(--danger); background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 30%, transparent); box-shadow: 0 2px 6px color-mix(in srgb, var(--danger) 16%, transparent); }
/* Static state badges borrow the shape but must not offer a click affordance. */
.qm-icon-btn.qm-cur-default, .qm-icon-btn.qm-cur-default:hover { background: var(--surface-2); border-color: var(--line-2); color: var(--ink-400); transform: none; box-shadow: none; }
@media (prefers-reduced-motion: reduce) { .qm-icon-btn, .qm-icon-btn:hover, .qm-icon-btn:active { transform: none; } }
/* Touch devices: raise the universal row action control to a 44px target
   (WCAG 2.5.5 / Apple HIG) without inflating desktop table density. */
@media (pointer: coarse) {
  .qm-icon-btn { width: 44px; height: 44px; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
}
.qm-check { font-size: 13.5px; font-weight: 500; color: var(--ink-600); display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.qm-check input { accent-color: var(--qm-primary); }
/* .qm-check does double duty: a compact inline option (a dish attribute, a list filter) and
   a standalone on/off switch for one setting. Only the second is a settings row, so the
   right-hand alignment is scoped to the settings panels rather than applied to the class —
   stretching the inline ones would strand each tick far from the word it belongs to. */
.qm-settings-panel .qm-check { display: flex; justify-content: space-between; align-items: center; width: 100%; gap: 12px; }
.qm-settings-panel .qm-check > input { order: 2; }
/* A tip inside a settings-row check hugs the label text. The row is space-between over
   [text, tip, switch], which parked the icon in the row's dead centre; the auto margin
   hands all free space to the gap AFTER the icon instead. */
.qm-settings-panel .qm-check .qm-tip { margin-inline-end: auto; }

/* Vendor profile / forms */
.qm-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 600px) { .qm-form-grid { grid-template-columns: 1fr; } }
/* minmax(0,…) not 1fr: a bare 1fr floors at the column's min-content, so one card holding a
   longer sentence stole width from its siblings and the three read as uneven. */
.qm-form-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 900px) { .qm-form-grid-3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .qm-form-grid-3 { grid-template-columns: 1fr; } }

/* Order status update */
.qm-status-form { display: inline-flex; gap: 8px; align-items: center; }
/* Drop the pill's heavy shadow so the controls read as one group. */
.qm-status-form .qm-btn { box-shadow: none; }
/* Only when the group pairs a button with a .form-select-sm does the button
   take the select's 36px height. On its own, each button keeps the size its
   own class declares, so row actions stay the same size as everywhere else. */
.qm-status-form:has(.form-select-sm) .qm-btn { min-height: 36px; padding: 0 16px; }
/* The same decision, extended to the rest of the row. On the driver's Deliveries list the cell
   pairs that 36px group with plain 32px .qm-icon-btn actions, and vertical-align: middle then
   centres two different heights — the icons sat 2px low beside the status control. Scoped with
   :has() to cells that actually carry a small select, so every other row of actions keeps the
   32px its own class declares. */
.qm-actions-cell:has(.form-select-sm) .qm-icon-btn { width: 36px; height: 36px; min-width: 36px; min-height: 36px; }
/* Selects here use the unified .form-select / .form-select-sm component (above). */

/* ============================================================================
ARTICLE (blog/page)
========================================================================== */
.qm-article { padding: 0; overflow: hidden; }
.qm-article-hero { width: 100%; height: clamp(200px, 38vw, 340px); object-fit: cover; display: block; }
.qm-article-meta { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); align-items: center; padding: 20px 28px 0; font-size: 12.5px; color: var(--ink-400); }
.qm-article-meta span { display: inline-flex; align-items: center; gap: 5px; }
.qm-article-meta i { color: var(--qm-primary); }
.qm-article-body { padding: var(--sp-4) var(--sp-6) var(--sp-7); color: var(--ink-700); line-height: 1.85; font-size: var(--fs-600); }
.qm-article-body h2, .qm-article-body h3 { color: var(--ink-900); font-weight: 700; margin: var(--sp-6) 0 var(--sp-3); line-height: 1.25; }
.qm-article-body h2 { font-size: 1.45rem; }
.qm-article-body h3 { font-size: 1.18rem; }
.qm-article-body h2:first-child, .qm-article-body h3:first-child, .qm-article-body p:first-child { margin-top: 0; }
.qm-article-body p { margin-bottom: var(--sp-4); }
/* Lead paragraph (larger intro) — add class="qm-lead" to the first <p>. */
.qm-article-body .qm-lead { font-size: 1.12rem; color: var(--ink-800); font-weight: 500; line-height: 1.7; }
.qm-article-body img { max-width: 100%; height: auto; border-radius: var(--r-md); margin: var(--sp-3) 0; }
/* Rich prose: lists */
.qm-article-body ul, .qm-article-body ol { margin: 0 0 var(--sp-4); padding-left: var(--sp-6); }
.qm-article-body li { margin-bottom: var(--sp-2); }
.qm-article-body li::marker { color: var(--qm-primary); }
.qm-article-body ul ul, .qm-article-body ol ol, .qm-article-body ul ol, .qm-article-body ol ul { margin: var(--sp-2) 0 0; }
/* Rich prose: links (brand + accessible focus) */
.qm-article-body a { color: var(--qm-primary); text-decoration: underline; text-underline-offset: 2px; transition: color var(--t-fast); }
.qm-article-body a:hover { color: var(--qm-primary-700); }
.qm-article-body a:focus-visible { outline: 3px solid color-mix(in srgb, var(--qm-primary) 45%, transparent); outline-offset: 2px; border-radius: 3px; }
.qm-article-body strong, .qm-article-body b { color: var(--ink-900); font-weight: 700; }
/* Rich prose: blockquote (brand left accent) */
.qm-article-body blockquote { margin: var(--sp-5) 0; padding: var(--sp-3) var(--sp-5); border-left: 4px solid var(--qm-primary); background: var(--qm-primary-50); border-radius: 0 var(--r-md) var(--r-md) 0; color: var(--ink-800); font-size: 1.05rem; line-height: 1.7; font-style: normal; }
.qm-article-body blockquote p { margin: 0; font-size: inherit; font-style: normal; }
.qm-article-body blockquote p + p { margin-top: var(--sp-3); }
/* Rich prose: tables */
.qm-article-body table { width: 100%; border-collapse: collapse; margin: 0 0 var(--sp-4); font-size: 14.5px; }
.qm-article-body th, .qm-article-body td { padding: var(--sp-3); border: 1px solid var(--line); text-align: left; vertical-align: top; }
.qm-article-body th { background: var(--surface-2); color: var(--ink-900); font-weight: 700; }
.qm-article-body hr { border: 0; border-top: 1px solid var(--line); margin: var(--sp-6) 0; }
.qm-article-body code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 1px 6px; color: var(--ink-800); }

/* ============================================================================
LEGAL / LONG-FORM LAYOUT (.qm-legal) — sticky TOC + content + "Last updated"
Used by privacy/terms via page.php.
========================================================================== */
.qm-legal { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
@media (min-width: 992px) { .qm-legal { grid-template-columns: 260px minmax(0, 1fr); align-items: start; } }
.qm-legal-toc { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); padding: var(--sp-4); }
@media (min-width: 992px) { .qm-legal-toc { position: sticky; top: calc(68px + var(--sp-6)); } }
.qm-legal-toc-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-400); margin: 0 0 var(--sp-3); }
.qm-legal-toc ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-legal-toc a { display: block; padding: 8px 10px; border-radius: var(--r-sm); color: var(--ink-600); font-size: 13.5px; font-weight: 600; text-decoration: none; transition: background var(--t-fast), color var(--t-fast); }
.qm-legal-toc a:hover, .qm-legal-toc a.is-active { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-legal-body { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); overflow: hidden; }
.qm-legal-meta { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; padding: var(--sp-4) var(--sp-6) 0; font-size: var(--fs-400); color: var(--ink-400); }
.qm-legal-meta i { color: var(--qm-primary); margin-right: 5px; }
.qm-legal-body .qm-article-body :is(h1,h2,h3,h4,h5,h6) { scroll-margin-top: calc(68px + var(--sp-6)); padding-top: var(--sp-2); border-top: 1px solid var(--line-2); }
.qm-legal-body .qm-article-body > :is(h1,h2,h3,h4,h5,h6):first-of-type { border-top: 0; padding-top: 0; }

/* ============================================================================
DEAL CARDS
========================================================================== */
.qm-deal-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-1); height: 100%; display: flex; flex-direction: column; transition: transform var(--t), box-shadow var(--t); }
.qm-deal-card:hover { transform: translateY(-5px); box-shadow: var(--e-3); }
.qm-deal-media { aspect-ratio: 2/1; overflow: hidden; position: relative; }
.qm-deal-media img { width: 100%; height: 100%; object-fit: cover; }
.qm-deal-body { padding: 20px; flex: 1 1 auto; display: flex; flex-direction: column; }
/* Sits on the image, top-left, above it — not in the body flow (where a flex-column
   child stretched it to the card's full width). Absolute, so it shrinks to its text. */
.qm-deal-badge { position: absolute; top: 12px; left: 12px; z-index: 2; display: inline-block; background: var(--qm-primary-600); color: #fff; font-weight: 700; font-size: 12px; padding: 4px 12px; border-radius: var(--r-pill); box-shadow: 0 2px 8px rgba(0,0,0,.28); }
.qm-deal-title { font-size: 17px; font-weight: 700; color: var(--ink-900); margin: 0 0 6px; }
.qm-deal-rest { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); font-weight: 600; color: var(--qm-primary-600); margin: 0 0 8px; }
.qm-deal-rest i { font-size: 12px; opacity: .85; }
.qm-deal-desc { font-size: 13.5px; color: var(--ink-400); margin-bottom: 14px; }
.qm-deal-foot { display: flex; justify-content: space-between; align-items: center; margin-top: auto; }
/* Solid coupon chip (was a dated dashed box) — copy-to-clipboard ready, brand-tinted. */
.qm-deal-code { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); color: var(--qm-primary-700); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: var(--r-sm); padding: 6px 12px; font-weight: 700; letter-spacing: .5px; cursor: pointer; transition: background var(--t-fast), border-color var(--t-fast); }
.qm-deal-code:hover { background: var(--qm-primary-100); border-color: var(--qm-primary); }
.qm-deal-code i { font-size: 11px; opacity: .8; }
.qm-deal-min { font-size: 12px; color: var(--ink-400); margin: var(--sp-3) 0 0; }

/* ============================================================================
CONTACT
========================================================================== */
.qm-contact-cards { display: flex; flex-direction: column; gap: 14px; }
.qm-contact-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-1); padding: var(--sp-4); display: flex; gap: var(--sp-4); align-items: flex-start; }
.qm-contact-card i { font-size: var(--fs-800); color: var(--qm-primary); margin-top: 3px; width: 22px; text-align: center; }
.qm-contact-label { font-size: 12px; text-transform: uppercase; letter-spacing: .5px; color: var(--ink-400); font-weight: 600; margin-bottom: 2px; }

/* ============================================================================
MISC
========================================================================== */
/* Canonical empty state — icon + heading + body (+ optional CTA). Use for every
"no data" moment so the same empty looks identical across all dashboards. */
.qm-empty { text-align: center; padding: 64px 20px; color: var(--ink-400); }
/* Direct child only. This is the big illustrative icon at the top of an empty state, and every
   one of them is a direct child — but 17 empty states also carry a button, and an unscoped
   descendant rule blows the icon inside that button up to 44px too. */
.qm-empty > i, .qm-empty > svg { font-size: 44px; color: var(--ink-300); margin-bottom: 14px; }
.qm-empty :is(h1,h2,h3,h4,h5,h6) { font-size: var(--fs-700); font-weight: 700; color: var(--ink-700); margin: 0 0 6px; }
.qm-empty p { font-size: 14px; color: var(--ink-400); margin: 0 auto; max-width: 360px; line-height: 1.55; }
.qm-empty .qm-btn { margin-top: 18px; }
/* Table empty-row variant — drop a single full-width centred cell into a tbody:
<tr><td colspan="N" class="qm-empty-cell"><div class="qm-empty">…</div></td></tr>.
Trims the vertical padding (the cell sits inside an existing card). */
.qm-empty-cell { text-align: center; }
.qm-empty-cell .qm-empty { padding: 40px 20px; }
/* A stat-tile trio INSIDE a card fills the card's row — the tiles grow to share
   it equally — where the page-level KPI slider keeps its fifth-width tiles. */
.qm-d2 .qm-kpi-grid.qm-kpi-grid-fill > * { flex: 1 1 0; }

/* Paired stat cards (trading / payee / gateway): the boxed stats keep their
   style but FILL the card — each box grows to share the row, and the set rides
   the vertical centre of whatever height the row's tallest neighbour sets. */
.qm-d2 .qm-billing-row .qm-card { display: flex; flex-direction: column; }
.qm-d2 .qm-billing-row .qm-order-totals { flex: 1 1 auto; align-content: center; margin: 0; gap: 12px; }
.qm-d2 .qm-billing-row .qm-ot-item { flex: 1 1 auto; padding: 12px 16px; }
/* Paired tables (team | activity): one row rhythm on both sides, and the
   toolbar anchored to the card's foot so a short table never strands it. */
.qm-d2 .qm-billing-row .qm-table tbody td { padding-block: 13px; }
.qm-d2 .qm-billing-row .qm-card > .qm-table-foot { margin-top: auto; }
/* The partner's address: a stated fact, not a muted aside. */
.qm-d2 .qm-loc-addr { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; font-size: 15px; font-weight: 700; color: var(--ink-900, #1c1917); }
.qm-d2 .qm-loc-addr i {
  width: 30px; height: 30px; border-radius: 999px; font-size: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 10%, #fff);
}
/* Inside a d2 data table an empty state is a NOTICE, not a hero: a third of the
   old 64px padding, so a page of young panels reads dense instead of hollow. */
.qm-d2 .qm-table .qm-empty { padding: 26px 14px; }
.qm-d2 .qm-table .qm-empty > i { font-size: 26px; margin-bottom: 8px; }
.qm-d2 .qm-table .qm-empty p { font-size: 13px; margin: 0 auto; }
/* Right-aligned, non-wrapping action column — replaces the duplicated
.qm-staff-actions-cell / .qm-cat-actions-cell inline rules. */
/* Row actions: one right-aligned row, vertically centred, evenly spaced — whether the
cell holds icon buttons, labelled buttons, or buttons wrapped in inline <form>s. The
child rules are what actually align a mixed cell; text-align alone cannot. */
.qm-actions-cell { white-space: nowrap; text-align: right; }
.qm-actions-cell > *,
.qm-actions-cell > form > * { vertical-align: middle; }
.qm-actions-cell > * + *,
.qm-actions-cell > form + * { margin-left: 4px; }
/* The delete action is a POST, so it is a <form> among <a class="qm-icon-btn"> siblings. Bootstrap's
   .d-inline makes that form an inline box the height of a line (19px) next to 32px buttons, and
   vertical-align: middle then centres two different boxes — the form sat 4.8px low on every row of
   every list that has one. Matching the siblings' inline-flex makes all three the same box, which is
   what the alignment rule above was always assuming. !important because .d-inline carries it too. */
.qm-actions-cell > form { display: inline-flex !important; align-items: center; }
/* Drag handle on a table row. Only the grip drags — the rest of the row keeps its links and its
   switches — so the two gestures never compete.

   No size of its own, unlike .qm-pc-drag: that grip stands among .form-select fields and takes
   their 44px, while this one stands among the row's .qm-icon-btn actions and IS one, so it takes
   the 32px they already have (44px on a coarse pointer, from the same rule). Read off the row it
   sits in rather than copied across — the two screens do not agree on a control height.

   Written as a COMPOUND selector because a single class loses the cursor: Bootstrap's reboot sets
   `cursor: pointer` on `[type="button"]:not(:disabled)`, which is two simple selectors to a lone
   class's one. `.qm-icon-btn.qm-drag-grip` matches it and app.css loads after Bootstrap, so it
   wins on order — no !important, which would only hand the same fight to whoever styles this
   next. (`.qm-pc-drag`, the grip this one is copied from, loses that fight and still shows a
   pointer.) */
/* The grip column must be as narrow as the grip. `width: 1%` did not do it: these tables are
   table-layout: auto, where surplus width is handed out in PROPORTION to each column's declared
   width — so 1% of a 1492px table still claimed ~105px for a 32px button and left a 73px hole
   before the first real column. Declaring 1px makes that share negligible, and the cell falls back
   to its own minimum: grip + the table's normal cell padding. The gap to the next column is then
   the same 24px every other column boundary has. */
.qm-drag-col, .qm-drag-cell { width: 1px; white-space: nowrap; }
.qm-icon-btn.qm-drag-grip { cursor: grab; color: var(--ink-400); }
.qm-icon-btn.qm-drag-grip:active { cursor: grabbing; }
/* No `is-dragging` rule here: `.qm-table tr.is-dragging` already ships for the navigation
   builder further down, and these rows are .qm-table rows. */
[hidden] { display: none !important; }

/* ============================================================================
TEMPLATE-FIDELITY — spacing utilities, ex-collection
(Button styles consolidated into the single BUTTONS block above.)
========================================================================== */
/* Spacing / sizing utilities */
.qm-sticky-top { position: sticky; top: calc(68px + var(--sp-6)); }
.qm-max-w-sm { max-width: 560px; }
.qm-img-thumb { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; }
.qm-img-prev { height: 40px; margin-top: 8px; border-radius: 6px; }
/* Modern identity cell for data tables: rounded thumbnail + stacked text. */
.qm-tbl-id { display: inline-flex; align-items: center; gap: 12px; min-width: 0; }
.qm-tbl-thumb { width: 44px; height: 44px; border-radius: 12px; object-fit: cover; flex-shrink: 0;
  background: linear-gradient(135deg, var(--surface-2) 0%, #fff 100%);
  box-shadow: 0 0 0 1px var(--line-2), 0 4px 10px rgba(78,8,29,.10); }
.qm-tbl-id-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.qm-tbl-id-text strong { color: var(--ink-900); line-height: 1.2; }
.qm-tbl-id-text small { color: var(--ink-500); font-size: 12px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Circular variant for people (users, drivers, staff, reservation guests, authors). */
.qm-tbl-thumb.is-round { border-radius: 50%; }
/* Icon tile — same surface/ring as the photo thumb, but holds a contained 3D icon
or a FontAwesome glyph (taxonomy rows whose icon is a graphic, not a photo). */
.qm-tbl-ico { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; flex-shrink: 0; border-radius: 12px; background: linear-gradient(135deg, var(--surface-2) 0%, #fff 100%); box-shadow: 0 0 0 1px var(--line-2), 0 4px 10px rgba(78,8,29,.10); color: var(--qm-primary); font-size: 19px; overflow: hidden; }
.qm-tbl-ico img { width: 32px; height: 32px; object-fit: contain; }

/* Taxonomy icon source (add/edit modals): a mode dropdown (Default icons / Font Awesome /
Image) that shows one input at a time, plus the scrollable icon grid. */
.qm-iconsrc-mode { margin-bottom: 8px; }
.qm-iconpick { display: flex; flex-wrap: wrap; gap: 8px; max-height: 200px; overflow-y: auto; padding: 10px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
/* The merged picker: a search box over a grid of ~130 cells. Smaller cells than the 46px the
   picker used when it held 42 glyphs, so a screenful shows a useful amount of the library. */
.qm-iconpick-menu { width: 340px; max-width: 92vw; padding: 0; }
/* THE search field a picker uses. It was two — the dropdown's, and the taxonomy flag grid's, whose
   input padding lived on a separate .qm-iconflag-q class. Anything built from the wrapper alone
   therefore got the glyph with no room made for it, and the placeholder ran underneath it. The
   wrapper, the glyph and the input's padding are one rule now; only the PLACEMENT varies, below.
   Offsets are measured from the input's own edge, so they no longer depend on the wrapper's
   padding — which is what made the two versions disagree (left: 20px vs 12px) for the same look. */
.qm-iconpick-search { position: relative; }
.qm-iconpick-search > i { position: absolute; inset-inline-start: 12px; top: 50%; transform: translateY(-50%); font-size: 12px; color: var(--ink-400); pointer-events: none; }
.qm-iconpick-search .form-control { padding-inline-start: 32px; height: 34px; font-size: .9rem; }
/* In the dropdown it lines up on the same edges as the grid below it; in a taxonomy source panel
   it simply sits above its grid. Margin, not padding, so the wrapper IS the input's box. */
.qm-iconpick-menu > .qm-iconpick-search { margin: 10px 10px 6px; }
.qm-iconsrc-panel > .qm-iconpick-search { margin-bottom: 8px; }
.dropdown-menu.qm-iconpick-menu > .qm-iconpick { width: auto; max-height: 300px; overflow-y: auto; margin: 0 10px 10px; padding: 10px; }
.qm-iconpick-opt { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; padding: 0; background: var(--surface); border: 1.5px solid var(--line); border-radius: var(--r-sm); cursor: pointer; color: var(--ink-700); transition: all var(--t-fast); }
.qm-iconpick-opt:hover { border-color: var(--qm-primary-100); transform: translateY(-1px); box-shadow: var(--e-1); }
.qm-iconpick-opt.is-active { border-color: var(--qm-primary); background: var(--qm-primary-50); color: var(--qm-primary); box-shadow: var(--e-primary); }
.qm-iconpick-opt img { width: 30px; height: 30px; object-fit: contain; }
.qm-iconpick-fa .qm-iconpick-opt { font-size: 19px; }
/* Same FA glyph grid used inside a dropdown popover (e.g. the nav column-icon field): give it a
   compact multi-column width + height so the glyphs wrap to a tidy grid instead of one wide row. */
.dropdown-menu > .qm-iconpick { width: 300px; max-width: 88vw; max-height: 264px; }
.qm-iconpick::-webkit-scrollbar { width: 8px; }
.qm-iconpick::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; }
/* A picked image/3D icon rendered inline where a cuisine/tag glyph usually sits. */
.qm-cat-ico-img { width: 24px; height: 24px; object-fit: contain; vertical-align: middle; }
/* The "Flag" mode panel note in the cuisine icon picker. */
.qm-iconsrc-note { display: flex; align-items: flex-start; gap: 8px; padding: 12px 14px; margin: 0; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); font-size: var(--fs-400); color: var(--ink-600); line-height: 1.45; }
.qm-iconsrc-note i { color: var(--qm-primary); margin-top: 2px; }
.qm-img-prev-lg { height: 44px; margin-top: 8px; border-radius: 8px; }
/* Image field with an X "remove" button on the current preview (logo, cover, avatar). */
.qm-img-preview { position: relative; display: inline-block; }
.qm-img-remove-btn { position: absolute; top: -7px; right: -7px; width: 22px; height: 22px; padding: 0; border: 2px solid var(--surface); border-radius: 50%; background: var(--qm-primary); color: #fff; font-size: 10px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--e-2); transition: transform var(--t-fast), background var(--t-fast); }
.qm-img-remove-btn:hover { background: var(--qm-primary-700); transform: scale(1.08); }
.qm-img-remove-btn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
/* Admin → Footer: gallery image slots + section toggle spacing. */
/* Footer gallery manager: a sorting surface, not a photo wall. Head = title + count + tip + Add;
   a live marquee preview of the strip; then compact tiles that drag (or arrow-key) into order,
   with remove revealed on hover (always shown where there is no hover). */
.qm-foot-gal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.qm-foot-gal-head h2 { display: flex; align-items: center; gap: 10px; margin: 0; }
.qm-foot-gal-head .qm-pill { font-weight: 600; }
.qm-foot-strip { position: relative; overflow: hidden; border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2, #faf8f9); padding: 12px 0; margin-bottom: 14px; }
.qm-foot-strip[hidden] { display: none; }
.qm-foot-strip-label { position: absolute; top: 6px; left: 10px; z-index: 1; font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-500); background: var(--surface); padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line); }
.qm-foot-strip-track { display: flex; gap: 10px; width: max-content; padding: 12px 0 0; animation: qm-foot-strip-run 28s linear infinite; }
.qm-foot-strip:hover .qm-foot-strip-track { animation-play-state: paused; }
.qm-foot-strip-track img { flex: 0 0 auto; width: 112px; aspect-ratio: 5/4; object-fit: cover; border-radius: 8px; box-shadow: 0 3px 10px rgba(31,11,18,.12); }
@keyframes qm-foot-strip-run { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .qm-foot-strip-track { animation: none; } }
.qm-foot-gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 10px; }
.qm-foot-slot { position: relative; border-radius: 9px; cursor: grab; outline: none; }
.qm-foot-slot:active { cursor: grabbing; }
.qm-foot-slot:focus-visible .qm-img-preview { box-shadow: 0 0 0 3px var(--qm-primary-100, rgba(255,0,61,.18)); border-color: var(--qm-primary); }
.qm-foot-slot.is-dragging { opacity: .45; }
.qm-foot-slot .qm-img-preview { display: block; width: 100%; aspect-ratio: 5/4; border-radius: 9px; border: 1px solid var(--line); transition: transform .15s ease, box-shadow .15s ease; }
.qm-foot-slot:hover .qm-img-preview { transform: translateY(-1px); box-shadow: var(--e-2); }
.qm-foot-slot .qm-img-preview img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 9px; pointer-events: none; }
.qm-foot-slot .qm-img-remove-btn { opacity: 0; transition: opacity .15s ease, transform var(--t-fast), background var(--t-fast); }
.qm-foot-slot:hover .qm-img-remove-btn, .qm-foot-slot:focus-within .qm-img-remove-btn { opacity: 1; }
@media (hover: none) { .qm-foot-slot .qm-img-remove-btn { opacity: 1; } }
.qm-foot-gal-empty { margin: 4px 0 0; font-size: 13px; color: var(--ink-500); }
.qm-foot-gal-empty[hidden] { display: none; }
.qm-foot-gal-add { margin-top: 14px; }
.qm-foot-gal-add[hidden] { display: none; }
/* An image picked but not yet saved: dashed, so the tile reads as pending until Save footer. */
.qm-foot-slot.is-new { cursor: default; }
.qm-foot-slot.is-new .qm-img-preview { border: 2px dashed var(--qm-primary); padding: 2px; background: var(--qm-primary-50, #fff0f3); }
.qm-foot-slot.is-new .qm-img-preview img { border-radius: 6px; }
.qm-foot-sw { margin-bottom: 12px; }
@media (max-width: 640px) { .qm-foot-gallery-grid { grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); } }
.qm-img-removed { margin-top: 8px; font-size: var(--fs-400); color: var(--ink-500); }
.qm-linkish { border: 0; background: none; padding: 0; color: var(--qm-primary); font-weight: 600; cursor: pointer; text-decoration: underline; }
.qm-linkish:hover { color: var(--qm-primary-700); }
/* Cuisine flag picker — searchable grid of country flags (Auto + specific countries). Its search
   box is .qm-iconpick-search, the same one every picker uses; only the grid below it is special. */
.qm-iconpick-flag { max-height: 232px; }
.qm-iconpick-flag .qm-iconpick-opt { width: 46px; height: 34px; padding: 0; overflow: hidden; }
.qm-iconpick-flag .qm-iconpick-opt img { width: 100%; height: 100%; object-fit: cover; border-radius: 3px; }
.qm-iconpick-opt.qm-iconflag-auto { width: auto; min-width: 66px; gap: 6px; padding: 0 12px; font-weight: 700; font-size: 12px; color: var(--ink-700); }
.qm-iconpick-opt.qm-iconflag-auto i { font-size: 14px; color: var(--qm-primary); }
/* Fixed-width small inline input (admin/campaigns inline rejection-note field). */
.qm-w-130 { width: 130px; }
/* Dark swatch behind the dark-background-logo preview (admin/branding). */
.qm-logo-dark-prev { display: inline-block; background: var(--ink-900); border-radius: var(--r-sm); padding: 8px 12px; margin-top: 8px; }
.qm-logo-dark-prev img { max-height: 30px; width: auto; display: block; }
.qm-section-tint { background: var(--tint); }
/* The section's own top/bottom padding stays 0 (pt-0) so the GAP between this
section and its neighbours is unaffected - but a tinted band still needs its
own internal cushion so the colour doesn't read as "no spacing" around its
content. Added on the container (not the section) for exactly that reason. */
.qm-section-tint > .container-fluid { padding-top: var(--sp-7); padding-bottom: var(--sp-5); }
/* Same internal-cushion fix as .qm-section-tint above, for the two other
sections that follow a tinted band (collections/testimonials) and read as
"no spacing" against it without their own top cushion. */
.qm-popular-section > .container-fluid { padding-top: var(--sp-7); }
.qm-testimonials > .container-fluid { padding-top: var(--sp-7); }
.qm-stories-section > .container-fluid { padding-top: var(--sp-7); }
/* Whatever section lands directly after the tinted band needs that same cushion,
whichever one it happens to be - keyed on adjacency rather than on a section's own
class, because which section follows the band differs per homepage layout (popular
dishes / a cuisine spotlight / deals). Scoped to .pt-0 because only a section that
gave up its own top padding is relying on the previous section's bottom padding for
the gap, and here that padding sits INSIDE the tint: the colour runs right up to the
next heading. A following section that kept its own padding already clears the band. */
.qm-section-tint + .qm-section.pt-0 > .container-fluid { padding-top: var(--sp-7); }

/* "Explore our collections" — ad boxes + alternating vertical promo + 3 cards */
.qm-coll-ad { position: relative; display: block; border-radius: var(--r-card); overflow: hidden; box-shadow: var(--e-2); aspect-ratio: 16/7; }
.qm-coll-ad img, .qm-coll-ad video { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-coll-ad video { pointer-events: none; }
.qm-coll-ad:hover img, .qm-coll-ad:hover video { transform: scale(1.05); }
.qm-coll-ad-btn { position: absolute; left: 16px; bottom: 16px; background: rgba(255,255,255,.95); color: var(--ink-900); font-weight: 700; font-size: var(--fs-400); padding: 9px 18px; border-radius: var(--r-pill); box-shadow: var(--e-1); transition: all var(--t-fast); }
.qm-coll-ad:hover .qm-coll-ad-btn { background: var(--qm-primary-600); color: #fff; }
.qm-coll-promo { position: relative; height: 100%; min-height: 360px; border-radius: var(--r-card); overflow: hidden; box-shadow: var(--e-3); display: flex; background: var(--qm-grad); }
/* Popular-dishes vertical ad: spans both card rows; capped when it stacks on mobile. */
.qm-popular-ad { height: 100%; }
@media (max-width: 767.98px) { .qm-popular-ad { min-height: 220px; max-height: 300px; } }
.qm-coll-promo > img, .qm-coll-promo > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.qm-coll-promo > video { pointer-events: none; }
.qm-coll-promo-body { position: relative; z-index: 1; margin-top: auto; width: 100%; padding: 22px; color: #fff; background: linear-gradient(180deg, transparent, rgba(31,11,18,.94)); }
.qm-coll-note { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; background: var(--qm-grad); padding: 4px 11px; border-radius: var(--r-pill); }
.qm-coll-promo-body :is(h1,h2,h3,h4,h5,h6) { color: #fff; font-size: 21px; font-weight: 700; margin: 12px 0 4px; }
.qm-coll-promo-body p { color: rgba(255,255,255,.86); font-size: var(--fs-400); margin-bottom: 16px; }
.qm-coll-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* ============================================================================
TEMPLATE HEADER CHROME (location, order-now, explore, notif/cart/account, brands)
========================================================================== */
.qm-nav { gap: 14px; }
.qm-cuis-toggle { display: inline-flex; align-items: center; gap: 7px; background: var(--qm-primary-50); color: var(--qm-primary); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 8px 12px; font-size: var(--fs-400); font-weight: 700; cursor: pointer; flex-shrink: 0; transition: all var(--t-fast); }
.qm-cuis-toggle:hover { background: var(--qm-primary-600); color: #fff; border-color: var(--qm-primary-600); }
/* Bare hamburger button (no pill/background) shared by the left Tags-sidebar
toggle and the mobile-drawer's Cuisines shortcut beside search - same size
and treatment as .qm-nav-toggle above, so all three read as one consistent
icon-button family. */
.qm-side-toggle, .qm-search-cuis-toggle { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; background: transparent; border: 0; border-radius: 50%; color: var(--qm-primary); cursor: pointer; padding: 0; line-height: 1; flex-shrink: 0; transition: background var(--t-fast); }
.qm-side-toggle:hover, .qm-search-cuis-toggle:hover { background: var(--qm-primary-50); }
/* Custom 3-bar mark instead of a static Font Awesome glyph, so it can morph
into an X on open and the middle bar can run shorter than the other two for
a less generic look. Driven entirely by aria-expanded (kept in sync on click
by assets/js/app.js, both for #qm-nav-toggle directly and for every
[aria-controls] trigger via bindPanel) rather than :hover/:focus, so the
open/closed shape always matches the real state - no lingering "stuck" look
that only clears on an unrelated click elsewhere. */
.qm-burger { position: relative; display: inline-block; width: 18px; height: 13px; flex-shrink: 0; }
.qm-burger span { position: absolute; left: 0; width: 100%; height: 2px; border-radius: 2px; background: currentColor; transition: transform var(--t-fast), opacity var(--t-fast); }
.qm-burger span:nth-child(1) { top: 0; }
.qm-burger span:nth-child(2) { top: 5.5px; width: 65%; left: 17.5%; }
.qm-burger span:nth-child(3) { top: 11px; }
[aria-expanded="true"] > .qm-burger span:nth-child(1) { top: 5.5px; transform: rotate(45deg); }
[aria-expanded="true"] > .qm-burger span:nth-child(2) { opacity: 0; transform: scaleX(0); }
[aria-expanded="true"] > .qm-burger span:nth-child(3) { top: 5.5px; transform: rotate(-45deg); }
/* Only meaningful inside the mobile drawer's search row - Cuisines already
has its own desktop-bar pill (#qm-cuis-toggle in .qm-nav-actions). */
.qm-search-cuis-toggle { display: none; }

/* Location picker */
.qm-loc { flex-shrink: 0; position: relative; padding-right: 16px; }
.qm-loc::after { content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%); width: 1px; height: 24px; background: var(--line); }
@media (max-width: 991.98px) { .qm-loc::after { display: none; } }
.qm-loc-btn { display: inline-flex; align-items: center; gap: 7px; background: transparent; border: 0; cursor: pointer; color: var(--ink-800); font-weight: 600; font-size: 13.5px; max-width: 180px; }
.qm-loc-btn > i.fa-map-marker-alt { color: var(--qm-primary); }
.qm-loc-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-loc-pop { min-width: 320px; border: 0; box-shadow: var(--e-3); border-radius: var(--r-md); }
/* Service-mode toggle relocated to the header's left cluster (before location/search). */
.qm-nav > .qm-nav-mode { flex-shrink: 0; }
@media (max-width: 991.98px) { .qm-nav > .qm-nav-mode { display: none; } }
/* Its mobile-drawer duplicate (.qm-nav-mode--mobile, inside #qm-nav-collapse) takes over
below 992px so delivery/pickup stays reachable once the bar copy above is hidden;
hide that duplicate everywhere else so it never shows twice. */
@media (min-width: 992px) { .qm-nav-mode--mobile { display: none; } }

/* Order Now */
.qm-order-now { display: inline-flex; align-items: center; gap: 7px; background: var(--qm-grad); color: #fff; border-radius: var(--r-pill); padding: 9px 16px; font-weight: 700; font-size: 13.5px; white-space: nowrap; box-shadow: 0 10px 20px color-mix(in srgb, var(--qm-primary) 20%, transparent); transition: transform var(--t-fast); }
.qm-order-now:hover { color: #fff; transform: translateY(-2px); }

/* Explore / Pages mega dropdown */
.qm-explore-btn { background: transparent; border: 0; color: var(--ink-800); font-weight: 600; font-size: 14px; cursor: pointer; white-space: nowrap; }
.qm-explore-btn:hover { color: var(--qm-primary); }
/* (Dead .qm-explore-menu/.qm-explore-grid/.qm-explore-col-title removed — superseded by .qm-mega-menu.) */

/* Full mega menu: every page in the system, grouped (shown from the Pages button) */
/* Full-width mega menu spanning the nav row. Bootstrap's
dynamic positioning is disabled via data-bs-display="static" on the toggle. */
.qm-explore.dropdown { position: static; }
.qm-mega-menu { position: absolute; left: 0; right: 0; top: calc(100% + 12px); width: 100%; max-width: none; margin: 0; border: 1px solid var(--qm-primary-100); box-shadow: var(--e-4); border-radius: var(--r-lg); padding: 28px 26px; z-index: 1080; background: linear-gradient(180deg, #fff 0%, #fff4f8 100%); }
.qm-mega-body { display: grid; grid-template-columns: minmax(240px, 1fr) 2fr; gap: 28px; align-items: stretch; }
.qm-mega-grid { display: flex; gap: 26px; }
.qm-mega-col { flex: 1 1 0; min-width: 0; }
/* Featured promo panel (left of the mega menu) */
.qm-mega-promo { position: relative; display: block; border-radius: var(--r-lg); overflow: hidden; min-height: 300px; box-shadow: var(--e-2); text-decoration: none; }
.qm-mega-promo-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.qm-mega-promo-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: flex-end; gap: 8px; padding: 20px; background: linear-gradient(180deg, color-mix(in srgb, var(--qm-primary-600) 0%, transparent) 26%, color-mix(in srgb, var(--qm-primary-600) 92%, transparent) 100%); color: #fff; }
.qm-mega-promo-pill { align-self: flex-start; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; background: rgba(0,0,0,.28); color: #fff; padding: 4px 11px; border-radius: var(--r-pill); }
.qm-mega-promo-title { font-size: var(--fs-800); font-weight: 800; line-height: 1.2; color: #fff; }
.qm-mega-promo-text { font-size: 12.5px; color: rgba(255,255,255,.9); line-height: 1.45; }
.qm-mega-promo-cta { align-self: flex-start; display: inline-flex; align-items: center; gap: 7px; margin-top: 8px; font-size: 12.5px; font-weight: 800; color: var(--qm-primary-600); background: #fff; padding: 8px 16px; border-radius: var(--r-pill); }
.qm-mega-promo-cta i { transition: transform var(--t-fast); }
.qm-mega-promo:hover .qm-mega-promo-cta i { transform: translateX(3px); }
/* Column heading — red dot + uppercase + accent underline (template parity). */
.qm-mega-title { display: block; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-900); padding: 0 0 12px; margin-bottom: 10px; border-bottom: 1px solid var(--qm-primary-100); }
.qm-mega-title::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--qm-primary); margin-right: 8px; vertical-align: middle; }
/* A chosen column icon stands IN the dot's place, so the heading carries one marker, not two. The
   icon inherits the dot's colour and gap and is boxed to a fixed width, which keeps every column
   title starting on the same x whichever glyphs are picked. */
.qm-mega-title:has(i)::before { display: none; }
.qm-mega-title i { display: inline-block; width: 15px; margin-inline-end: 8px; font-size: var(--fs-400); line-height: 1; text-align: center; color: var(--qm-primary); vertical-align: middle; }
/* Links — red dot bullet, left-aligned (template parity). */
.qm-mega-menu .dropdown-item { position: relative; padding: 7px 8px 7px 18px; border-radius: var(--r-sm); font-size: var(--fs-400); font-weight: 500; color: var(--ink-700); white-space: normal; }
.qm-mega-menu .dropdown-item::before { content: ""; position: absolute; left: 5px; top: 50%; transform: translateY(-50%); width: 5px; height: 5px; border-radius: 50%; background: var(--qm-primary); opacity: .5; transition: opacity var(--t-fast); }
.qm-mega-menu .dropdown-item:hover { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-mega-menu .dropdown-item:hover::before { opacity: 1; }
/* Small badge beside a mega-menu item (e.g. Homepage 1 [Marketplace], Blog [Modern]). */
.qm-mega-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; line-height: 1.6; color: var(--qm-primary); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: var(--r-pill); vertical-align: middle; white-space: nowrap; }
.qm-mega-menu .dropdown-item:hover .qm-mega-tag { background: #fff; }
.qm-mega-foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.qm-mega-foot-cta { font-size: 12.5px; color: var(--ink-500); display: inline-flex; align-items: center; gap: 8px; }
.qm-mega-foot-cta i { color: var(--qm-primary-600); }
.qm-mega-foot-links { display: inline-flex; gap: 16px; flex-wrap: wrap; }
.qm-mega-foot-links a { font-size: 12.5px; font-weight: 600; color: var(--ink-700); }
.qm-mega-foot-links a:hover { color: var(--qm-primary); }
/* Sponsored ad panel (left) — driven by active_ad('mega_menu'); columns span full
   width when no ad is booked via the .qm-mega-body--no-ad modifier. */
.qm-mega-body--no-ad { grid-template-columns: 1fr; }
.qm-mega-ad { display: flex; }
.qm-mega-ad .qm-ad-card { width: 100%; height: 100%; margin: 0; min-height: 300px; }
/* Mega-menu ad: the image fills the whole card with the caption overlaid at the bottom
   (same treatment as the tall promo), so no solid brand-colour panel shows behind the copy. */
.qm-ad-mega { display: flex; }
@media (max-width: 991.98px) {
  .qm-explore.dropdown { position: relative; }
  .qm-mega-menu { position: static; width: 100%; max-width: none; padding: 16px; }
  .qm-mega-body { grid-template-columns: 1fr; }
  .qm-mega-promo { min-height: 150px; }
  .qm-mega-grid { flex-wrap: wrap; }
  .qm-mega-col { flex: 1 1 40%; }
}
@media (max-width: 575.98px) { .qm-mega-col { flex: 1 1 100%; } }

/* Icon pill buttons (notif, cart) */
.qm-icon-pill { position: relative; width: 40px; height: 40px; border-radius: 50%; background: var(--qm-primary-50); border: 1px solid var(--line); color: var(--ink-700); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-700); cursor: pointer; transition: all var(--t-fast); }
.qm-icon-pill:hover { background: var(--qm-primary); color: #fff; border-color: var(--qm-primary); }
/* Language switcher: an auto-width pill (globe + code + caret) that shares the
icon-pill colour/height/hover but isn't squeezed into a 40px circle. */
.qm-lang-switch > .qm-icon-pill { width: auto; border-radius: var(--r-pill); gap: 6px; padding: 0 13px; font-size: var(--fs-400); font-weight: 700; line-height: 1; }
.qm-lang-switch > .qm-icon-pill i.fa-globe { font-size: 14px; }
.qm-lang-switch > .qm-icon-pill i.fa-caret-down { font-size: 11px; opacity: .55; }
.qm-lang-switch > .qm-icon-pill span { font-size: 12.5px; letter-spacing: .03em; }
.qm-lang-switch > .qm-icon-pill:hover i.fa-caret-down { opacity: 1; }
/* Medium tier (laptop-ish widths, just under the full desktop bar, down to
992px): search, Pages, Cuisines and Order Now all stay inline - there's still
room for them, just not for the full-text identity chrome too. Placed here,
after every base rule it touches (.qm-loc-btn, .qm-order-now, .qm-explore-btn,
.qm-icon-pill/.qm-lang-switch, .qm-cuis-toggle above), so each override
actually wins the cascade on source order instead of losing to a same-
specificity base rule that happens to sit later in the file - that exact bug
bit the location badge and Order Now's shape once already. */
@media (max-width: 1599.98px) and (min-width: 992px) {
  /* Language keeps its icon + caret (the only visual cue this is a dropdown
  once the 2-letter code is gone) instead of the account name's bare-icon
  treatment - a flag alone doesn't read as clickable the way an avatar does. */
  .qm-lang-switch > .qm-icon-pill span { display: none; }
  .qm-lang-switch > .qm-icon-pill { width: auto; padding: 0 10px; gap: 4px; }
  .qm-nav-actions > .qm-cuis-toggle span { display: none; }
  /* Match the desktop pill's own gradient badge (see the >=1600px block far
  below) instead of the plain tinted-circle every other icon-pill uses here -
  Cuisines keeps its bold desktop identity at every width, it just loses its
  label first. (.qm-nav-actions > .qm-cuis-toggle is two classes deep, so this
  already outranks the plain .qm-cuis-toggle base rule regardless of source
  order - kept here anyway to stay grouped with the rest of this tier.) */
  .qm-nav-actions > .qm-cuis-toggle { padding: 0; width: 40px; justify-content: center; background: linear-gradient(135deg,var(--qm-primary) 0%,var(--qm-primary-600) 100%); color: #fff; border-color: transparent; }
  /* Location pin's circular gradient badge (see the >=1600px block far below)
  was otherwise scoped to desktop-only, leaving a bare unstyled glyph with no
  background anywhere in this whole tier - duplicated here at the same 24px
  size rather than widening that rule's own range, so the already-verified
  >=1600px and <=991.98px tiers stay untouched. */
  .qm-loc-btn > i.fa-map-marker-alt { width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg,var(--qm-primary),var(--qm-primary-600)); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; }
}
/* Narrower slice of the medium tier: below ~1350px there isn't enough room left
for search AND Pages' AND Order Now's full labels at once. Pages keeps its
normal desktop look (plain text + caret, no icon - it never had one at this
size before and doesn't need one now) all the way down to the drawer
breakpoint; Order Now drops to icon-only (still tappable, matching the row's
other circular badges); search is the one that gives way here, since it's
the only element that can disappear without losing a destination - Pages,
Cuisines, Order Now and the account/cart/notif icons all stay reachable. */
@media (max-width: 1349.98px) and (min-width: 992px) {
  .qm-nav { gap: 8px; }
  .qm-nav-actions { gap: 8px; }
  .qm-nav-collapse { gap: 10px; }
  .qm-order-now--bar span { display: none; }
  /* Explicit square box (not the pill's own 9px/16px padding) so border-
  radius:999px reads as a true circle matching the other 40px icon-badges in
  this row, instead of the oval that padding alone produces on an icon-only
  box. */
  .qm-order-now--bar { width: 40px; height: 40px; padding: 0; gap: 0; justify-content: center; }
  .qm-loc-text,
  .qm-loc-btn > i.fa-caret-down { display: none; }
  .qm-loc-btn { max-width: none; gap: 0; min-width: 40px; justify-content: center; }
  .service-mode-toggle .mode-btn { padding: 8px 9px; }
  .qm-nav-collapse .main-search { display: none; }
}
/* Flag image used in the switcher, the dropdown items and the settings chips. */
.qm-lang-flag { border-radius: 3px; object-fit: cover; box-shadow: 0 0 0 1px rgba(0,0,0,.1); flex-shrink: 0; display: inline-block; vertical-align: middle; }
.qm-lang-item { display: flex; align-items: center; gap: 9px; }
/* Settings: language option chips (default = radios, enabled = checkboxes). */
.qm-lang-opts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 4px; }
.qm-lang-opt { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px 8px 10px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface); cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--ink-700); transition: border-color var(--t-fast), background var(--t-fast); }
.qm-lang-opt:hover { border-color: var(--qm-primary); }
.qm-lang-opt input { accent-color: var(--qm-primary); margin: 0; }
.qm-lang-opt:has(input:checked) { border-color: var(--qm-primary); background: var(--qm-primary-50); color: var(--ink-900); }
.qm-lang-opt.is-locked { opacity: .72; cursor: not-allowed; }
/* Phones: the wrapping pills leave a ragged last row. Stack each language on its own
   full-width row instead — flag + name on the left, the radio/checkbox on the right,
   hairline separators between rows. Same markup, just laid out as a clean list. */
@media (max-width: 767.98px) {
  .qm-lang-opts { flex-direction: column; flex-wrap: nowrap; gap: 0; margin-top: 6px; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
  .qm-lang-opt { width: 100%; border: 0; border-radius: 0; padding: 12px 14px; font-size: 14px; }
  .qm-lang-opt + .qm-lang-opt { border-top: 1px solid var(--line); }
  .qm-lang-opt .qm-lang-flag { order: 1; }
  .qm-lang-opt span { order: 2; }
  .qm-lang-opt input { order: 3; }
  .qm-lang-opt input[type="radio"] { margin-left: auto; width: 18px; height: 18px; }
  /* The enabled-languages checkboxes are the dashboard toggle switch (margin:0 at 0,2,1);
     override with higher specificity so the toggle sits at the right edge of the row. */
  .qm-lang-opts .qm-lang-opt input[type="checkbox"] { margin-left: auto; }
}
.qm-dot { position: absolute; top: -4px; right: -4px; background: var(--qm-grad); color: #fff; font-size: 10px; font-weight: 700; min-width: 18px; height: 18px; line-height: 18px; text-align: center; border-radius: var(--r-pill); padding: 0 4px; box-shadow: var(--e-primary); }
.qm-cart-link .qm-cart-count { position: absolute; top: -4px; right: -4px; }

/* Popovers (notifications + cart) */
.qm-pop { min-width: 300px; max-width: 360px; border: 0; box-shadow: var(--e-3); border-radius: var(--r-md); padding: 0; overflow: hidden; }
.qm-pop-head { padding: 14px 16px; font-weight: 700; color: var(--ink-900); border-bottom: 1px solid var(--line-2); }
.qm-pop-row { display: flex; gap: 11px; align-items: center; padding: 11px 16px; border-bottom: 1px solid var(--line-2); font-size: var(--fs-400); color: var(--ink-700); }
.qm-pop-row:hover { background: var(--surface-2); color: var(--qm-primary); }
.qm-pop-ico { width: 34px; height: 34px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.qm-pop-line { display: flex; justify-content: space-between; gap: 10px; padding: 9px 16px; font-size: var(--fs-400); color: var(--ink-700); }
.qm-pop-sub { display: flex; justify-content: space-between; padding: 11px 16px; font-weight: 700; color: var(--ink-900); border-top: 1px solid var(--line); }
.qm-cart-pop { padding-bottom: 14px; }
.qm-cart-pop .qm-btn { width: calc(100% - 32px); margin: 0 16px; }
.qm-pop-actions { display: flex; gap: 8px; padding: 14px 16px 0; }
.qm-pop-actions .qm-btn { flex: 1; width: auto; margin: 0; }
.qm-pop-empty { padding: 26px 16px; text-align: center; color: var(--ink-400); font-size: var(--fs-400); }
.qm-pop-empty i { font-size: var(--fs-h2); color: var(--ink-300); }

/* Notification bell dropdown (shared by the storefront header + dashboard topbar) */
.qm-notif-pop { width: 340px; max-width: 92vw; padding-bottom: 6px; }
.qm-notif-list { max-height: 380px; overflow-y: auto; }
.qm-notif-pop .qm-pop-row { align-items: flex-start; }
.qm-pop-row-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.qm-pop-row-body strong { font-weight: 600; color: var(--ink-900); line-height: 1.3; }
.qm-pop-row-sub { color: var(--ink-600); font-size: 12px; line-height: 1.35; }
.qm-pop-row-time { color: var(--ink-400); font-size: 11px; }
.qm-pop-row.is-unread { background: var(--qm-primary-50); }
.qm-pop-row.is-unread::after { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--qm-grad); margin-left: auto; flex-shrink: 0; align-self: center; }
/* Bell dropdown footer */
.qm-notif-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 12px; border-top: 1px solid var(--line-2); background: var(--surface-2); position: sticky; bottom: 0; }
.qm-notif-foot .qm-notif-markall { margin: 0; }
.qm-notif-foot-link { display: inline-flex; align-items: center; gap: 6px; background: none; border: 0; padding: 4px 6px; font-size: 12.5px; font-weight: 600; color: var(--qm-primary); cursor: pointer; text-decoration: none; border-radius: var(--r-sm); }
.qm-notif-foot-link:hover { background: var(--qm-primary-50); }
.qm-notif-foot-link[disabled] { color: var(--ink-400); cursor: default; background: none; }

/* Notifications Center page */
.qm-notif-c-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.qm-notif-c-filter { display: inline-flex; gap: 6px; }
.qm-notif-c-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.qm-notif-c-list { list-style: none; margin: var(--sp-4) 0 0; padding: 0; border: 1px solid var(--line-2); border-radius: var(--r-md); overflow: hidden; }
.qm-notif-c-item { display: flex; align-items: stretch; gap: 4px; border-bottom: 1px solid var(--line-2); transition: background var(--t-fast); }
.qm-notif-c-item:last-child { border-bottom: 0; }
.qm-notif-c-item:hover { background: var(--surface-2); }
.qm-notif-c-item.is-unread { background: var(--qm-primary-50); }
.qm-notif-c-main { display: flex; align-items: flex-start; gap: 12px; padding: 14px 16px; flex: 1 1 auto; min-width: 0; color: var(--ink-700); text-decoration: none; }
.qm-notif-c-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.qm-notif-c-body strong { font-weight: 600; color: var(--ink-900); }
.qm-notif-c-sub { font-size: var(--fs-400); color: var(--ink-600); }
.qm-notif-c-time { font-size: 11.5px; color: var(--ink-400); }
.qm-notif-c-del { display: flex; align-items: center; padding-right: 10px; margin: 0; }
.qm-notif-c-item.is-unread .qm-notif-c-main::after { content: ""; align-self: center; margin-left: auto; width: 9px; height: 9px; border-radius: 50%; background: var(--qm-grad); flex-shrink: 0; }

/* Per-user notification settings */
.qm-notif-prefs-lead { margin: var(--sp-4) 0 6px; font-weight: 700; color: var(--ink-900); }
/* Same placement rule as .form-switch above: description left, switch hard right, achieved
   with `order` so the input keeps its markup position ahead of the text it labels. */
.qm-switch-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-md); margin-bottom: 10px; cursor: pointer; transition: border-color var(--t-fast); }
.qm-switch-row:hover { border-color: var(--qm-primary); }
.qm-switch-row > input { order: 2; }
.qm-switch-row > span { order: 1; flex: 1 1 auto; }
/* Size and appearance now come from the shared pill-toggle definition further down,
   which this row is part of. It used to be a native 18px checkbox here, so the very
   same notification card rendered as a toggle inside the dashboard shell (where the
   blanket .qm-dash rule caught it) and as a plain checkbox on the storefront. */
.qm-switch-row input { flex-shrink: 0; }
/* DIRECT CHILD, like the two rules above it. As a descendant selector this also caught any
   nested span — a .qm-pill beside the title became a full-width flex column, and a pill and
   button sharing the trailing slot stacked on top of each other. The stack it exists to
   create is the label + description one, which is a direct child. */
.qm-switch-row > span { display: flex; flex-direction: column; gap: 2px; }
.qm-switch-row strong { font-weight: 600; color: var(--ink-900); }
.qm-switch-row small { color: var(--ink-600); font-size: 12.5px; }
/* An emphasised switch row - for the one setting in a card that decides who ELSE gets the
   feature rather than how it behaves, so it does not read as just another line. Reuses the
   selected-block treatment already in the dashboard (.qm-ob-rest.is-active, .ar-plan-choice):
   primary border over a soft primary wash, mixed into --surface so it follows the theme. */
/* One selected-state treatment, shared by the two row primitives that need it: a switch row
   that decides who else gets a feature, and a setup row used as an exclusive choice (the menu
   import methods). Same declaration, no second component. */
.qm-switch-row.is-highlight,
.qm-setup-row.is-highlight { border-color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 7%, var(--surface)); }
/* "Notification settings" link card on the notifications inbox (points to account). */
.qm-notif-settings-link { display: flex; align-items: center; gap: 14px; text-decoration: none; color: inherit; transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.qm-notif-settings-link:hover { border-color: var(--qm-primary); box-shadow: var(--e-2); }
.qm-notif-settings-link-ico { width: 40px; height: 40px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: var(--fs-600); }
.qm-notif-settings-link-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.qm-notif-settings-link-body strong { color: var(--ink-900); }
.qm-notif-settings-link-body small { color: var(--ink-500); font-size: 12.5px; }
.qm-notif-settings-link-arrow { color: var(--ink-400); flex-shrink: 0; }

/* Account dropdown (avatar + rich grid) */
.qm-account-btn { gap: 8px; }
.qm-avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; border: 2px solid var(--qm-primary-100); }
/* Signed-out counterpart: same .qm-avatar box, so the header keeps its geometry whether or
   not anyone is signed in. Only the fill and glyph centring are added here — every
   dimension still comes from .qm-avatar above. */
.qm-avatar-guest { display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary-100); color: var(--qm-primary); font-size: 14px; flex-shrink: 0; }
.qm-account-name { font-weight: 600; font-size: 14px; color: var(--ink-800); }
/* Dashboard top bar: name stacked over the role label. Kept a single <span>
   child of .qm-account-btn so the mobile collapse rule (.qm-account-btn > span)
   still hides the whole identity block, leaving avatar + caret. */
.qm-account-id { display: inline-flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.qm-account-role { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500); margin-top: 1px; }
/* Account-menu rows — the same accent icon-chip used in chart/table headers
(.qm-head-chip) instead of a bare, unstyled FontAwesome glyph, so this menu
reads as part of the same design system rather than a plain Bootstrap default. */
.qm-account-pop .dropdown-item { display: flex; align-items: center; gap: 11px; padding: 9px 16px; }
.qm-account-pop .dropdown-item .qm-head-chip { font-size: var(--fs-400); }

@media (max-width: 991.98px) {
  /* The action-row is at capacity (notif/cart/account/language/cuisines/menu-
  toggle all fighting for ~375px). Language and Cuisines move into the drawer's
  nav list instead; Account stays in the tight bar (icon-only, see the
  <=1599.98px block further down) alongside notifications, cart and the menu
  toggle. */
  .qm-nav-actions > .qm-cuis-toggle,
  .qm-nav-actions > .qm-lang-switch { display: none; }

  /* Top drawer row: service-mode toggle and Order Now side by side - not two
  separate full-width rows stacked with the CTA buried at the bottom of the
  whole list. Order Now keeps its full desktop look (gradient pill, icon +
  "Order Now" text, same padding/font as the >=992px bar) since it's the
  primary CTA; the toggle - secondary to it here - gives up a little of its
  own padding so both comfortably share the row without overflowing a narrow
  phone width. */
  .qm-drawer-toprow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .qm-drawer-toprow .qm-nav-mode--mobile { flex-shrink: 1; min-width: 0; }
  .qm-drawer-toprow .qm-nav-mode--mobile .mode-btn { padding: 7px 11px; font-size: 12.5px; }
  .qm-drawer-toprow .qm-order-now { flex-shrink: 0; white-space: nowrap; }
  /* Undo the shared >=1100px-max rule that hides .qm-order-now's text and
  shrinks it to icon-only - here it's the row's primary CTA and needs its
  full label, matching the desktop button exactly. */
  .qm-drawer-toprow .qm-order-now span { display: inline; }
  /* Desktop-bar copy (after search/Pages, its original spot) is redundant once
  the drawer-toprow copy is visible - only one Order Now should render at a time. */
  .qm-order-now--bar { display: none; }

  /* Search + Cuisines share one row here: Cuisines is a compact burger-style
  shortcut beside search instead of a labelled row further down the list. */
  .qm-drawer-search-row { display: flex; align-items: center; gap: 8px; width: 100%; }
  .qm-drawer-search-row .main-search { flex: 1; min-width: 0; width: auto; }
  .qm-search-cuis-toggle { display: inline-flex; }

  /* Drawer nav list (Pages, Language): a bordered group of consistent
  icon/label/chevron rows - the standard mobile-drawer link pattern - instead of
  isolated pill buttons. Icon left, label takes the remaining space, chevron
  pinned right; every row after the first gets a top border, giving a single
  divider between adjoining rows regardless of whether a row is a plain
  button/link or a dropdown-toggle wrapper div. */
  .qm-drawer-nav { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
  .qm-drawer-nav > * + * { border-top: 1px solid var(--line); margin-top: -1px; }
  .qm-drawer-link {
    display: flex; align-items: center; gap: 12px; width: 100%;
    background: transparent; border: 0; border-radius: 0;
    padding: 11px 4px; font-size: 14.5px; font-weight: 600; color: var(--ink-800);
    text-align: left; cursor: pointer;
  }
  .qm-drawer-link:hover { color: var(--qm-primary); }
  .qm-drawer-link:hover .qm-drawer-link-arrow { color: var(--qm-primary); }
  /* Same circular tinted-badge treatment as every other icon-in-a-row throughout
  the app (.qm-pop-ico in the notif/cart dropdowns, .qm-head-chip in the account
  menu) - a bare colored glyph here would be a second, inconsistent icon language.
  Pages/Language each get their own accent so the two remaining rows read as
  distinct destinations - an inline mega-menu and an inline language list. */
  .qm-drawer-link-ico { width: 34px; height: 34px; flex-shrink: 0; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-600); }
  /* Pages: bold brand-red badge (same gradient as the location pin/Cuisines
  badges elsewhere in the header) with a white glyph, not a tinted pastel -
  keeps every solid "action" icon in the header the same red family. */
  .qm-drawer-link-ico--red { background: linear-gradient(135deg,var(--qm-primary),var(--qm-primary-600)); color: #fff; }
  .qm-drawer-link-ico--teal { background: color-mix(in srgb, #0f9b8e 16%, #fff); color: #0f9b8e; }
  /* Real current-language flag instead of a generic globe glyph - object-fit
  crops it to fill the same circular footprint as the other row icons. */
  .qm-drawer-link-ico--flag { background: transparent; object-fit: cover; }
  .qm-drawer-link-label { flex: 1; min-width: 0; }
  .qm-drawer-link-arrow { flex-shrink: 0; font-size: 12px; color: var(--ink-400); transition: transform var(--t-fast), color var(--t-fast); }
  /* Same chevron-right glyph as the Cuisines row above (one consistent arrow
  style for all three drawer rows) - rotated 90deg on expand so it reads as
  pointing down at the now-open content, instead of the old caret's 180deg
  down-to-up flip. */
  .qm-explore-btn[aria-expanded="true"] .qm-drawer-link-arrow,
  .qm-lang-switch--mobile > button[aria-expanded="true"] .qm-drawer-link-arrow { transform: rotate(90deg); }

  /* Stable width regardless of expanded state. Without this, .qm-explore keeps
  the desktop-row rule's margin-left:auto, which collapses it to the button's own
  width while closed but forces it to the drawer's full width once the width:100%
  mega-menu becomes visible inside it - visibly shifting the whole row on open. */
  .qm-nav-collapse .qm-explore { margin-left: 0; width: 100%; }
  /* Neutralize the desktop-row "push right" auto margin (now on .qm-drawer-nav,
  see the >=992px block) - the drawer list must stay a normal full-width column
  item here, not shift/shrink toward the row's cross-axis end. */
  .qm-nav-collapse .qm-drawer-nav { margin-left: 0; }

  /* Language expands inline in the drawer, exactly like Pages, instead of
  floating as a detached overlay menu (which could also flip upward near the
  bottom of the drawer's scroll area - a jarring, inconsistent-feeling popup). */
  .qm-lang-switch--mobile .dropdown-menu {
    position: static; width: 100%; max-width: none; margin: 6px 0 0; padding: 4px 0 4px 48px;
    border: 0; box-shadow: none; border-radius: 0;
  }
  .qm-lang-switch--mobile .qm-lang-item { padding: 8px 10px; }
}
/* The Pages/Language row icons only exist for mobile-drawer consistency; hide
them from the medium tier up, where .qm-explore-btn is text + caret only,
same as desktop - Pages/Cuisines/Order Now are all inline-bar-styled from
992px up, not drawer-row-styled (that's mobile-only, see below). */
@media (min-width: 992px) {
  .qm-drawer-link-ico { display: none; }
  .qm-lang-switch--mobile,
  .qm-drawer-toprow { display: none; }
  /* Pages shows a separate, plain fa-caret-down here (text + caret, unlike
  Cuisines/Language which hide entirely) - the exact same icon as the account
  and language buttons beside it, not the mobile row's chevron-right. */
  .qm-explore-btn { display: inline-flex; align-items: center; gap: 6px; }
  .qm-drawer-link-arrow--mobile { display: none; }
}
@media (max-width: 991.98px) {
  .qm-drawer-link-arrow--desktop { display: none; }
}
/* Account collapses to just the avatar/icon (name hidden) from the medium
tier down (<=1599.98px) - search/Pages/Cuisines/Order Now still have their own
room inline, just not enough room for the full "Hi, Name" text too. */
@media (max-width: 1599.98px) {
  .qm-nav-actions .qm-account-btn { gap: 0; }
  .qm-nav-actions .qm-account-name,
  .qm-nav-actions .qm-account-btn > i.fa-caret-down { display: none; }
}
/* True phone/narrow-tablet only: the bar's remaining fixed elements (logo,
location, notif/cart/account icon-pills) also compact, once Cuisines/Language/
Pages have already moved into the drawer (see the <=991.98px blocks above).
At 375px (iPhone SE) this row previously needed ~396px against ~343px of real
room (header's own container padding included), clipping #qm-nav-toggle off
the right edge - every size/gap below is trimmed to close that gap, not just
for looks. */
@media (max-width: 991.98px) {
  .header .container-fluid { padding-left: 10px; padding-right: 10px; }
  .qm-nav { gap: 6px; }
  .qm-nav-logo img { max-width: 88px; }
  /* Moving Cuisines/Language into the drawer freed up real room here (down to just
  notifications, cart, account, menu-toggle) - a more generous gap than the old
  3px reads as deliberate spacing instead of a cramped row of squeezed icons. */
  .qm-nav-actions { gap: 6px; }
  .qm-nav-actions .qm-icon-pill { width: 36px; height: 36px; }
  /* One consistent 36px tap target across every icon button in this row
  (account, location, the two burger toggles) instead of three different
  sizes (36/40/44) fighting each other for space - that mismatch was most of
  the missing room. */
  .qm-nav-actions .qm-account-btn { min-width: 36px; min-height: 36px; padding: 0; justify-content: center; }
  #qm-side-toggle, #qm-nav-toggle { width: 36px; height: 36px; }
  .qm-loc { padding-right: 0; }
  .qm-loc-btn { max-width: none; gap: 0; min-width: 36px; min-height: 36px; padding: 0; justify-content: center; }
  .qm-loc-text,
  .qm-loc-btn > i.fa-caret-down { display: none; }
  /* Same gradient badge as the desktop pin (see the >=1600px block far below),
  sized to match the 36px weight of the notif/cart icon-pills right next to it
  instead of the smaller 24px desktop badge, which looked undersized against
  its neighbours in this row. */
  .qm-loc-btn > i.fa-map-marker-alt { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg,var(--qm-primary),var(--qm-primary-600)); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; }
  /* Left burger's short middle dash sits flush left (points toward the panel
  it opens); the right burger's sits flush right (mirror). Cuisines' burger,
  beside search in the drawer, keeps the default centered dash. */
  #qm-side-toggle .qm-burger span:nth-child(2) { left: 0; }
  #qm-nav-toggle .qm-burger span:nth-child(2) { left: auto; right: 0; }
}
/* ============================================================================
SLIDE-IN PANELS (left categories + right brands) + promo banner
========================================================================== */
.qm-panel-backdrop { position: fixed; inset: 0; background: rgba(31,11,18,.45); opacity: 0; visibility: hidden; transition: opacity var(--t); z-index: 1090; }
.qm-panel-backdrop.show { opacity: 1; visibility: visible; }

/* Left category sidebar */
.qm-side { position: fixed; top: 0; left: 0; width: 290px; max-width: min(290px, 86vw); height: 100vh; background: var(--surface); box-shadow: var(--e-4); z-index: 1095; transform: translateX(-105%); visibility: hidden; transition: transform var(--t-slow), visibility var(--t-slow); display: flex; flex-direction: column; }
.qm-side.open { transform: none; visibility: visible; }
.qm-side-head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.qm-side-title { font-weight: 800; font-size: var(--fs-700); color: var(--ink-900); }
.qm-side-close { background: var(--line-2); border: 0; width: 34px; height: 34px; border-radius: 50%; color: var(--ink-700); cursor: pointer; font-size: var(--fs-600); display: inline-flex; align-items: center; justify-content: center; transition: background var(--t-fast), color var(--t-fast); }
.qm-side-close:hover { background: var(--qm-primary); color: #fff; }
.qm-side-nav { padding: 12px; overflow-y: auto; flex: 1; }
.qm-side-link { display: flex; align-items: center; gap: 12px; padding: 10px 12px; border-radius: var(--r-sm); color: var(--ink-700); font-weight: 600; font-size: 14px; margin-bottom: 2px; transition: background var(--t-fast), color var(--t-fast); }
.qm-side-link:hover, .qm-side-link.active { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-side-ico { width: 34px; height: 34px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; overflow: hidden; }
.qm-side-ico-img img { width: 100%; height: 100%; object-fit: cover; }
/* Polished 3D store-type icon inside the tile (FA glyph fallback unchanged). */
.qm-side-ico .qm-side-ico-3d { width: 24px; height: 24px; object-fit: contain; display: block; filter: drop-shadow(0 1px 2px rgba(74,10,29,.22)); }
.qm-side-tag:hover .qm-side-ico .qm-side-ico-3d { transform: scale(1.06); transition: transform var(--t-fast); }
.qm-side-label { flex: 1; }
.qm-side-count { font-size: 11px; font-weight: 700; color: var(--ink-400); background: var(--line-2); border-radius: var(--r-pill); padding: 2px 8px; }
.qm-side-sep { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--ink-400); padding: 16px 12px 8px; }
.qm-side-rail-toggle { display: none; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: var(--qm-primary-50); color: var(--qm-primary); border: 0; cursor: pointer; transition: background var(--t-fast), color var(--t-fast); }
.qm-side-rail-toggle:hover { background: var(--qm-primary); color: #fff; }

/* ---- Desktop persistent icon-rail: the sidebar stays docked & icon-only,
expanding to the full labelled panel on toggle (overlay, no layout shift). ---- */
@media (min-width: 992px) {
  /* The left gutter and the header offset exist to clear the docked rail, so both
  are conditional on the rail actually being in the document. The kiosk can keep the
  site header while switching the rail off (Vendor Settings > Dine-in); without :has()
  the page would reserve 76px for a rail that was never rendered and simply show a
  gap. Same :has() idiom the expanded state below already uses. */
  body.index-page:has(#qm-side) { padding-left: 76px; }
  .qm-side-toggle { display: none; } /* hamburger redundant — rail is always visible */
  .qm-side-rail-toggle { display: inline-flex; }
  /* Full-height rail (top:0) like the template; the HEADER is offset to the right of it. */
  /* The docked rail is always on screen at this width, so it must undo the
  closed-panel visibility:hidden the base rule sets for the mobile overlay. */
  body.index-page .qm-side { transform: none; visibility: visible; top: 0; height: 100vh; width: 76px; box-shadow: var(--e-1); border-right: 1px solid var(--line); z-index: 1095; }
  /* A modal owns the viewport. The docked rail sits at z 1095 — above Bootstrap's
     1055 — so it floated over fullscreen dialogs and clipped their left edge. */
  body.index-page.modal-open .qm-side { visibility: hidden; }
  /* Header starts after the sidebar and shifts WITH it (collapsed rail 76px → expanded 290px). */
  /* The announcement band is the header's own top edge, so it clears the rail with it —
  listed here rather than in its own rule so the two can never disagree about where the
  chrome starts. */
  body.index-page:has(#qm-side) .header,
  body.index-page:has(#qm-side) .qm-announce { left: 76px; width: auto; right: 0; transition: left var(--t-slow), box-shadow var(--t), background var(--t); }
  body.index-page:has(#qm-side.open) .header,
  body.index-page:has(#qm-side.open) .qm-announce { left: 290px; }
  .qm-side:not(.open) .qm-side-title,
  .qm-side:not(.open) .qm-side-label,
  .qm-side:not(.open) .qm-side-count,
  .qm-side:not(.open) .qm-side-sep,
  .qm-side:not(.open) .qm-side-close { display: none; }
  .qm-side:not(.open) .qm-side-head { justify-content: center; padding: 16px 0; }
  .qm-side:not(.open) .qm-side-nav { padding: 10px 8px; }
  .qm-side:not(.open) .qm-side-link { justify-content: center; padding: 9px; position: relative; }
  .qm-side:not(.open) .qm-side-link[title]:hover::after,
  .qm-side:not(.open) .qm-side-link[title]:focus-visible::after {
    content: attr(title); position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%);
    background: var(--ink-900); color: #fff; font-size: 12px; font-weight: 600; white-space: nowrap;
    padding: 5px 10px; border-radius: var(--r-sm); box-shadow: var(--e-2); z-index: 6; pointer-events: none;
  }
  body.index-page .qm-side.open { width: 290px; box-shadow: var(--e-4); z-index: 1095; }
  .qm-side.open .qm-side-rail-toggle { transform: rotate(180deg); }
}

/* Right cuisines directory */
.qm-cuis-panel { position: fixed; top: 0; right: 0; width: 340px; max-width: min(340px, 90vw); height: 100vh; background: var(--surface); box-shadow: var(--e-4); z-index: 1095; transform: translateX(105%); visibility: hidden; transition: transform var(--t-slow), visibility var(--t-slow); display: flex; flex-direction: column; }
.qm-cuis-panel.open { transform: none; visibility: visible; }
.qm-cuis-head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.qm-cuis-head :is(h1,h2,h3,h4,h5,h6) { margin: 0; font-weight: 800; font-size: var(--fs-700); color: var(--ink-900); }
.qm-cuis-sub { font-size: 12px; color: var(--ink-400); }
.qm-cuis-close { background: var(--line-2); border: 0; width: 34px; height: 34px; border-radius: 50%; color: var(--ink-700); cursor: pointer; font-size: var(--fs-600); display: inline-flex; align-items: center; justify-content: center; transition: background var(--t-fast), color var(--t-fast); }
.qm-cuis-close:hover { background: var(--qm-primary); color: #fff; }
/* Themed dialogs — lift Bootstrap's plain modals to the warm, rounded, pink-tinted
dashboard look so every popup matches the cards (gradient surface, soft warm shadow,
tinted header, brand-tinted circular close). */
/* Every dialog opens vertically centered (matches the custom POS/customize
   overlays) — one rule instead of adding modal-dialog-centered to each modal.
   Long dialogs keep their own scroll via the max-height on .modal-body. */
.modal .modal-dialog { display: flex; align-items: center; min-height: calc(100% - 3.5rem); }
.modal .modal-dialog > .modal-content { width: 100%; }
.modal .modal-body { max-height: calc(100vh - 14rem); overflow-y: auto; }
/* …except when it carries a .qm-tip: overflow-y computes the other axis to auto too (same
   spec quirk as .table-responsive below), so the hover bubble was sliced at the body's edge
   and its overhang grew the very scrollbars the cap exists to manage. */
.modal .modal-body:has(.qm-tip) { max-height: none; overflow-y: visible; }
.modal-content { background: var(--card-bg); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-4); }
.modal-header { padding: 16px 22px; border-bottom: 1px solid var(--line-2); border-radius: var(--r-lg) var(--r-lg) 0 0; background: linear-gradient(180deg, var(--qm-primary-50), transparent); }
.modal-title { font-weight: 800; color: var(--ink-900); letter-spacing: -.01em; }
.modal-body { padding: 22px; }
.modal-footer { padding: 15px 22px 18px; border-top: 1px solid var(--line-2); gap: 8px; }
.modal .btn-close, .alert .btn-close { width: 30px; height: 30px; margin: 0; margin-inline-start: auto; padding: 0; border-radius: 50%; background-color: var(--surface); background-size: 11px; box-shadow: var(--e-1); opacity: .85; transition: all var(--t-fast); }
.modal .btn-close:hover, .modal .btn-close:focus, .alert .btn-close:hover, .alert .btn-close:focus { background-color: var(--qm-primary-100); opacity: 1; box-shadow: var(--e-2); }
.alert.alert-dismissible { display: flex; align-items: flex-start; gap: 12px; padding-inline-end: 14px; }
.alert.alert-dismissible .btn-close { position: static; flex: 0 0 auto; }
.qm-cuis-search { position: relative; padding: 14px 16px; border-bottom: 1px solid var(--line-2); }
.qm-cuis-search i { position: absolute; left: 28px; top: 50%; transform: translateY(-50%); color: var(--ink-400); font-size: var(--fs-400); }
.qm-cuis-search input { width: 100%; border: 1px solid var(--line); border-radius: var(--r-pill); padding: 9px 14px 9px 34px; font-size: 13.5px; }
.qm-cuis-body { overflow-y: auto; flex: 1; padding: 12px; }
.qm-cuis-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.qm-cuis-card { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; padding: 14px 8px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card-bg); transition: all var(--t-fast); }
.qm-cuis-card:hover { border-color: var(--qm-primary); transform: translateY(-2px); box-shadow: var(--e-2); }
.qm-cuis-card-ico { width: 46px; height: 46px; border-radius: 50%; overflow: hidden; background: var(--line-2); }
.qm-cuis-card-ico img { width: 100%; height: 100%; object-fit: cover; }
.qm-cuis-name { font-size: 12.5px; font-weight: 600; color: var(--ink-800); }

/* ============================================================================
HEADER REFINEMENTS — tighter search, cuisines A–Z rail, nicer left sidebar
========================================================================== */
/* Cap the header search so the bar breathes (desktop row layout only — the mobile
drawer is a column flex, where this same flex-basis would set a 300px HEIGHT instead
of a width, leaving a large empty gap). */
@media (min-width: 1600px) {
  .qm-nav-collapse .main-search { flex: 0 1 300px; max-width: 340px; min-width: 0; }
  .qm-nav-collapse { gap: 14px; justify-content: flex-start; }
  .qm-nav-collapse .service-mode-toggle { margin-left: auto; }
}
#qm-header-search { font-variant-ligatures: none; }

/* Cuisines A–Z panel: grid + sticky letters + alphabet rail */
.qm-cuis-wrap { display: flex; flex: 1; min-height: 0; }
.qm-cuis-panel .qm-cuis-body { flex: 1; }
.qm-cuis-letter { grid-column: 1 / -1; font-size: 12px; font-weight: 800; color: var(--qm-primary); letter-spacing: .5px; padding: 12px 4px 4px; position: sticky; top: 0; background: var(--surface); }
.qm-cuis-count { font-size: 11px; font-weight: 700; color: var(--ink-400); background: var(--line-2); border-radius: var(--r-pill); padding: 1px 7px; }
.qm-cuis-card { position: relative; }
.qm-cuis-panel .qm-cuis-card .qm-cuis-count { position: absolute; top: 8px; right: 8px; }
/* Representative dish icons row (reuses the 23 3D icons) — wraps onto its own line */
.qm-cuis-panel .qm-cuis-card { flex-wrap: wrap; }
.qm-cuis-dishes { display: flex; gap: 5px; align-items: center; flex-basis: 100%; margin-top: 3px; }
.qm-cuis-dish { width: 22px; height: 22px; object-fit: contain; filter: drop-shadow(0 1px 2px rgba(0,0,0,.35)); }
.qm-cuis-dish-fa { font-size: 14px; color: rgba(255,255,255,.78); }
/* Admin: cuisine → assign dishes picker (modal) */
.qm-ctag-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.qm-ctag-opt { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer; font-size: var(--fs-400); }
.qm-ctag-opt:hover { border-color: var(--qm-primary); }
.qm-ctag-opt img { width: 24px; height: 24px; object-fit: contain; flex: 0 0 auto; }
.qm-ctag-opt input { accent-color: var(--qm-primary); }
.qm-cat-dishrow { display: inline-flex; align-items: center; gap: 4px; }
@media (max-width: 540px) { .qm-ctag-grid { grid-template-columns: 1fr; } }
.qm-alpha-rail { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 14px 6px; border-left: 1px solid var(--line-2); overflow-y: auto; }
.qm-alpha { font-size: 11px; font-weight: 700; color: var(--qm-primary); width: 18px; height: 16px; display: flex; align-items: center; justify-content: center; border-radius: 4px; }
.qm-alpha:hover { background: var(--qm-primary-600); color: #fff; }
.qm-alpha.is-off { color: var(--ink-300); pointer-events: none; }

/* Nicer left sidebar (brand header + larger pill items) */
.qm-side-head { padding: 16px 18px; background: var(--tint); }
.qm-side-brand { display: flex; align-items: center; gap: 10px; }
.qm-side-brand img { height: 30px; width: auto; }
.qm-side-nav { padding: 14px 12px; }
.qm-side-link { padding: 11px 12px; border-radius: var(--r-md); font-weight: 600; }
.qm-side-link:hover { background: var(--qm-primary-50); transform: translateX(2px); }
.qm-side-ico { width: 38px; height: 38px; box-shadow: var(--e-1); }
.qm-side-sep { padding: 18px 12px 8px; }
.qm-side-sep::after { content: ""; display: block; height: 1px; background: var(--line-2); margin-top: 8px; }

/* ============================================================================
Restaurant operations suite (dashboard-only)
Floor-plan tables | Kitchen Pipeline (KDS) | AI chat | sectioned settings
========================================================================== */

/* --- Shared dashboard section header ------------------------------------- */
.qm-ops-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-5); }
/* A HEAD THAT IS THE WHOLE CARD KEEPS NO GAP UNDER IT. The margin above exists to hold the head
   off the content beneath it; where there is no content — a card that is only a title, a caption
   and one action — that margin lands on top of the card's own padding, leaving 41px below and
   21px above. The card read as bottom-heavy and its button as sitting high, when the button was in
   fact centred all along. */
.qm-card > .qm-ops-head:only-child { margin-bottom: 0; }
/* The copy column defaults to a flex-basis of its own max-content, so a subtitle
   longer than about half the header claims the whole row and wraps the actions onto
   a second line, left-aligned. A small basis lets the sentence wrap inside its column
   instead, keeping the actions on the first line and hard right. */
/* Basis is the wrap threshold: the action wraps below the title only once the title
   can no longer hold this much. 320px was wider than a col-lg-4 card, so heads inside
   a narrow column wrapped even with room to spare; 220px still wraps on phones. */
.qm-ops-head > :first-child { flex: 1 1 220px; min-width: 0; }
/* Phones: 220px is wider than a phone card's whole row, so every single-action
   head wrapped its button under the title. Basis 0 lets the title column
   shrink (and wrap its own text) first — the action keeps the title row,
   hard right, on every head. Top-aligned, so a tall subtitle (the kitchen
   page's multi-line hint) can't drag the action down to mid-card: the button
   belongs beside the TITLE line. */
@media (max-width: 575.98px) {
    .qm-ops-head { align-items: flex-start; }
    .qm-ops-head > :first-child { flex-basis: 0; }
}
.qm-ops-head .qm-ops-sub { color: var(--ink-400); font-size: var(--fs-400); margin: 6px 0 0; }
/* Plain table headers (a bare h2) get their title/subtitle gap solely from this margin.
   Section headers add 4px via the .qm-snap-section-title h2's own margin, so pin their
   subtitle back to 2px — both header styles then land on the same ~6px gap. */
.qm-ops-head:has(.qm-snap-section-title) .qm-ops-sub { margin-top: 2px; }
/* On phones the page-level date filter can't sit beside the title, so it wraps under it at its
   own width and reads as cramped. Stack the header instead: the title/description block and the
   filter each take the full width, and the filter's pills stretch to fill it as one flush bar -
   the filter stays fully visible, never hidden. Where the header also carries an action (e.g.
   staff analytics' "Manage staff"), that group stacks too: filter full-width, button beneath. */
@media (max-width: 575.98px) {
  /* Only headers that carry a wide control need to stack on a phone: the reporting-period filter
     (.qm-period) and the detail-page identity (.qm-tbl-id). A primary action button (a full-size
     .qm-btn such as "Add user" / "New restaurant") does NOT stack - it stays inline and
     right-aligned beside the title, the same compact treatment as a small "View all" link
     (.qm-btn-sm) or the reservations "Today" shortcut, so every list header reads the same way. */
  .qm-ops-head:has(.qm-period, .qm-tbl-id) { flex-direction: column; align-items: stretch; }
  .qm-ops-head:has(.qm-period, .qm-tbl-id) > * { width: 100%; min-width: 0; }
  /* In the row layout the copy column carries a 220px flex-basis that reads as a WIDTH. Once the
     header stacks to a column here that same basis becomes a HEIGHT and inflates the title /
     identity block into a ~220px-tall empty box - the gap seen under "Right Now" and the
     detail-page logos. Reset it so the block is only as tall as its content. */
  .qm-ops-head:has(.qm-period, .qm-tbl-id) > :first-child { flex-basis: auto; }
  /* A lone primary action sits beside the title, not full-width. Let the title shrink to its own
     content so the button fits on the same row, and pin the button hard-right (margin-auto) so it
     stays right even when a long title pushes it onto a second line. */
  .qm-ops-head:has(> .qm-btn:not(.qm-btn-sm)):not(:has(.qm-period, .qm-tbl-id)) > :first-child { flex: 0 1 auto; }
  .qm-ops-head > .qm-btn:not(.qm-btn-sm) { margin-inline-start: auto; }
  /* Section headers (a plain <h2> title, not stacked above) keep the title and its control on one
     row. The title must not grow to fill the row - that would push the small "View all" link /
     status pill onto a second line, left-aligned. Size the title to its content so space-between
     pins the control to the right, exactly as on desktop. */
  .qm-ops-head > h2:first-child { flex: 0 1 auto; }
  .qm-ops-head .qm-period { width: 100%; }
  .qm-ops-head .qm-period-btn { flex: 1 1 0; min-width: 0; padding-left: 4px; padding-right: 4px; text-align: center; }
  .qm-ops-head > .d-flex { flex-wrap: wrap; }
  /* Every action in the row goes full-width and stacks - whether it is a bare button or a button
     wrapped in an inline <form> (the status / feature toggles) - so they line up as equal bars
     instead of one filling the row and the next sitting half-width beside it. */
  .qm-ops-head > .d-flex > .qm-period,
  .qm-ops-head > .d-flex > .qm-btn,
  .qm-ops-head > .d-flex > .qm-seg,
  .qm-ops-head > .d-flex > form { flex: 1 1 100%; }
  .qm-ops-head > .d-flex > .qm-btn { justify-content: center; }
  /* A segmented view-switcher (List / Calendar) stretches to a full-width toggle with two equal
     segments, so it lines up with the stacked full-width action buttons in the same toolbar. */
  .qm-ops-head > .d-flex > .qm-seg { display: flex; }
  .qm-ops-head > .d-flex > .qm-seg > .qm-seg-btn { flex: 1 1 0; min-width: 0; justify-content: center; }
  /* The inline <form> keeps Bootstrap's display: inline !important, so its button can't be
     stretched with flex - size it to the full-width form directly. */
  .qm-ops-head > .d-flex > form > .qm-btn { width: 100%; justify-content: center; }
  /* Exactly-two-button action group (e.g. inventory's Stocktake + Add, staff's Activity + Add):
     show the pair side by side as two equal columns on their own full-width row, instead of two
     stacked full-width bars. The two-children test excludes segmented controls and three-plus-action
     toolbars (the reservations bar), which keep stacking via the rule above. */
  .qm-ops-head > .d-flex:has(> .qm-btn ~ .qm-btn):not(:has(> :nth-child(3))) { flex: 1 1 100%; flex-wrap: nowrap; }
  .qm-ops-head > .d-flex:has(> .qm-btn ~ .qm-btn):not(:has(> :nth-child(3))) > .qm-btn { flex: 1 1 0; min-width: 0; }
}

/* --- Orders: reconciliation filter bar + tender totals ------------------- */
.qm-order-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; padding: 14px 16px; margin-bottom: 16px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); }
.qm-of-field { display: flex; flex-direction: column; gap: 4px; }
.qm-of-field.qm-of-grow { flex: 1 1 180px; min-width: 160px; }
.qm-of-label { font-size: 11px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--ink-400); }
/* align-items: flex-end, not center. A .qm-of-field stacks its label above its control, so an
   actions cell centred on the FULL column height sits ~8px below the input it belongs to — visible
   on every filter bar that carries a row action, the navigation link rows included. Ending them
   together puts the button on the input's own baseline. */
.qm-of-actions { display: flex; gap: 8px; align-items: flex-end; margin-left: auto; }
/* A filter bar whose fields have no visible label has nothing to offset against, so its actions
   centre as before. */
.qm-order-filters:not(:has(.qm-of-label)) .qm-of-actions { align-items: center; }
/* Phone-only "Filters" collapse (hidden checkbox + :has(), no JS): reclaim vertical space on
   phones; tablet/desktop (>=768px, incl. iPad) keep the toggle hidden and every field visible.
   The toggle is a clean borderless row INSIDE the bar (no nested box), so it reads as part of the
   bar rather than a floating button. The checkbox is fully out of flow so it never adds a gap. */
.qm-of-open-cb { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; opacity: 0; overflow: hidden; clip: rect(0 0 0 0); pointer-events: none; }
.qm-of-toggle { display: none; }
.qm-of-chevron { font-size: var(--fs-400); color: var(--ink-400); transition: transform .2s ease; }
/* Phones: each field takes a full row, and the Reset button spans the full width to match. */
@media (max-width: 575.98px) {
  .qm-order-filters .qm-of-field, .qm-order-filters .qm-of-field.qm-of-grow { flex: 1 1 100%; min-width: 0; max-width: none; }
  .qm-order-filters .qm-of-actions { margin-left: 0; width: 100%; }
  .qm-order-filters .qm-of-actions .qm-filter-reset { width: 100%; }
}
/* Collapse the filter bar behind a tap-to-open toggle on phones AND tablets (iPad portrait is 768px,
   landscape 1024px). Above 1024px the dashboard has room to show every field inline. */
@media (max-width: 1024px) {
  .qm-of-toggle { flex: 1 1 100%; display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 40px; margin: 0; font-size: 14px; font-weight: 700; color: var(--ink-900); cursor: pointer; user-select: none; }
  .qm-of-toggle > span { display: inline-flex; align-items: center; gap: 8px; }
  .qm-of-toggle > span > .fa-sliders-h { color: var(--qm-primary); }
  .qm-order-filters:has(.qm-of-open-cb:not(:checked)) > .qm-of-field,
  .qm-order-filters:has(.qm-of-open-cb:not(:checked)) > .qm-of-actions { display: none; }
  .qm-of-open-cb:checked ~ .qm-of-toggle .qm-of-chevron { transform: rotate(180deg); }
}
.qm-order-totals { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 16px; }
.qm-ot-item { display: inline-flex; flex-direction: column; gap: 2px; padding: 8px 14px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); min-width: 96px; }
.qm-ot-k { font-size: 11px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; color: var(--ink-400); }
.qm-ot-v { font-size: 17px; font-weight: 700; color: var(--ink-800); }
/* Linked strip items (the dashboard "Right now" snapshot). */
a.qm-ot-item { text-decoration: none; transition: border-color var(--t-fast), transform var(--t-fast), box-shadow var(--t-fast); }
a.qm-ot-item:hover { border-color: var(--qm-primary); transform: translateY(-2px); box-shadow: var(--e-1); }
.qm-ot-k i { color: var(--qm-primary); margin-inline-end: 3px; }
/* On phones the chips wrap to two per row and, sized to their content, leave a ragged band of
   white space on the right. Let them grow to share each row's full width (a lone last chip -
   e.g. "Net" - fills the row) so the strip reads as a tidy grid. */
@media (max-width: 575.98px) { .qm-order-totals .qm-ot-item { flex: 1 1 auto; } }
/* "Right now" strip (vendor + admin dashboards) — the .qm-ot-item chips become
live tiles: an accent icon chip (colour from the shared .qm-kpi--* accents), the
value stacked over its label, an accent edge, and a hover reveal arrow. Same
tinted-gradient + lift-on-hover formula as the .qm-kpi-rich accent cards below
it, so this strip reads as the same design family rather than a flatter,
lower-effort leftover from before that card system existed. */
.qm-rightnow .qm-order-totals { display: grid; grid-template-columns: repeat(auto-fit, minmax(205px, 1fr)); gap: 12px; }
.qm-rightnow .qm-ot-item { flex-direction: row; align-items: center; gap: 12px; padding: 13px 15px; border-radius: var(--r-md); position: relative; overflow: hidden;
  background: radial-gradient(135% 160% at 100% 0, color-mix(in srgb, var(--kc, var(--qm-primary)) 12%, #fff) 0, var(--surface) 60%);
  border-color: color-mix(in srgb, var(--kc, var(--qm-primary)) 20%, #fff);
  transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-rightnow .qm-ot-item::before { content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 3px; background: var(--kc, var(--qm-primary)); }
.qm-rightnow .qm-ot-item:first-child { border-color: color-mix(in srgb, var(--kc, var(--qm-primary)) 20%, #fff); }
.qm-rightnow .qm-ot-ico { flex: none; width: 42px; height: 42px; border-radius: 13px; display: grid; place-items: center; font-size: var(--fs-700); color: var(--kc, var(--qm-primary)); background: color-mix(in srgb, var(--kc, var(--qm-primary)) 14%, #fff); position: relative; z-index: 1; }
.qm-rightnow .qm-ot-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; position: relative; z-index: 1; }
.qm-rightnow .qm-ot-v { font-size: var(--fs-900); font-weight: 800; line-height: 1.15; white-space: nowrap; }
.qm-rightnow .qm-ot-k { letter-spacing: .03em; }
.qm-rightnow .qm-ot-go { margin-inline-start: auto; font-size: 12px; color: var(--ink-300); opacity: 0; transform: translateX(-5px); transition: opacity var(--t-fast), transform var(--t-fast), color var(--t-fast); position: relative; z-index: 1; }
.qm-rightnow .qm-ot-item:hover { transform: translateY(-3px); border-color: color-mix(in srgb, var(--kc, var(--qm-primary)) 42%, var(--line)); box-shadow: 0 14px 26px color-mix(in srgb, var(--kc, var(--qm-primary)) 20%, transparent); }
.qm-rightnow .qm-ot-item:hover .qm-ot-go { opacity: 1; transform: none; color: var(--kc, var(--qm-primary)); }
@media (prefers-reduced-motion: reduce) { .qm-rightnow .qm-ot-item, .qm-rightnow .qm-ot-go { transition: none; } }

/* --- Inventory: linked-dish picker (scrollable checklist in the item modal) - */
.qm-inv-dishlist { max-height: 220px; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--r-sm); padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; background: var(--surface); }
.qm-inv-dishlist .qm-check { margin: 0; }

/* --- Report section sub-heading (drawer Z-report and similar) ------------- */
.qm-report-sub { display: flex; align-items: center; gap: 8px; font-size: var(--fs-400); font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-600); margin: 0 0 10px; }
.qm-report-sub i { color: var(--qm-primary); }

/* --- Floor-plan: zone tabs ----------------------------------------------- */
.qm-zone-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; border-bottom: 1px solid var(--line); padding-bottom: 12px; }
.qm-zone-tab { display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border-radius: var(--r-pill); border: 1px solid var(--line); background: var(--surface); color: var(--ink-600); font-weight: 600; font-size: var(--fs-400); cursor: pointer; transition: all var(--t-fast); text-decoration: none; }
.qm-zone-tab:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-zone-tab.is-active { background: var(--qm-grad); color: #fff; border-color: transparent; box-shadow: var(--e-1); }
.qm-zone-tab .qm-zone-tab-count { background: rgba(0,0,0,.12); border-radius: var(--r-pill); padding: 0 7px; font-size: 11px; }
.qm-zone-tab.is-active .qm-zone-tab-count { background: rgba(255,255,255,.28); }
/* Pencil beside the ACTIVE zone tab — opens the zone modal in edit mode (rename / delete). */
.qm-zone-edit { width: 34px; height: 34px; font-size: 12px; align-self: center; margin-left: -2px; }

/* --- Floor-plan: legend + toolbar ---------------------------------------- */
.qm-floor-legend { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; margin-bottom: 14px; font-size: var(--fs-400); color: var(--ink-600); }
.qm-floor-legend .qm-leg { display: inline-flex; align-items: center; gap: 7px; }
.qm-floor-legend .qm-leg-dot { width: 14px; height: 14px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; color: #fff; }
/* Non-colour cue: each legend swatch carries the same status glyph as its chip,
so the legend reads without relying on colour (WCAG 1.4.1). */
.qm-floor-legend .qm-leg-dot::before { font-family: "Font Awesome 5 Free"; font-weight: 900; font-size: 8px; line-height: 1; }
.qm-leg-dot.is-vacant { background: var(--ok); }
.qm-leg-dot.is-vacant::before { content: "\f111"; }
.qm-leg-dot.is-occupied { background: var(--danger); }
.qm-leg-dot.is-occupied::before { content: "\f6c0"; }
.qm-leg-dot.is-cleaning { background: var(--warn); }
.qm-leg-dot.is-cleaning::before { content: "\f51a"; }
.qm-leg-dot.is-reserved { background: var(--info); }
.qm-leg-dot.is-reserved::before { content: "\f274"; }
.qm-floor-hint { color: var(--ink-400); font-size: 12px; }
.qm-floor-tools { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; }
.qm-floor-ctl { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); font-weight: 600; color: var(--ink-700); background: var(--surface); border: 1px solid var(--field-border); border-radius: var(--r-pill); padding: 5px 12px; cursor: pointer; transition: all var(--t-fast); }
.qm-floor-ctl:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-floor-ctl input { accent-color: var(--qm-primary); }
.qm-floor-ctl i { font-size: 12px; }

/* --- Floor-plan: the grid canvas ----------------------------------------- */
/* overflow: auto (not hidden) - tables-floor.js already clamps drag/arrow-key
   moves against grid.scrollWidth/scrollHeight, i.e. it treats this canvas as
   scrollable; overflow:hidden would silently clip any table placed past the
   visible fold with no way to scroll back to it. */
/* Floor canvas: a clean, cool neutral (distinct from the pink dashboard chrome) so the
   status-coloured tables read with real contrast, with a faint dot grid for alignment. */
.qm-floor-grid { position: relative; min-height: 460px; border: 1px solid var(--line); border-radius: var(--r-lg); background-color: color-mix(in srgb, var(--ink-900) 4%, #fff);
  background-image: radial-gradient(color-mix(in srgb, var(--ink-900) 10%, transparent) 1.3px, transparent 1.3px); background-size: 34px 34px; background-position: 14px 14px; overflow: auto; touch-action: none; transition: min-height var(--t); }
.qm-floor-grid.is-expanded { min-height: calc(100vh - 210px); }
.qm-floor-grid.is-locked { cursor: default; }
.qm-floor-grid.is-locked .qm-table-card { cursor: default; }
.qm-floor-grid.is-empty::after { content: attr(data-empty); position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--ink-400); font-size: 14px; padding: 20px; text-align: center; }

/* --- Floor-plan table tile: a clean centred tile whose STATUS is carried by colour
   (a solid top accent bar + border + dot), not by five stacked chips. Content is
   centred; edit/delete reveal on hover in the empty top corner - nothing collides. */
.qm-table-card { --tw: 118px; --sc: var(--ink-400); position: absolute; width: var(--tw); min-height: 96px; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; text-align: center;
  background: color-mix(in srgb, var(--sc) 7%, var(--surface)); border: 1.5px solid color-mix(in srgb, var(--sc) 55%, var(--line));
  border-top: 4px solid var(--sc); border-radius: var(--r-md); box-shadow: var(--e-1); padding: 12px 10px 11px; cursor: grab; user-select: none;
  transition: box-shadow var(--t-fast), transform var(--t-fast); }
.qm-table-card:hover { box-shadow: var(--e-3); transform: translateY(-2px); }
.qm-table-card:active { cursor: grabbing; }
.qm-table-card.is-dragging { opacity: .45; }
.qm-table-card:focus-visible { outline: 3px solid var(--qm-primary); outline-offset: 2px; z-index: 4; }
.qm-table-card.is-kbd-moving { outline: 3px dashed var(--qm-primary); outline-offset: 2px; box-shadow: var(--e-3); z-index: 5; }
/* Per-status accent colour (--sc drives the top bar, border tint, status dot). */
.qm-table-card.is-vacant { --sc: var(--ok); }
.qm-table-card.is-occupied { --sc: var(--danger); }
.qm-table-card.is-cleaning { --sc: var(--warn); }
.qm-table-card.is-reserved { --sc: var(--info); }
.qm-table-card.is-maintenance { --sc: var(--ink-400); background-image: repeating-linear-gradient(45deg, transparent, transparent 9px, color-mix(in srgb, var(--ink-900) 5%, transparent) 9px, color-mix(in srgb, var(--ink-900) 5%, transparent) 18px); }
.qm-table-card .qm-tc-label { font-weight: 800; font-size: 19px; color: var(--ink-900); line-height: 1; white-space: nowrap; }
.qm-table-card .qm-tc-seats { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 600; color: var(--ink-500); }
.qm-table-card .qm-tc-seats i { color: var(--ink-400); font-size: 11px; }
.qm-table-card .qm-tc-status { display: inline-flex; align-items: center; gap: 6px; margin-top: 3px; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; padding: 3px 10px; border-radius: var(--r-pill); border: 1px solid color-mix(in srgb, var(--sc) 34%, transparent); background: color-mix(in srgb, var(--sc) 15%, var(--surface)); color: color-mix(in srgb, var(--sc) 72%, #000); cursor: pointer; transition: filter var(--t-fast); }
.qm-table-card .qm-tc-status:hover { filter: brightness(.96); }
.qm-tc-status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--sc); flex: 0 0 auto; }
/* Merge tag sits inline in the tile flow (not an overlay), so it never collides. */
.qm-tc-merge { display: inline-flex; align-items: center; gap: 3px; margin-top: 2px; font-size: 10px; font-weight: 700; color: var(--ink-500); }
/* Corner cues (reserved-soon / event / hidden) - tiny dots, top-inline-start, stacked
   horizontally so they never overlap the number or the actions. */
.qm-tc-cues { position: absolute; top: 7px; inset-inline-start: 7px; display: flex; gap: 3px; z-index: 3; }
.qm-tc-cue { width: 17px; height: 17px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 9px; color: #fff; box-shadow: var(--e-1); }
.qm-tc-cue.is-soon { background: var(--warn); color: #4a2c00; animation: qm-kpi-pulse 2s ease-out infinite; --kc: var(--warn); }
.qm-tc-cue.is-event { background: var(--qm-primary); }
.qm-tc-cue.is-hidden { background: var(--ink-400); }
/* Card actions: reveal on hover/focus in the empty top-inline-end corner - hidden at
   rest so the tile stays clean, so they cannot collide with the centred content. */
.qm-table-card .qm-tc-actions { position: absolute; top: 6px; inset-inline-end: 6px; display: flex; gap: 4px; opacity: 0; transition: opacity var(--t-fast); z-index: 3; }
.qm-table-card:hover .qm-tc-actions, .qm-table-card:focus-within .qm-tc-actions { opacity: 1; }
.qm-table-card .qm-tc-actions button { background: var(--surface); border: 1px solid var(--line); color: var(--ink-600); width: 28px; height: 28px; border-radius: var(--r-md); font-size: 12px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--e-2); transition: color var(--t-fast), border-color var(--t-fast), background var(--t-fast); }
.qm-table-card .qm-tc-actions button:hover { color: #fff; background: var(--qm-primary); border-color: var(--qm-primary); }
.qm-table-card .qm-tc-actions [data-table-delete]:hover { background: var(--danger); border-color: var(--danger); }
.qm-table-card .qm-tc-actions button:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-leg-dot.is-maintenance { background: var(--ink-400); }
.qm-leg-dot.is-maintenance::before { content: "\f0ad"; }
.qm-table-card.is-hidden-table { opacity: .6; }

/* Floor-plan table shapes: square (base, width --tw), rectangle (a long, double-width
   table) and round (a circle). --tw is set per seat-count below so a 2-top reads
   smaller than an 8-top and the plan actually looks like a room, not a uniform grid. */
.qm-table-card.is-shape-rectangle { width: calc(var(--tw) * 1.9); }
/* Round tables read best as a full status-coloured ring rather than a top tab. */
.qm-table-card.is-shape-round { width: var(--tw); height: var(--tw); min-height: var(--tw); border: 3px solid var(--sc); border-radius: 50%; padding: 10px; }

/* Seat-count sizing: the card grows with capacity (clamped 1..12 by the view). */
.qm-table-card.is-seats-1, .qm-table-card.is-seats-2 { --tw: 100px; }
.qm-table-card.is-seats-3, .qm-table-card.is-seats-4 { --tw: 118px; }
.qm-table-card.is-seats-5, .qm-table-card.is-seats-6 { --tw: 138px; }
.qm-table-card.is-seats-7, .qm-table-card.is-seats-8 { --tw: 158px; }
.qm-table-card.is-seats-9, .qm-table-card.is-seats-10 { --tw: 178px; }
.qm-table-card.is-seats-11, .qm-table-card.is-seats-12 { --tw: 198px; }

/* Panel-open selection ring + reserved-soon warning ring (an upcoming booking within
   the hold window). Both are outlines that don't disturb the status colour beneath. */
.qm-table-card.is-selected { box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 34%, transparent), var(--e-3); z-index: 6; }
.qm-table-card.is-reserved-soon { box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 42%, transparent), var(--e-2); }
.qm-table-card.has-bookings { cursor: pointer; }

/* --- Table schedule panel (right drawer): today's reservation timeline ---- */
.qm-tsched { position: fixed; top: 0; right: 0; bottom: 0; width: min(400px, 92vw); background: var(--surface); border-inline-start: 1px solid var(--line); box-shadow: -8px 0 28px rgba(78,8,29,.14); z-index: 1050; display: flex; flex-direction: column; transform: translateX(100%); visibility: hidden; transition: transform var(--t), visibility var(--t); }
.qm-tsched.is-open { transform: translateX(0); visibility: visible; }
.qm-tsched-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 20px 20px 14px; border-bottom: 1px solid var(--line); }
.qm-tsched-title { font-size: var(--fs-800); font-weight: 800; color: var(--ink-900); margin: 0; }
.qm-tsched-sub { font-size: var(--fs-400); color: var(--ink-500); margin: 3px 0 0; }
.qm-tsched-body { flex: 1 1 auto; overflow-y: auto; padding: 12px 16px; display: flex; flex-direction: column; }
.qm-tsched-foot { padding: 14px 16px; border-top: 1px solid var(--line); }
.qm-tsched-foot .qm-btn { width: 100%; justify-content: center; }
.qm-tsched-seats { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; color: var(--ink-700); }
.qm-tsched-seats i { color: var(--ink-400); }
/* --- Service-day timeline: an "available" track with booked blocks placed + sized
   by turn time, hour ticks down the axis, and a live "now" line. This is the answer
   to "when is the table free vs reserved" at a glance. --------------------------- */
/* Timeline fills the whole panel body (no dead space); percent-positioned children
   need a definite track height, which flex-stretch + min-height provide. */
.qm-ttl { flex: 1 1 auto; display: flex; gap: 8px; padding-top: 6px; min-height: 440px; }
.qm-ttl-axis { position: relative; flex: 0 0 44px; align-self: stretch; }
.qm-ttl-tick { position: absolute; inset-inline-end: 4px; transform: translateY(-50%); font-size: 11px; font-weight: 600; color: var(--ink-400); white-space: nowrap; }
.qm-ttl-track { position: relative; flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-md); background: color-mix(in srgb, var(--ok) 6%, var(--surface)); overflow: hidden; }
.qm-ttl-line { position: absolute; inset-inline: 0; border-top: 1px dashed color-mix(in srgb, var(--ink-400) 26%, transparent); }
.qm-ttl-now { position: absolute; inset-inline: 0; border-top: 2px solid var(--qm-primary); z-index: 4; }
.qm-ttl-now span { position: absolute; inset-inline-end: 5px; top: -9px; font-size: 9px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; background: var(--qm-primary); color: #fff; padding: 0 6px; border-radius: var(--r-pill); }
.qm-ttl-block { position: absolute; inset-inline: 6px; min-height: 44px; border-radius: var(--r-sm); padding: 5px 9px; overflow: hidden; border-inline-start: 3px solid var(--info); background: var(--info-bg); box-shadow: var(--e-1); z-index: 2; }
.qm-ttl-block.is-confirmed, .qm-ttl-block.is-pending, .qm-ttl-block.is-waitlisted { border-inline-start-color: var(--info); background: var(--info-bg); }
.qm-ttl-block.is-next { border-inline-start-color: var(--warn); background: var(--warn-bg); box-shadow: 0 0 0 2px color-mix(in srgb, var(--warn) 26%, transparent), var(--e-1); }
.qm-ttl-block.is-seated { border-inline-start-color: var(--danger); background: var(--danger-bg); }
.qm-ttl-block.is-past { border-inline-start-color: var(--line-2); background: var(--surface-2); opacity: .72; }
.qm-ttl-b-time { display: block; font-size: 12px; font-weight: 800; color: var(--ink-900); }
.qm-ttl-b-guest { display: block; font-size: 12.5px; font-weight: 700; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-ttl-b-meta { display: block; font-size: 11px; color: var(--ink-500); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-ttl-occ { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--oc, #64748b); margin-inline-end: 5px; vertical-align: middle; }
.qm-ttl-free { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 7px; color: var(--ok); font-weight: 700; font-size: var(--fs-400); }

/* ======================= Floor / Timeline view toggle ===================== */
.qm-tables-viewseg { flex: 0 0 auto; }
.qm-tables-views[data-tables-view='floor'] .qm-tv-timeline { display: none; }
.qm-tables-views[data-tables-view='timeline'] .qm-tv-floor { display: none; }

/* ======================= Full-width reservation timeline (Gantt) ==========
   Tables down the left (sticky), today's service window across the top, each
   booking placed by start + sized by turn time. Empty track = the table is free. */
.qm-gantt-card { padding: 0; overflow: hidden; }
.qm-gantt { --g-tbl-w: 152px; }
.qm-gantt-scroll { overflow-x: auto; overflow-y: visible; }
.qm-gantt-head { display: flex; position: sticky; top: 0; z-index: 5; background: var(--surface); border-bottom: 2px solid var(--line); width: max-content; }
.qm-gantt-corner { position: sticky; inset-inline-start: 0; z-index: 6; flex: 0 0 var(--g-tbl-w); display: flex; align-items: center; gap: 6px; padding: 11px 12px; font-weight: 700; font-size: 11.5px; color: var(--ink-500); background: var(--surface); border-inline-end: 2px solid var(--line); }
.qm-gantt-axis { position: relative; height: 42px; flex: 0 0 auto; }
.qm-gantt-hour { position: absolute; top: 13px; transform: translateX(-50%); font-size: 11px; font-weight: 600; color: var(--ink-400); white-space: nowrap; }
.qm-gantt-body { position: relative; width: max-content; min-width: 100%; }
.qm-gantt-zone { position: sticky; inset-inline-start: 0; z-index: 4; display: flex; align-items: center; gap: 7px; padding: 6px 12px; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-600); background: color-mix(in srgb, var(--ink-900) 4%, var(--surface)); border-bottom: 1px solid var(--line); width: max-content; min-width: 100%; }
.qm-gantt-zone-n { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 7px; font-size: 10px; color: var(--ink-500); }
.qm-gantt-row { display: flex; border-bottom: 1px solid var(--line); }
.qm-gantt-row:last-child { border-bottom: 0; }
/* The table cell is a link → new booking pre-selected for that table (hover reveals a +). */
.qm-gantt-tbl { position: sticky; inset-inline-start: 0; z-index: 3; flex: 0 0 var(--g-tbl-w); display: flex; align-items: center; gap: 8px; padding: 0 12px; background: var(--surface); border-inline-end: 2px solid var(--line); text-decoration: none; color: inherit; cursor: pointer; transition: background var(--t-fast); }
.qm-gantt-tbl:hover { background: color-mix(in srgb, var(--qm-primary) 8%, var(--surface)); }
.qm-gantt-tbl:hover .qm-gantt-tnum { color: var(--qm-primary-600); }
.qm-gantt-tbl:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: -2px; }
.qm-gantt-tbl-go { margin-inline-start: 4px; font-size: 11px; color: var(--qm-primary); opacity: 0; transition: opacity var(--t-fast); }
.qm-gantt-tbl:hover .qm-gantt-tbl-go, .qm-gantt-tbl:focus-visible .qm-gantt-tbl-go { opacity: 1; }
.qm-gantt-tnum { font-weight: 800; font-size: 14px; color: var(--ink-900); }
.qm-gantt-tseat { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--ink-500); }
.qm-gantt-tseat i { font-size: 10px; color: var(--ink-400); }
.qm-gantt-tdot { width: 9px; height: 9px; border-radius: 50%; margin-inline-start: auto; box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 16%, transparent); }
.qm-gantt-tdot.is-vacant { color: var(--ok); background: var(--ok); }
.qm-gantt-tdot.is-occupied { color: var(--danger); background: var(--danger); }
.qm-gantt-tdot.is-cleaning { color: var(--warn); background: var(--warn); }
.qm-gantt-tdot.is-reserved { color: var(--info); background: var(--info); }
.qm-gantt-tdot.is-maintenance { color: var(--ink-400); background: var(--ink-400); }
.qm-gantt-track { position: relative; flex: 0 0 auto; height: 56px; background: color-mix(in srgb, var(--ok) 5%, var(--surface)); }
.qm-gantt-grid { position: absolute; top: 0; bottom: 0; width: 1px; background: color-mix(in srgb, var(--ink-900) 7%, transparent); }
.qm-gantt-block { position: absolute; top: 6px; bottom: 6px; border-radius: var(--r-sm); padding: 4px 8px; overflow: hidden; border-inline-start: 3px solid var(--info); background: var(--info-bg); box-shadow: var(--e-1); display: flex; flex-direction: column; justify-content: center; gap: 1px; z-index: 2; cursor: default; }
.qm-gantt-block.is-upcoming { border-inline-start-color: var(--info); background: var(--info-bg); }
.qm-gantt-block.is-seated { border-inline-start-color: var(--danger); background: var(--danger-bg); }
.qm-gantt-block.is-past { border-inline-start-color: var(--line-2); background: var(--surface-2); opacity: .8; }
.qm-gantt-b-time { font-size: 11px; font-weight: 800; color: var(--ink-900); white-space: nowrap; }
.qm-gantt-b-guest { font-size: 11px; font-weight: 600; color: var(--ink-700); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-gantt-b-party { font-size: 10px; color: var(--ink-500); white-space: nowrap; }
.qm-gantt-now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--qm-primary); z-index: 4; pointer-events: none; }
.qm-gantt-now span { position: absolute; top: 2px; inset-inline-start: 50%; transform: translateX(-50%); font-size: 9px; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; background: var(--qm-primary); color: #fff; padding: 1px 6px; border-radius: var(--r-pill); white-space: nowrap; }

/* Shape picker in the add/edit-table modal — each option's preview is a miniature
floor table card (same surface / pink border / soft shadow as the real cards), so
it previews the actual result and matches the theme. Active = brand tint + glow. */
.qm-shape-picker { display: flex; gap: 12px; }
.qm-shape-opt { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 11px; padding: 16px 8px 12px; background: var(--surface); border: 1.5px solid var(--line); border-radius: var(--r-md); cursor: pointer; color: var(--ink-600); font-size: 12px; font-weight: 700; transition: all var(--t-fast); }
.qm-shape-opt:hover { border-color: var(--qm-primary-100); box-shadow: var(--e-1); color: var(--ink-900); }
.qm-shape-opt.is-active { border-color: var(--qm-primary); background: var(--qm-primary-50); color: var(--qm-primary); box-shadow: var(--e-primary); }
.qm-shape-opt:focus-visible { outline: 3px solid var(--qm-primary); outline-offset: 2px; }
.qm-shape-ico { display: block; background: var(--surface); border: 2px solid var(--line); box-shadow: var(--e-1); transition: all var(--t-fast); }
.qm-shape-opt:hover .qm-shape-ico { border-color: var(--qm-primary-100); }
.qm-shape-opt.is-active .qm-shape-ico { border-color: var(--qm-primary); box-shadow: var(--e-primary); }
.qm-shape-ico-square { width: 30px; height: 30px; border-radius: 7px; }
.qm-shape-ico-rect { width: 46px; height: 26px; border-radius: 7px; }
.qm-shape-ico-round { width: 32px; height: 32px; border-radius: 50%; }
.qm-shape-name { line-height: 1; }
.qm-tc-merge i { font-size: 8px; }
.qm-table-ghost { position: fixed; z-index: 1080; width: 118px; pointer-events: none; opacity: .92; transform: rotate(-1.5deg); box-shadow: var(--e-3); }

/* --- Merged tables: show two tables joined as one seating unit --------------
A joined table names its partner (badge) and, on hover/focus, lights up its
whole group so the connection is visible on the floor, not just implied. In the
timeline a joined row is chipped and its (mirrored) blocks carry a link cue, so
a party seated across a pair reads as both tables busy — matching the booking
rules, which already treat a merge group as one unit. Accent = --info (linked). */
.qm-table-card.is-merged { outline: 1.5px solid color-mix(in srgb, var(--info) 32%, transparent); outline-offset: 2px; }
.qm-table-card.is-merge-hi { outline: 2.5px solid var(--info); outline-offset: 3px; box-shadow: 0 0 0 4px var(--info-bg), var(--e-2); z-index: 6; }
.qm-gantt-tmerge { display: inline-flex; align-items: center; gap: 2px; font-size: 9px; font-weight: 800; color: var(--info); background: var(--info-bg); padding: 0 4px; border-radius: var(--r-pill); line-height: 1.5; white-space: nowrap; }
.qm-gantt-tmerge i { font-size: 7px; }
.qm-gantt-row.is-merged .qm-gantt-tbl { box-shadow: inset 3px 0 0 color-mix(in srgb, var(--info) 45%, transparent); }
.qm-gantt-block.is-joined { border-inline-end: 2px dashed color-mix(in srgb, var(--info) 55%, transparent); }
.qm-gantt-b-join { color: var(--info); font-size: 8px; }
.qm-ttl-block.is-joined { box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--info) 40%, transparent); }
.qm-ttl-b-join { color: var(--info); font-size: 9px; }
.qm-tsched-join { display: inline-flex; align-items: center; gap: 3px; font-weight: 700; color: var(--info); }
.qm-tsched-join i { font-size: 9px; }

/* --- Kitchen Pipeline (KDS board) ----------------------------------------
Live order board polished to the dashboard's modern/vibrant feel: rounded
cards, soft warm shadows, colour-coded column headers (New=blue,
Preparing=amber, Ready=green). Styling only — board behaviour unchanged. */
.qm-kds { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; align-items: start; }
/* Per-process colour theming: New=blue, Preparing=amber, Ready=green. */
.qm-kds-col { position: relative; background: linear-gradient(180deg, var(--kds-soft, var(--surface)) 0%, var(--surface) 64px); border: 1px solid var(--kds-line, var(--line)); border-radius: var(--r-lg); padding: 16px; min-height: 220px; box-shadow: var(--e-1); transition: box-shadow var(--t), transform var(--t); }
.qm-kds-col:hover { box-shadow: var(--e-2); transform: translateY(-2px); }
.qm-kds-col[data-col="new"] { --kds-c: #2563eb; --kds-c-700: #1d4ed8; --kds-soft: #eef4ff; --kds-line: #cfe0ff; }
.qm-kds-col[data-col="preparing"] { --kds-c: #c2660a; --kds-c-700: #9a4f06; --kds-soft: #fff6ec; --kds-line: #fadcb8; }
.qm-kds-col[data-col="ready"] { --kds-c: #138a3b; --kds-c-700: #0f6e30; --kds-soft: #eefaf1; --kds-line: #c2ead0; }
.qm-kds-col-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--kds-line, var(--line)); }
/* Markup uses <h2> for the column title (see kitchen.php). */
.qm-kds-col-head h2 { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-400); font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--kds-c-700, var(--ink-700)); margin: 0; }
.qm-kds-col-head h2::before { content: ""; width: 9px; height: 9px; border-radius: var(--r-pill); background: var(--kds-c, var(--qm-primary)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--kds-c, var(--qm-primary)) 18%, transparent); }
.qm-kds-col-head .qm-kds-count { min-width: 26px; text-align: center; background: var(--kds-c, var(--qm-primary)); border: 0; color: #fff; font-weight: 800; font-size: 12px; border-radius: var(--r-pill); padding: 3px 10px; box-shadow: 0 4px 10px color-mix(in srgb, var(--kds-c, var(--qm-primary)) 28%, transparent); }
.qm-kds-card { background: var(--surface); border: 1px solid var(--line); border-left: 4px solid var(--kds-c, var(--qm-primary)); border-radius: var(--r-md); box-shadow: var(--e-1); padding: 13px 14px; margin-bottom: 12px; transition: box-shadow var(--t), transform var(--t), border-color var(--t); }
.qm-kds-card:last-child { margin-bottom: 0; }
.qm-kds-card:hover { box-shadow: var(--e-2); transform: translateY(-2px); border-left-color: var(--kds-c-700, var(--kds-c, var(--qm-primary))); }
.qm-kds-card.is-late { border-left-color: var(--danger); box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 22%, transparent), var(--e-1); }
.qm-kds-card-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.qm-kds-card-top .qm-kds-no { font-weight: 800; color: var(--ink-900); font-size: var(--fs-600, 15px); letter-spacing: -.01em; margin-right: auto; }
.qm-kds-card-top .qm-kds-mode { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; border-radius: var(--r-pill); padding: 3px 10px; }
.qm-kds-mode-delivery { color: #1d4ed8; background: #eaf1ff; }
.qm-kds-mode-pickup { color: var(--warn); background: var(--warn-bg); }
/* Payment badge on the KDS card (paid vs money-still-due). */
.qm-kds-card-top .qm-kds-pay { font-size: 11px; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; border-radius: var(--r-pill); padding: 3px 9px; display: inline-flex; align-items: center; gap: 4px; }
.qm-kds-pay.is-paid { color: var(--ok); background: var(--ok-bg); }
.qm-kds-pay.is-due { color: var(--warn); background: var(--warn-bg); }
.qm-kds-cust { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--ink-700); margin-top: 7px; }
.qm-kds-cust i { color: var(--ink-400); }
.qm-kds-cust .qm-kds-phone { color: var(--ink-500); font-weight: 500; }
.qm-kds-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; margin: 9px 0; }
.qm-kds-timer { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--ink-500); background: var(--surface-2); border-radius: var(--r-pill); padding: 2px 10px; }
.qm-kds-timer i { color: var(--ink-400); }
.qm-kds-sched { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--warn); background: var(--warn-bg); border-radius: var(--r-pill); padding: 2px 10px; }
.qm-kds-card.is-late .qm-kds-timer { color: var(--danger); background: var(--danger-bg); }
.qm-kds-card.is-late .qm-kds-timer i { color: var(--danger); }
.qm-kds-items { list-style: none; margin: 0 0 11px; padding: 0; border-top: 1px solid var(--line-2); padding-top: 9px; }
.qm-kds-items li { font-size: var(--fs-400); color: var(--ink-700); padding: 4px 0; }
.qm-kds-items li + li { border-top: 1px dashed var(--line-2); }
.qm-kds-item-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.qm-kds-item-name { font-weight: 600; margin-right: auto; }
.qm-kds-items .qm-kds-qty { font-weight: 800; color: var(--qm-primary-600); }
.qm-kds-opt { font-size: 11px; color: var(--ink-500); margin-top: 3px; padding-left: 9px; border-left: 2px solid var(--kds-line, var(--line)); }
/* Per-line prep tick. Sized for a gloved finger on a wall screen, not a mouse: the
   control is the box itself rather than a small glyph inside a large hit area. */
.qm-kds-tick { flex: 0 0 auto; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: 1.5px solid var(--line); background: var(--surface); color: transparent; border-radius: var(--r-sm); cursor: pointer; transition: background var(--t), border-color var(--t), color var(--t); }
.qm-kds-tick:hover:not(:disabled) { border-color: var(--kds-c, var(--qm-primary)); color: color-mix(in srgb, var(--kds-c, var(--qm-primary)) 45%, transparent); }
.qm-kds-tick:disabled { opacity: .55; cursor: progress; }
.qm-kds-tick[aria-pressed="true"] { background: var(--ok); border-color: var(--ok); color: #fff; }
.qm-kds-items li.is-ready .qm-kds-item-name { color: var(--ink-400); text-decoration: line-through; font-weight: 600; }
.qm-kds-items li.is-ready .qm-kds-qty { color: var(--ink-400); }
.qm-kds-items li.is-ready .qm-kds-opt { color: var(--ink-400); }
/* Where a line is cooked. Shown only on the whole-kitchen board, where a pass needs to
   see which screen a dish went to; on a station screen every line is that station's. */
.qm-kds-station { flex: 0 0 auto; font-size: 11px; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; color: var(--ink-500); background: var(--surface-2); border-radius: var(--r-pill); padding: 2px 8px; }
/* Lines ticked, out of lines on the ticket — sits with the timer, and turns green
   the moment the pass has finished the whole ticket. */
.qm-kds-prog { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--ink-500); background: var(--surface-2); border-radius: var(--r-pill); padding: 2px 10px; }
.qm-kds-prog i { color: var(--ink-400); }
.qm-kds-prog.is-done { color: var(--ok); background: var(--ok-bg); }
.qm-kds-prog.is-done i { color: var(--ok); }
.qm-kds-note { display: flex; align-items: flex-start; gap: 6px; font-size: 12px; font-weight: 600; color: var(--warn); background: var(--warn-bg); border: 1px solid color-mix(in srgb, var(--warn) 28%, transparent); border-radius: var(--r-sm); padding: 7px 10px; margin-bottom: 11px; }
.qm-kds-note i { margin-top: 1px; }
.qm-kds-empty { color: var(--ink-400); font-size: var(--fs-400); font-weight: 600; text-align: center; padding: 30px 8px; border: 1px dashed var(--kds-line, var(--line)); border-radius: var(--r-md); background: color-mix(in srgb, var(--kds-soft, var(--surface)) 55%, #fff); }
.qm-kds-advance { width: 100%; }
.qm-kds-foot { display: flex; align-items: center; gap: 8px; margin-top: 11px; }
/* Advance button adopts the column's process colour so the CTA reads in-context. */
.qm-kds-advance.qm-btn { background: var(--kds-c, var(--qm-primary)); border-color: var(--kds-c, var(--qm-primary)); box-shadow: 0 6px 14px color-mix(in srgb, var(--kds-c, var(--qm-primary)) 26%, transparent); }
.qm-kds-advance.qm-btn:hover { background: var(--kds-c-700, var(--kds-c, var(--qm-primary))); border-color: var(--kds-c-700, var(--kds-c, var(--qm-primary))); transform: translateY(-1px); }
/* Header live clock — monospace pill so the ticking digits don't jitter the layout. */
#qm-kds-clock { display: inline-flex; align-items: center; font-family: var(--f-mono); font-size: var(--fs-400); font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink-700); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 12px; }

/* --- AI assistant chat ---------------------------------------------------- */
.qm-chat-notice { display: flex; gap: 12px; align-items: flex-start; background: var(--info-bg); border: 1px solid #bcd6f7; color: var(--ink-700); border-radius: var(--r-md); padding: 14px 16px; margin-bottom: 16px; }
.qm-chat-notice i { color: var(--info); font-size: var(--fs-800); margin-top: 1px; }
.qm-chat-log { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 18px; height: 440px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }
.qm-chat-msg { display: flex; gap: 10px; max-width: 86%; }
.qm-chat-msg.is-user { align-self: flex-end; flex-direction: row-reverse; }
.qm-chat-avatar { flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-600); color: #fff; overflow: hidden; }
.qm-chat-msg.is-bot .qm-chat-avatar { background: var(--qm-grad); }
.qm-chat-msg.is-user .qm-chat-avatar { background: var(--ink-700); }
.qm-chat-avatar img { width: 100%; height: 100%; object-fit: cover; }
.qm-chat-bubble { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 14px; font-size: 14px; line-height: 1.5; color: var(--ink-700); white-space: pre-wrap; word-wrap: break-word; }
.qm-chat-msg.is-user .qm-chat-bubble { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--ink-800); }
.qm-chat-bubble.is-typing span { display: inline-block; width: 7px; height: 7px; margin: 0 1px; border-radius: 50%; background: var(--ink-300); animation: qm-typing 1s infinite ease-in-out; }
.qm-chat-bubble.is-typing span:nth-child(2) { animation-delay: .15s; }
.qm-chat-bubble.is-typing span:nth-child(3) { animation-delay: .3s; }
@keyframes qm-typing { 0%, 60%, 100% { opacity: .3; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-3px); } }
.qm-chat-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 14px 0; }
.qm-chat-chip { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 7px 14px; font-size: var(--fs-400); color: var(--ink-600); cursor: pointer; transition: all var(--t-fast); }
.qm-chat-chip:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-chat-form { display: flex; gap: 10px; margin-top: 14px; }
.qm-chat-form .form-control { flex: 1; }
@media (prefers-reduced-motion: reduce) { .qm-chat-bubble.is-typing span { animation: none; } }

/* --- The assistant surfaces — the PAGE (.qm-d2 .qm-card.qm-chat) and the
   header off-canvas (#qmAiOffcanvas .qm-chat) wear ONE treatment: a flexing
   borderless log that opens top-left with the assistant's welcome bubble
   (wearing the admin-set assistant avatar, its one identity mark) and a
   composer pinned behind a hairline. The PAGE also offers starter prompts
   as a card grid that yields once the conversation starts; the drawer
   carries none (its markup passes no suggestions). Only geometry differs:
   the page owns viewport height (focus mode included); the drawer keeps
   its own flex height. */
.qm-d2 .qm-card.qm-chat { display: flex; flex-direction: column; height: calc(100dvh - 264px); min-height: 540px; }
.qm-d2 .qm-card.qm-chat .qm-chat-log,
#qmAiOffcanvas .qm-chat .qm-chat-log { flex: 1 1 auto; height: auto; min-height: 0; background: transparent; border: 0; border-radius: 0; padding: 6px 4px; }
.qm-d2 .qm-card.qm-chat .qm-chat-chips { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin: 14px 0 0; }
.qm-d2 .qm-card.qm-chat .qm-chat-chip {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    text-align: start; border-radius: var(--r-md); padding: 12px 14px;
    font-size: 12px; font-weight: 600; color: var(--ink-700, #44403c); line-height: 1.4;
}
.qm-d2 .qm-card.qm-chat .qm-chat-chip::after {
    content: '\f054'; font-family: 'Font Awesome 5 Free'; font-weight: 900;
    font-size: 10px; color: var(--ink-300, #d6d3d1); transition: color var(--t-fast), transform var(--t-fast);
}
.qm-d2 .qm-card.qm-chat .qm-chat-chip:hover { border-color: var(--qm-primary); color: var(--ink-900, #1c1917); box-shadow: var(--e-1); transform: translateY(-1px); }
.qm-d2 .qm-card.qm-chat .qm-chat-chip:hover::after { color: var(--qm-primary); transform: translateX(2px); }
/* Prompts are for starting; a running conversation reclaims the room. */
.qm-d2 .qm-card.qm-chat:has(.qm-chat-msg + .qm-chat-msg) .qm-chat-chips { display: none; }
.qm-d2 .qm-card.qm-chat .qm-chat-form,
#qmAiOffcanvas .qm-chat .qm-chat-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.qm-d2 .qm-card.qm-chat .qm-chat-form .form-control,
#qmAiOffcanvas .qm-chat .qm-chat-form .form-control { min-height: 46px; border-radius: var(--r-pill); background: var(--surface-2, #faf9f7); padding-inline: 18px; }
.qm-d2 .qm-card.qm-chat .qm-chat-form .form-control:focus,
#qmAiOffcanvas .qm-chat .qm-chat-form .form-control:focus { background: var(--surface, #fff); }
@media (max-width: 991.98px) { .qm-d2 .qm-card.qm-chat .qm-chat-chips { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575.98px) {
    .qm-d2 .qm-card.qm-chat { height: calc(100dvh - 210px); }
    .qm-d2 .qm-card.qm-chat .qm-chat-chips { grid-template-columns: 1fr; }
}
/* Focus mode (body.qm-fullscreen) hides the whole chrome, so the viewport-math
   surfaces stop reserving room for it: the chat stretches to the screen, and
   the setup rail pins to the top instead of 192px below it. */
body.qm-fullscreen .qm-d2 .qm-card.qm-chat { height: calc(100dvh - 32px); }
body.qm-fullscreen .qm-d2 .qm-su-side { top: 16px; }
body.qm-fullscreen .qm-d2 .qm-su-group { scroll-margin-top: 16px; }

/* AI sparkle mark — inline SVG (three sparkles) in the brand gradient. Sits inside
   the round header icon-pill and next to the panel title; sizing only, colour is
   the gradient baked into the SVG. */
.qm-ai-spark { width: 19px; height: 19px; display: inline-block; vertical-align: middle; }
#qmAiOffcanvas .offcanvas-title .qm-ai-spark { width: 20px; height: 20px; }
/* Mono sparkle sitting in an 18px icon slot (sidebar nav item + settings tab) —
   sizing only; colour is currentColor so it tracks the surrounding text/hover. */
.qm-dash-nav a .qm-ai-spark, .qm-settings-nav button .qm-ai-spark { width: 18px; height: 16px; flex-shrink: 0; }

/* Header AI off-canvas — fill the panel height so the composer pins flush to the
   bottom with no dead space; the log grows to take the free height. Scoped to the
   panel so the full-page vendor assistant keeps its fixed-height log. */
#qmAiOffcanvas.offcanvas-end { width: min(800px, 94vw); }
#qmAiOffcanvas .offcanvas-header { padding: 18px 20px; border-bottom: 1px solid var(--line); }
#qmAiOffcanvas .offcanvas-title { display: inline-flex; align-items: center; line-height: 1.2; }
#qmAiOffcanvas .offcanvas-body { display: flex; flex-direction: column; padding: 18px 20px; }
#qmAiOffcanvas .qm-chat { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
#qmAiOffcanvas .qm-chat-log { flex: 1 1 auto; height: auto; min-height: 0; }
#qmAiOffcanvas .qm-chat-form { margin-top: 14px; }

/* --- Sectioned settings page --------------------------------------------- */
/* minmax(0, …), not a bare 1fr. A 1fr track is minmax(auto, 1fr), and that auto floor is the
   content's MIN-content width, so the column refuses to go narrower than its widest
   unbreakable child and pushes the page out sideways instead. On Menu → Structure the track
   computed to 526px inside a 351px container and the whole dashboard scrolled. Zero floor lets
   the column take the space it is given; the panels inside already handle their own overflow
   (the settings nav is a deliberate swipeable strip). */
.qm-settings { display: grid; grid-template-columns: 232px minmax(0, 1fr); gap: 24px; align-items: start; }
.qm-settings-nav { position: sticky; top: var(--qm-dash-stick-top, 92px); display: flex; flex-direction: column; gap: 4px; }
/* Buttons switch a panel in place; LINKS are for a nav whose sections are separate requests —
   Classifications pages each list server-side, so its entries cannot be data-panel buttons. Same
   control either way, so they share the rule rather than growing a second look. */
.qm-settings-nav button,
.qm-settings-nav a { display: flex; align-items: center; gap: 11px; width: 100%; text-align: left; padding: 11px 14px; border-radius: var(--r-sm); border: 1px solid transparent; background: transparent; color: var(--ink-600); font-size: 14px; font-weight: 600; cursor: pointer; text-decoration: none; transition: all var(--t-fast); }
.qm-settings-nav button:hover,
.qm-settings-nav a:hover { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-settings-nav button.is-active,
.qm-settings-nav a.is-active { background: var(--qm-grad); color: #fff; box-shadow: var(--e-1); }
.qm-settings-nav button i,
.qm-settings-nav a i { width: 18px; text-align: center; }
/* A heading over a run of entries, so a nav that mixes two KINDS of section says which is which. */
.qm-settings-nav .qm-of-label { padding: 12px 14px 4px; }
.qm-settings-nav .qm-of-label:first-child { padding-top: 0; }
/* The count sits at the far end of the row, not against the label. */
.qm-settings-nav .qm-tab-count { margin-inline-start: auto; }
.qm-settings-panel { display: none; }
.qm-settings-panel.is-active { display: block; animation: qm-fade-in .25s ease; }
@keyframes qm-fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.qm-settings-panel :is(h1,h2,h3,h4,h5,h6) { font-size: 19px; margin-bottom: 4px; }
.qm-hours-row { display: grid; grid-template-columns: 130px 1fr 1fr 110px; gap: 12px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--line-2); }
.qm-hours-row:last-child { border-bottom: 0; }
.qm-hours-row .qm-hours-day { font-weight: 600; color: var(--ink-700); }
/* Delivery-tier rows separate exactly like the opening-hours rows above. */
.qm-zone-row { padding: 8px 0; border-bottom: 1px solid var(--line-2); }
.qm-zone-row:last-child { border-bottom: 0; }

/* --- Responsive ----------------------------------------------------------- */
/* The dashboard content column is 332px narrower than the viewport (276px rail +
2x28px padding), so three KDS columns stop fitting well above the 900px tier the
storefront uses. Collapsed on the existing 1100px component tier; .qm-hours-row
stays on 900 because it also renders on account/edit.php, which has no rail. */
@media (max-width: 1100px) {
  .qm-kds { grid-template-columns: 1fr; }
  .qm-settings { grid-template-columns: minmax(0, 1fr); }
  /* Swipeable single-row tab strip. flex-wrap:wrap used to pile the tabs into several
     messy rows on tablet/mobile; instead they now sit on one row that scrolls (swipes)
     horizontally with the scrollbar hidden. */
  .qm-settings-nav { position: static; flex-direction: row; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; scroll-snap-type: x proximity; gap: 6px; padding-bottom: 6px; margin-bottom: 6px; }
  .qm-settings-nav::-webkit-scrollbar { display: none; }
  .qm-settings-nav button { width: auto; flex: 0 0 auto; white-space: nowrap; scroll-snap-align: start; }
}
@media (max-width: 900px) {
  .qm-hours-row { grid-template-columns: 1fr 1fr; }
  .qm-hours-row .qm-hours-day { grid-column: 1 / -1; }
}

/* Scroll-arrow reachability for tab strips (settings-tabs.js): the .qm-settings-nav pill nav
   AND the .qm-tabs underline strip. Desktop wrapper is transparent (display:contents) so the
   strip renders as before; on tablet/mobile it becomes one scrolling row whose prev/next
   arrows appear only while it overflows, so every tab is reachable by tap (not just swipe). */
.qm-snav-wrap, .qm-tabs-wrap { display: contents; }
.qm-snav-arrow { display: none; align-items: center; justify-content: center; flex: 0 0 auto; width: 34px; height: 42px; border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface); color: var(--qm-primary); cursor: pointer; font-size: 12px; box-shadow: var(--e-1); }
.qm-snav-arrow.is-off { visibility: hidden; }
@media (max-width: 1100px) {
  .qm-snav-wrap { display: flex; align-items: center; gap: 4px; margin-bottom: 6px; min-width: 0; max-width: 100%; }
  .qm-snav-wrap > .qm-settings-nav { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
  .qm-snav-wrap.has-overflow .qm-snav-arrow { display: inline-flex; }
}
@media (max-width: 991.98px) {
  .qm-tabs-wrap { display: flex; align-items: center; gap: 4px; min-width: 0; max-width: 100%; margin: var(--sp-4) 0 var(--sp-5); }
  .qm-tabs-wrap > .qm-tabs { flex: 1 1 auto; min-width: 0; margin: 0; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .qm-tabs-wrap > .qm-tabs::-webkit-scrollbar { display: none; }
  .qm-tabs-wrap > .qm-tabs .qm-tab { flex: 0 0 auto; white-space: nowrap; }
  .qm-tabs-wrap.has-overflow .qm-snav-arrow { display: inline-flex; height: 38px; }
}

/* Footer "We accept" payment strip — constrain it (the template's global img{height:auto}
was overriding the height="26" attribute, so the wide PayPal/cards strip rendered full-size). */
.copyright .payment-logo img { height: 28px; width: auto; max-width: 100%; border-radius: 4px; }

/* ============================================================================
Loyalty rewards (impact meter, reward cards, badges, strip)
============================================================================ */
/* Owner impact meter — % give-back bar + real-calculation note. */
.qm-loyalty-meter { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-5); }
.qm-loyalty-meter-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 8px; }
.qm-loyalty-meter-pct { font-weight: 800; color: var(--qm-primary); font-size: 22px; line-height: 1; }
.qm-loyalty-meter[data-level="watch"] .qm-loyalty-meter-pct { color: #c47d05; }
.qm-loyalty-meter[data-level="high"] .qm-loyalty-meter-pct { color: #c0392b; }
.qm-loyalty-meter-track { height: 12px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; }
.qm-loyalty-meter-fill { display: block; height: 100%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--qm-primary), var(--qm-primary-600)); transition: width .6s ease; }
.qm-loyalty-meter[data-level="watch"] .qm-loyalty-meter-fill { background: linear-gradient(90deg, var(--gold), #f59f00); }
.qm-loyalty-meter[data-level="high"] .qm-loyalty-meter-fill { background: linear-gradient(90deg, #ff8a3d, #d63b3b); }
.qm-loyalty-meter-note { font-size: var(--fs-400); color: var(--ink-500); margin: 12px 0 0; }
.qm-loyalty-meter-note strong { color: var(--ink-800); }
/* Per-reward effective-% table with mini bars (the table itself is .qm-table). */
.qm-loyalty-rowbar { width: 90px; height: 8px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; display: inline-block; vertical-align: middle; }
.qm-loyalty-rowbar > span { display: block; height: 100%; background: var(--qm-primary); border-radius: var(--r-pill); }
/* Loyalty member lookup: the phone is the identifier. The number goes in on the left and the members it
   matches list under it as it is typed; the one picked opens on the right with the owner's adjust fields
   (points, reason, apply) on one line, and the balance updates in place. */
.qm-loy-hint { margin: 6px 0 0; font-size: var(--fs-400); color: var(--ink-500); }
.qm-loy-results-head { display: flex; align-items: center; gap: 8px; margin: 10px 0 6px; font-weight: 700; font-size: var(--fs-400); color: var(--ink-700); }
.qm-loy-results { display: flex; flex-direction: column; gap: 6px; max-height: 340px; overflow-y: auto; }
.qm-loy-empty { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 14px 16px; text-align: center; color: var(--ink-500); border: 1px dashed var(--line); border-radius: var(--r-md); }
.qm-loy-empty i { font-size: 22px; color: var(--ink-400); }
.qm-loy-empty p { margin: 0; max-width: 32ch; }
.qm-loy-msg { margin: 10px 0 0; font-size: var(--fs-400); font-weight: 600; color: var(--ok, #1e8e3e); }
.qm-loy-msg.is-error { color: var(--danger, #c0392b); }
/* Settings -> Loyalty: the three channel switches, one per row, a rule between them. */
.qm-loy-chans .qm-check { padding: 10px 0; }
.qm-loy-chans .qm-check + .qm-check { border-top: 1px solid var(--line); }
.qm-loy-row { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.3fr) auto; gap: 10px; align-items: center; width: 100%; padding: 10px 12px; text-align: start; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; transition: border-color .12s ease, background .12s ease; }
.qm-loy-row:hover, .qm-loy-row:focus-visible { background: var(--qm-primary-50); border-color: var(--qm-primary); outline: none; }
.qm-loy-row.is-active { border-color: var(--qm-primary); box-shadow: inset 3px 0 0 var(--qm-primary); }
.qm-loy-row-main { display: flex; flex-direction: column; min-width: 0; }
.qm-loy-row-main strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-900); }
.qm-loy-row-phone { font-variant-numeric: tabular-nums; font-size: var(--fs-400); color: var(--ink-700); }
.qm-loy-row-email { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-400); color: var(--ink-500); }
.qm-loy-none { margin: 0; padding: 10px 0; color: var(--ink-500); }
.qm-loy-sel { height: 100%; display: flex; flex-direction: column; padding: 16px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.qm-loy-sel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.qm-loy-sel-who { display: flex; flex-direction: column; min-width: 0; }
.qm-loy-sel-name { font-size: var(--fs-600); color: var(--ink-900); }
.qm-loy-sel-phone { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink-700); }
.qm-loy-sel-email { font-size: var(--fs-400); color: var(--ink-500); overflow: hidden; text-overflow: ellipsis; }
.qm-loy-sel-facts { display: flex; gap: 20px; margin: 10px 0 0; }
.qm-loy-sel-facts dt { margin: 0; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-500); }
.qm-loy-sel-facts dd { margin: 0; font-weight: 700; color: var(--ink-900); }
.qm-loy-adjust { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: auto; padding-top: 14px; }
.qm-loy-adjust .qm-loy-pts { width: 120px; flex: 0 0 auto; }
.qm-loy-adjust .qm-loy-note { min-width: 160px; flex: 1 1 160px; }
/* Reward cards (customer rewards page + checkout claim list). */
.qm-reward-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.qm-reward-card { position: relative; border: 1px solid var(--line); border-radius: var(--r-md); padding: 16px; background: var(--surface); display: flex; flex-direction: column; gap: 8px; }
.qm-reward-card.is-eligible { border-color: var(--qm-primary-100); background: linear-gradient(180deg, #fff, var(--qm-primary-50)); }
.qm-reward-ico { width: 38px; height: 38px; border-radius: 12px; background: var(--qm-primary-50); color: var(--qm-primary); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-700); }
.qm-reward-title { font-weight: 700; font-size: var(--fs-600, 15px); margin: 0; color: var(--ink-900); }
.qm-reward-cost { font-size: 12px; font-weight: 700; color: var(--qm-primary); }
.qm-reward-need { font-size: 12px; color: var(--ink-400); }
.qm-points-pill { display: inline-flex; align-items: center; gap: 6px; background: var(--qm-primary-50); color: var(--qm-primary-600); border: 1px solid var(--qm-primary-100); border-radius: var(--r-pill); padding: 5px 12px; font-weight: 700; font-size: var(--fs-400); }
/* Customer balance strip on the restaurant detail page. */
.qm-loyalty-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; background: linear-gradient(180deg, #fff, var(--qm-primary-50)); border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); padding: 14px 18px; margin-bottom: 18px; }
.qm-loyalty-strip i { color: var(--qm-primary); }
.qm-loyalty-strip .qm-loyalty-strip-bar { flex: 1 1 180px; height: 8px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; min-width: 120px; }
.qm-loyalty-strip .qm-loyalty-strip-bar > span { display: block; height: 100%; background: var(--qm-primary); border-radius: var(--r-pill); }
/* Per-reward give-back strips (Settings -> Loyalty rules): every bar names its
reward and shows the % the bar encodes — an unlabeled meter reads as decoration. */
.qm-loyalty-strip-name { flex: 0 1 auto; min-width: 160px; font-weight: 700; font-size: var(--fs-400); color: var(--ink-800); }
.qm-loyalty-strip-name small { font-size: 12px; font-weight: 600; color: var(--ink-500); margin-inline-start: 6px; }
.qm-loyalty-strip-pct { font-weight: 800; color: var(--qm-primary); font-size: var(--fs-400); min-width: 44px; text-align: end; }
/* Restaurant card "Rewards" badge (sits under the deal badge). Reuses the
same brand-primary tint/border already established for loyalty elsewhere
(.qm-points-pill, .qm-loyalty-strip) rather than an unrelated colour - and a
rounded-rectangle instead of a pill so it doesn't read as a third copy of
the deal/featured shape. */
.qm-rest-loyalty { display: inline-block; background: var(--qm-primary-50); color: var(--qm-primary-600); border: 1px solid var(--qm-primary-100); font-size: 11px; font-weight: 700; line-height: 1; border-radius: var(--r-sm); padding: 5px 11px; }
/* Kitchen pipeline reward badge. */
.qm-kds-loyalty { margin-top: 6px; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--warn); background: var(--warn-bg); border: 1px solid color-mix(in srgb, var(--warn) 28%, transparent); border-radius: var(--r-pill); padding: 4px 10px; }
/* Order-track "you earned N points" callout. */
.qm-loyalty-earned { display: flex; align-items: center; gap: 10px; background: linear-gradient(180deg, #fff, var(--qm-primary-50)); border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); padding: 12px 16px; margin: 14px 0; font-size: 14px; }
.qm-loyalty-earned i { color: var(--qm-primary); font-size: var(--fs-800); }
.qm-loyalty-earned a { color: var(--qm-primary-600); font-weight: 700; }

/* ============================================================================
Advertising (ad cards, disclosure label, listing banner, placement map)
============================================================================ */
.qm-ad-card { position: relative; }
.qm-ad-label { position: absolute; top: 10px; left: 10px; z-index: 2; background: rgba(17, 17, 17, .72); color: #fff; font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; border-radius: 5px; padding: 3px 7px; }
/* Full-width listing-page ad banner (tag/cuisine pages). */
.qm-ad-banner { position: relative; display: flex; align-items: stretch; gap: 0; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; margin-bottom: 18px; box-shadow: var(--e-1); background: var(--surface); }
.qm-ad-banner-media { flex: 0 0 200px; min-height: 120px; overflow: hidden; background: var(--line-2); }
.qm-ad-banner-media img, .qm-ad-banner-media video { width: 100%; height: 100%; object-fit: cover; }
.qm-ad-banner-body { flex: 1 1 auto; padding: 16px 20px; display: flex; flex-direction: column; justify-content: center; gap: 6px; }
.qm-ad-banner-body :is(h1,h2,h3,h4,h5,h6) { margin: 0; font-size: var(--fs-800); }
.qm-ad-banner-body p { margin: 0; color: var(--ink-500); font-size: 14px; }
@media (max-width: 575.98px) { .qm-ad-banner { flex-direction: column; } .qm-ad-banner-media { flex-basis: auto; aspect-ratio: 16/9; } }
/* Hero ad slide reuses .qm-hero-promo; only the label is added. */
/* Owner placement map — a small CSS wireframe of where each ad lands. */
.qm-ad-map { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); padding: 12px; }
.qm-ad-map-screen { border: 1px solid var(--line); border-radius: 8px; background: #fff; overflow: hidden; }
.qm-ad-map-bar { height: 16px; background: linear-gradient(90deg, var(--qm-primary), var(--qm-primary-600)); }
.qm-ad-map-body { display: flex; gap: 6px; padding: 8px; }
.qm-ad-map-col { display: flex; flex-direction: column; gap: 6px; }
.qm-ad-map-zone { border-radius: 6px; background: var(--line-2); border: 1.5px dashed transparent; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 700; color: var(--ink-400); text-align: center; padding: 4px; transition: all var(--t-fast); }
.qm-ad-map-hero { height: 56px; }
.qm-ad-map-box { height: 44px; }
.qm-ad-map-tall { height: 94px; width: 56px; }
.qm-ad-map-tile { height: 44px; }
.qm-ad-map-list { height: 34px; }
.qm-ad-map-video { height: 30px; }
.qm-ad-map-card { height: 30px; }
.qm-ad-map-zone.is-target { background: var(--qm-primary-50); border-color: var(--qm-primary); color: var(--qm-primary-600); box-shadow: 0 0 0 2px var(--qm-primary-100) inset; }
/* ONE ZONE SPEAKS AT A TIME. Every zone drew its own name at full strength, so seven labels
   competed across two page mock-ups and the map read as noise — the one thing it exists to answer,
   "where does the placement I picked sit", was the hardest thing to see. The others stay as blocks
   so the page shape is still legible; only the selected one is named. */
.qm-ad-map-zone { color: transparent; }
.qm-ad-map-zone.is-target { color: var(--qm-primary-600); }
.qm-ad-map-screen:hover .qm-ad-map-zone { color: var(--ink-400); }
.qm-ad-map-screen:hover .qm-ad-map-zone.is-target { color: var(--qm-primary-600); }
.qm-ad-map-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; flex: 1 1 auto; }

/* ============================================================================
Utility sidebars
LEFT: Tags-only, colorful gradient tiles, pink panel, PUSHES the body on expand.
RIGHT: keep the overlay, fill the height (no scroll), dark image background.
============================================================================ */
/* --- Left: colorful Tags accordion --- */
.qm-side { background: linear-gradient(180deg, #fff8fb 0%, #fff0f5 46%, #ffeaf1 100%); border-right: 1px solid color-mix(in srgb, var(--qm-primary) 18%, transparent); transition: transform var(--t-slow), width var(--t-slow), background var(--t-fast), visibility var(--t-slow); }
.qm-side-head { background: transparent; }
.qm-side-tag .qm-side-ico { background: linear-gradient(135deg, var(--cc, var(--qm-primary)), color-mix(in srgb, var(--cc, #ff003d) 60%, #000)); color: #fff; box-shadow: 0 4px 10px color-mix(in srgb, var(--cc, #ff003d) 32%, transparent); transition: transform var(--t-fast); }
.qm-side-tag .qm-side-ico i { color: #fff; }
.qm-side-tag:hover .qm-side-ico { transform: scale(1.07); }
.qm-side-tag:hover { background: color-mix(in srgb, var(--cc, #ff003d) 9%, #fff); }
@media (min-width: 992px) {
  /* Docked push: NO backdrop/dim — the page stays bright and
  interactive; expanding just widens the left gutter and pushes the body right. */
  #qm-side-backdrop { display: none; }
  body.index-page:has(#qm-side) { transition: padding-left var(--t-slow); }
  body.index-page:has(#qm-side.open) { padding-left: 290px; }
}
/* Suppress the width/push animation while restoring the persisted state on load. */
body.qm-side-noanim, body.qm-side-noanim .qm-side { transition: none !important; }

/* ===== Header polish (desktop ≥1600px — this bar's content, including the
Cuisines/Language pills and full account name, genuinely needs that much room) ===== */
@media (min-width: 1600px) {
  .qm-nav-logo img { max-height: 36px; width: auto; }
  .qm-loc-btn { max-width: none; padding: 8px 12px; border-radius: var(--r-pill); border: 0; background: transparent; font-weight: 700; box-shadow: none; }
  .qm-loc-btn > i.fa-map-marker-alt { width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg,var(--qm-primary),var(--qm-primary-600)); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; }
  .qm-nav-actions { gap: 4px; padding: 4px 6px; border-radius: var(--r-pill); border: 1px solid color-mix(in srgb, var(--qm-primary) 16%, transparent); background: linear-gradient(180deg,#fff 0%,#fff4f8 100%); box-shadow: inset 0 1px 0 rgba(255,255,255,.72); }
  .qm-nav-actions .qm-icon-pill { background: transparent; border-color: transparent; }
  .qm-nav-actions .qm-icon-pill:hover { background: #fff; border-color: color-mix(in srgb, var(--qm-primary) 20%, transparent); color: var(--qm-primary); }
  .qm-nav-actions .qm-account-btn { padding: 6px 10px; border-radius: var(--r-pill); transition: background var(--t-fast); }
  .qm-nav-actions .qm-account-btn:hover { background: #fff; }
  .qm-nav-actions .qm-cuis-toggle { background: linear-gradient(135deg,var(--qm-primary) 0%,var(--qm-primary-600) 100%); color: #fff; border-color: transparent; padding: 9px 16px; box-shadow: 0 8px 16px color-mix(in srgb, var(--qm-primary) 26%, transparent); }
  .qm-nav-actions .qm-cuis-toggle:hover { color: #fff; transform: translateY(-1px); box-shadow: 0 10px 18px color-mix(in srgb, var(--qm-primary) 32%, transparent); }
}

/* ===== "Browse by food type" strip — faithful port of the template's svg-foodtype-strip ===== */
.svg-foodtype-strip { position: relative; z-index: 4; padding: 14px 0 12px; background: linear-gradient(180deg, #fffafc 0%, #fff2f6 100%); border-top: 1px solid color-mix(in srgb, var(--qm-primary) 14%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--qm-primary) 18%, transparent); }
.svg-foodtype-strip .svg-foodtype-shell { border: 1px solid color-mix(in srgb, var(--qm-primary) 22%, transparent); border-radius: 16px; padding: 11px 12px 13px; background: linear-gradient(180deg, #ffffff 0%, #fff7fa 100%); box-shadow: 0 10px 22px rgba(69,9,25,.08); }
.svg-foodtype-strip .svg-foodtype-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.svg-foodtype-strip .svg-foodtype-head span { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: #9a5a6c; }
.svg-foodtype-strip .svg-foodtype-carousel { display: grid; grid-template-columns: 42px minmax(0,1fr) 42px; align-items: stretch; gap: 10px; }
.svg-foodtype-strip .svg-foodtype-nav { border: 1px solid color-mix(in srgb, var(--qm-primary) 28%, transparent); border-radius: 12px; background: linear-gradient(180deg, #fff8fb 0%, #ffe9f1 100%); color: var(--qm-primary-600); min-height: 84px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease, opacity .2s ease; }
.svg-foodtype-strip .svg-foodtype-nav i { font-size: 14px; line-height: 1; }
.svg-foodtype-strip .svg-foodtype-nav:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--qm-primary) 50%, transparent); box-shadow: 0 8px 16px color-mix(in srgb, var(--qm-primary) 14%, transparent); }
.svg-foodtype-strip .svg-foodtype-nav:disabled { cursor: not-allowed; opacity: .4; transform: none; box-shadow: none; }
.svg-foodtype-strip .svg-foodtype-track { display: flex; align-items: stretch; gap: 10px; overflow-x: auto; overflow-y: hidden; padding: 3px 1px 5px; min-width: 0; scroll-behavior: smooth; scrollbar-width: none; -ms-overflow-style: none; touch-action: pan-x; -webkit-overflow-scrolling: touch; }
.svg-foodtype-strip .svg-foodtype-track::-webkit-scrollbar { display: none; }
.svg-foodtype-strip .svg-foodtype-item { position: relative; flex: 0 0 92px; min-height: 84px; padding: 14px 8px 10px; border-radius: 14px; border: 1px solid color-mix(in srgb, var(--qm-primary) 20%, transparent); background: linear-gradient(180deg, #fff 0%, #ffeff4 100%); display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px; text-decoration: none; text-align: center; transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+1) { background: linear-gradient(180deg, #ffffff 0%, #eefaf1 100%); border-color: rgba(74,161,16,.24); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+2) { background: linear-gradient(180deg, #ffffff 0%, #fff4e9 100%); border-color: rgba(251,97,0,.22); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+3) { background: linear-gradient(180deg, #ffffff 0%, #eef7ff 100%); border-color: rgba(10,94,196,.2); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+4) { background: linear-gradient(180deg, #ffffff 0%, #fff2f7 100%); border-color: color-mix(in srgb, var(--qm-primary) 22%, transparent); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+5) { background: linear-gradient(180deg, #ffffff 0%, #fff7ed 100%); border-color: rgba(255,125,0,.24); }
.svg-foodtype-strip .svg-foodtype-item .svg-foodtype-count { position: absolute; top: 5px; right: 5px; min-width: 24px; padding: 2px 6px; border-radius: var(--r-pill); border: 1px solid rgba(255,255,255,.68); background: var(--qm-grad); color: #fff; font-size: 9px; font-weight: 800; line-height: 1; letter-spacing: .01em; box-shadow: 0 6px 12px color-mix(in srgb, var(--qm-primary) 24%, transparent); text-align: center; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+1) .svg-foodtype-count { background: linear-gradient(135deg, #49a514 0%, #2f7d0e 100%); box-shadow: 0 6px 12px rgba(45,122,14,.3); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+2) .svg-foodtype-count { background: linear-gradient(135deg, #fb6100 0%, #ed7d31 100%); box-shadow: 0 6px 12px rgba(197,90,17,.3); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+3) .svg-foodtype-count { background: linear-gradient(135deg, #0a87d9 0%, #0a5dc4 100%); box-shadow: 0 6px 12px rgba(10,94,196,.28); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+4) .svg-foodtype-count { background: var(--qm-grad); box-shadow: 0 6px 12px color-mix(in srgb, var(--qm-primary) 24%, transparent); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+5) .svg-foodtype-count { background: linear-gradient(135deg, #ff9c00 0%, #ff6a00 100%); box-shadow: 0 6px 12px rgba(204,96,0,.29); }
.svg-foodtype-strip .svg-foodtype-item img { width: 25px; height: 25px; object-fit: contain; }
.svg-foodtype-strip .svg-foodtype-item i { font-size: 23px; line-height: 1; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+1) i { color: #2f7d0e; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+2) i { color: #d56100; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+3) i { color: #0a5dc4; }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+4) i { color: var(--qm-primary-600); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+5) i { color: #e06b00; }
.svg-foodtype-strip .svg-foodtype-item .svg-foodtype-title { display: block; color: #351a24; font-size: 11px; font-weight: 700; line-height: 1.2; white-space: nowrap; }
.svg-foodtype-strip .svg-foodtype-item.svg-foodtype-item-fill { flex: 0 0 156px; min-width: 156px; }
.svg-foodtype-strip .svg-foodtype-item.svg-foodtype-item-wide .svg-foodtype-title { white-space: normal; line-height: 1.15; }
.svg-foodtype-strip .svg-foodtype-item:hover, .svg-foodtype-strip .svg-foodtype-item:focus { transform: translateY(-2px); border-color: color-mix(in srgb, var(--qm-primary) 50%, transparent); box-shadow: 0 10px 18px color-mix(in srgb, var(--qm-primary) 16%, transparent); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+1):hover, .svg-foodtype-strip .svg-foodtype-item:nth-child(5n+1):focus { border-color: rgba(74,161,16,.48); box-shadow: 0 10px 18px rgba(74,161,16,.16); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+2):hover, .svg-foodtype-strip .svg-foodtype-item:nth-child(5n+2):focus { border-color: rgba(251,97,0,.48); box-shadow: 0 10px 18px rgba(251,97,0,.16); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+3):hover, .svg-foodtype-strip .svg-foodtype-item:nth-child(5n+3):focus { border-color: rgba(10,94,196,.46); box-shadow: 0 10px 18px rgba(10,94,196,.16); }
.svg-foodtype-strip .svg-foodtype-item:nth-child(5n+5):hover, .svg-foodtype-strip .svg-foodtype-item:nth-child(5n+5):focus { border-color: rgba(255,125,0,.48); box-shadow: 0 10px 18px rgba(255,125,0,.16); }
@media (max-width: 575.98px) { .svg-foodtype-strip .svg-foodtype-carousel { grid-template-columns: 34px minmax(0,1fr) 34px; gap: 8px; } }
/* Fun 3D category icons (Fluent Emoji 3D) — bigger than the glyphs, soft shadow for pop */
.svg-foodtype-strip .svg-foodtype-item img.svg-cat-3d { width: 44px; height: 44px; object-fit: contain; margin: -2px 0 1px; filter: drop-shadow(0 3px 5px rgba(78,8,29,.18)); transition: transform .2s ease; }
.svg-foodtype-strip .svg-foodtype-item:hover img.svg-cat-3d { transform: translateY(-1px) scale(1.06); }

/* ===== P12-D: LEFT sidebar — template home5-sidepanel look (white-card links + rounded-square gradient icons) ===== */
.qm-side-nav .qm-side-tag { background: #fff; border: 1px solid color-mix(in srgb, var(--qm-primary) 16%, transparent); border-radius: 12px; padding: 8px 10px; min-height: 48px; margin-bottom: 8px; gap: 10px; color: #2d1a22; }
.qm-side-nav .qm-side-tag:hover, .qm-side-nav .qm-side-tag:focus { transform: translateX(2px); border-color: color-mix(in srgb, var(--qm-primary) 38%, transparent); box-shadow: 0 10px 18px rgba(74,10,29,.12); background: #fff; color: #2d1a22; }
.qm-side-tag .qm-side-ico { width: 32px; height: 32px; min-width: 32px; border-radius: 10px; box-shadow: 0 8px 14px color-mix(in srgb, var(--cc, #ff003d) 26%, transparent); }
.qm-side-tag .qm-side-label { font-size: var(--fs-400); font-weight: 700; color: #2d1a22; }
.qm-side-tag .qm-side-count { background: color-mix(in srgb, var(--cc, #ff003d) 14%, #fff); color: color-mix(in srgb, var(--cc, #ff003d) 64%, #000); font-weight: 800; }
/* collapsed rail: keep just the gradient tile, drop the card chrome so the 76px rail stays clean */
.qm-side:not(.open) .qm-side-nav .qm-side-tag { background: transparent; border-color: transparent; box-shadow: none; margin-bottom: 6px; padding: 7px; min-height: 0; }
.qm-side:not(.open) .qm-side-nav .qm-side-tag:hover { transform: none; }

/* ===== P12-E (rev): RIGHT Cuisines panel — template cuisines side panel: dense single-column cards + distributed alpha rail ===== */
.qm-cuis-panel .qm-cuis-body { overflow-y: auto; }
.qm-cuis-panel .qm-cuis-grid { grid-template-columns: 1fr; grid-auto-rows: minmax(48px,auto); align-content: start; gap: 8px; height: auto; }
.qm-cuis-panel .qm-cuis-card { flex-direction: row; align-items: center; justify-content: flex-start; text-align: left; gap: 9px; padding: 8px 10px; min-height: 0; }
.qm-cuis-panel .qm-cuis-card:last-child:nth-child(odd) { grid-column: 1 / -1; }
.qm-cuis-panel .qm-cuis-card-ico.qm-cuis-ico { width: 30px; height: 30px; min-width: 30px; border-radius: 9px; }
.qm-cuis-panel .qm-cuis-name { text-align: left; font-size: 11px; line-height: 1.15; flex: 0 1 auto; }
.qm-cuis-panel .qm-cuis-card .qm-cuis-count { position: static; top: auto; right: auto; margin-left: auto; flex: 0 0 auto; }
/* distribute the A-Z rail down the full panel height instead of bunching it at the top */
.qm-cuis-panel .qm-alpha-rail { justify-content: space-between; gap: 0; padding-top: 8px; padding-bottom: 8px; overflow: visible; }
.qm-cuis-panel .qm-alpha { width: 20px; height: 18px; }

/* --- Right: Cuisines panel — image background, fill the height, no vertical scroll --- */
.qm-cuis-panel { color: #fff; background: linear-gradient(160deg, rgba(44,8,20,.90) 0%, color-mix(in srgb, var(--qm-primary-700) 82%, transparent) 52%, rgba(30,7,18,.92) 100%), var(--qm-cuis-bg, url('../img/promos/collection-2.webp')); background-size: cover; background-position: center; }
.qm-cuis-panel .qm-cuis-head { border-bottom-color: rgba(255,255,255,.16); }
.qm-cuis-panel .qm-cuis-head :is(h1,h2,h3,h4,h5,h6) { color: #fff; }
.qm-cuis-panel .qm-cuis-sub { color: rgba(255,255,255,.72); }
.qm-cuis-panel .qm-cuis-close { background: rgba(255,255,255,.14); color: #fff; }
.qm-cuis-panel .qm-cuis-close:hover { background: var(--qm-primary); }
.qm-cuis-panel .qm-cuis-search { border-bottom-color: rgba(255,255,255,.14); }
.qm-cuis-panel .qm-cuis-search i { color: rgba(255,255,255,.6); }
.qm-cuis-panel .qm-cuis-search input { background: rgba(255,255,255,.12); border-color: rgba(255,255,255,.22); color: #fff; }
.qm-cuis-panel .qm-cuis-search input::placeholder { color: rgba(255,255,255,.62); }
/* The 9 cards stretch to fill the panel height — no empty gap, no scroll. */
.qm-cuis-panel .qm-cuis-body { overflow-y: auto; }
.qm-cuis-panel .qm-cuis-grid { grid-template-columns: 1fr; grid-auto-rows: minmax(52px,auto); gap: 8px; align-content: start; height: auto; }
.qm-cuis-panel .qm-cuis-card { background: rgba(255,255,255,.10); border-color: rgba(255,255,255,.18); justify-content: flex-start; gap: 8px; min-height: 0; min-width: 0; padding: 7px 9px; }
.qm-cuis-panel .qm-cuis-name { white-space: normal; min-width: 0; line-height: 1.12; font-size: 11px; }
.qm-cuis-panel .qm-cuis-card-ico.qm-cuis-ico { width: 28px; height: 28px; min-width: 28px; }
.qm-cuis-panel .qm-cuis-count { font-size: 9px; padding: 1px 5px; }
.qm-cuis-panel .qm-cuis-card:hover { background: rgba(255,255,255,.18); border-color: rgba(255,255,255,.42); transform: translateY(-2px); }
.qm-cuis-panel .qm-cuis-name { color: #fff; }
.qm-cuis-panel .qm-cuis-card:last-child:nth-child(odd) { grid-column: 1 / -1; }
.qm-cuis-panel .qm-cuis-count { background: rgba(255,255,255,.20); color: #fff; }
.qm-cuis-panel .qm-cuis-card-ico.qm-cuis-ico { width: 44px; height: 44px; border-radius: 50%; background: linear-gradient(135deg, var(--cc, var(--qm-primary)), color-mix(in srgb, var(--cc, #ff003d) 60%, #000)); color: #fff; box-shadow: none; }
.qm-cuis-panel .qm-cuis-card-ico.qm-cuis-ico i { color: #fff; }
.qm-cuis-panel .qm-alpha-rail { border-left-color: rgba(255,255,255,.14); }
.qm-cuis-panel .qm-alpha { color: rgba(255,255,255,.92); }
.qm-cuis-panel .qm-alpha:hover { background: rgba(255,255,255,.24); color: #fff; }
.qm-cuis-panel .qm-alpha.is-off { color: rgba(255,255,255,.3); }

/* ============================================================================
Shared components (single H1, real stars, FAQ accordion,
blog cards, utilities, mobile resets).
========================================================================== */

/* ---- Single page H1 (every page has exactly one <h1 class="qm-page-title">) ---- */
/* When inside the red .qm-page-head band it inherits white; standalone it is ink. */
.qm-page-title { font-size: clamp(26px, 3.4vw, 38px); font-weight: 800; letter-spacing: -.02em; line-height: 1.1; margin: 0 0 var(--sp-3); color: var(--ink-900); }
.qm-page-head .qm-page-title { color: #fff; margin: 0 0 var(--sp-3); }
/* Visually-hidden H1 helper (for pages whose visible title is a hero/heading elsewhere). */
.qm-h1-sr { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* ---- Star rating (shared partial cards/stars.php renders this) ---- */
/* Wrapper is aria-hidden; an sr-only "X out of 5" sits beside it. Full = --star,
empty = --star-empty, half uses fa-star-half-alt in --star. */
.qm-stars { display: inline-flex; align-items: center; gap: 2px; color: var(--star); font-size: 12px; line-height: 1; }
.qm-stars .far, .qm-stars .fa-star-o { color: var(--star-empty); }
.qm-stars.qm-stars-lg { font-size: var(--fs-700); gap: 2px; }
.qm-stars.qm-stars-sm { font-size: 11px; }

/* ---- FAQ accordion (.qm-faq) — reusable component mirroring /help, vanilla JS,
no Bootstrap-collapse dependency (button[aria-expanded] + region[hidden]). ---- */
.qm-faq { display: flex; flex-direction: column; gap: var(--sp-3); }
.qm-faq-cat { margin-bottom: var(--sp-5); }
.qm-faq-cat-title { font-size: var(--fs-400); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--qm-primary-600); margin: 0 0 var(--sp-3); }
.qm-faq-item { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface); transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.qm-faq-item:hover { border-color: var(--qm-primary-100); }
.qm-faq-item.is-open { border-color: var(--qm-primary-100); box-shadow: var(--e-1); }
.qm-faq-q { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); width: 100%; text-align: left; background: var(--surface); border: 0; cursor: pointer; padding: var(--sp-4) var(--sp-5); font-size: var(--fs-600); font-weight: 600; color: var(--ink-900); line-height: 1.5; }
.qm-faq-q:hover { color: var(--qm-primary); }
.qm-faq-item.is-open .qm-faq-q { color: var(--qm-primary); background: var(--qm-primary-50); }
.qm-faq-q:focus-visible { outline: 3px solid color-mix(in srgb, var(--qm-primary) 45%, transparent); outline-offset: -3px; }
/* CSS chevron (no icon dependency) */
.qm-faq-q::after { content: ""; flex: 0 0 auto; width: 9px; height: 9px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(45deg); transition: transform var(--t); margin-right: 4px; }
.qm-faq-item.is-open .qm-faq-q::after { transform: rotate(-135deg); }
.qm-faq-a { color: var(--ink-600); font-size: var(--fs-600, 15px); line-height: 1.7; padding: 0 var(--sp-5) var(--sp-4); }
.qm-faq-a > :first-child { margin-top: 0; }
.qm-faq-a > :last-child { margin-bottom: 0; }
.qm-faq-a[hidden] { display: none; }
/* FAQ search box + "no results" + CTA (mirrors /help). */
.qm-faq-search { position: relative; max-width: 620px; margin: 0 auto var(--sp-6); display: flex; align-items: center; gap: var(--sp-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-2); box-shadow: var(--e-2); }
.qm-faq-search > i { position: absolute; left: calc(var(--sp-4) + 2px); color: var(--ink-400); pointer-events: none; }
.qm-faq-search .form-control { border: 0; box-shadow: none; padding-left: var(--sp-8); background: transparent; }
.qm-faq-search .form-control:focus { box-shadow: none; }
.qm-faq-search .qm-btn { flex: 0 0 auto; }
.qm-faq-noresult { color: var(--ink-500); font-size: 14px; }

/* ---- Blog card (.qm-blog-card) — article-appropriate (NOT the restaurant card) ---- */
.qm-blog-card { position: relative; display: flex; flex-direction: column; height: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t); }
.qm-blog-card:hover { transform: translateY(-5px); box-shadow: var(--e-3); }
.qm-blog-card-media { display: block; position: relative; aspect-ratio: 16/9; overflow: hidden; background: var(--line-2); }
.qm-blog-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-blog-card:hover .qm-blog-card-media img { transform: scale(1.05); }
.qm-blog-card-cat { position: absolute; top: var(--sp-3); left: var(--sp-3); background: var(--qm-primary-600); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 4px 11px; border-radius: var(--r-pill); text-decoration: none; }
.qm-blog-card-cat:hover { background: var(--qm-primary-600); color: #fff; }
.qm-blog-card-body { display: flex; flex-direction: column; flex: 1; padding: var(--sp-4) var(--sp-5) var(--sp-5); }
.qm-blog-card-title { font-size: 17px; font-weight: 700; line-height: 1.35; margin: 0 0 var(--sp-2); color: var(--ink-900); }
.qm-blog-card-title a { color: inherit; text-decoration: none; }
.qm-blog-card-title a:hover { color: var(--qm-primary); }
.qm-blog-card-excerpt { font-size: 14px; color: var(--ink-500); line-height: 1.6; margin: 0 0 var(--sp-4); flex: 1; }
.qm-blog-card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); font-size: 12px; color: var(--ink-400); margin-bottom: var(--sp-3); }
.qm-blog-card-meta span { display: inline-flex; align-items: center; gap: 5px; }
.qm-blog-card-meta i { color: var(--qm-primary-600); }
.qm-blog-card-more { align-self: flex-start; font-size: var(--fs-400, 13px); font-weight: 700; color: var(--qm-primary-600); text-decoration: none; display: inline-flex; align-items: center; gap: 6px; transition: gap var(--t-fast); }
.qm-blog-card-more:hover { color: var(--qm-primary-700); gap: 9px; }

/* ---- Global base helpers ---- */
html { overflow-x: hidden; }
.full-width { width: 100%; }
.padding-10 { padding: 10px; }
/* Accessible skip-to-content link (used by all three layouts). */
.skip-link { position: absolute; top: -100%; left: 0; background: var(--ink-900); color: #fff; padding: 12px 24px; z-index: 99999; font-size: var(--fs-600); font-weight: 700; text-decoration: none; border-radius: 0 0 var(--r-sm) 0; transition: top .2s ease; }
.skip-link:focus, .skip-link:focus-visible { top: 0; color: #fff; outline: 2px solid #fff; outline-offset: 2px; }

/* ============================================================================
MOBILE RESETS (≤991.98px) — sticky→static, cart table reflow, 44px tap targets
========================================================================== */
@media (max-width: 991.98px) {
  /* Sticky side rails become static so they don't eat vertical space on phones. */
  .qm-filter-card, .qm-cart-side, .qm-sticky-top { position: static; top: auto; }
  /* Quantity steppers reach the 44px touch-target minimum. */
  .qm-cart-qty button, .qm-qty-form button { width: 44px; height: 44px; font-size: var(--fs-700); }
}
@media (max-width: 767.98px) {
  /* Cart line-items table reflows to stacked cards (no 5-col overflow on phones). */
  .qm-cart-table, .qm-cart-table tbody, .qm-cart-table tr, .qm-cart-table td { display: block; width: 100%; }
  .qm-cart-table tr { position: relative; border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3); margin-bottom: var(--sp-3); display: grid; grid-template-columns: 62px 1fr; gap: var(--sp-2) var(--sp-3); align-items: center; }
  .qm-cart-table td { border: 0; padding: 0; }
  .qm-cart-table .qm-cart-td-img { grid-row: 1 / span 2; }
  .qm-cart-table .qm-cart-td-total { text-align: left; }
  .qm-cart-table .qm-cart-td-remove { position: absolute; top: var(--sp-2); right: var(--sp-2); }
}


/* ============================================================================
Per-page component styles (consolidated here from the individual views).
Tokens only (brand red = var(--qm-primary); success/met = var(--rating-great));
no rgba(255,0,61,x); no off-grid hardcodes where a --sp-N or radius token fits.
========================================================================== */

/* ====================== HOME ====================== */

/* ---- Hero slider (was inline in hero-slider.php) ---- */
.qm-heroslider { position: relative; }
.qm-heroslider-track { position: relative; }
.qm-heroslide { display: none; animation: qm-hero-fade .5s ease both; }
.qm-heroslide.is-active { display: block; }
@keyframes qm-hero-fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
/* Slide headlines share the .qm-hero h1 ramp (declared with it, near the hero). */
.qm-hero-promo .qm-hero-cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: 6px; }
.qm-hero-promo-a { background: var(--tint); }
.qm-hero-promo-b { background: linear-gradient(135deg, #f3f8ff 0%, var(--surface-2) 55%, var(--surface) 100%); }
.qm-hero-promo-c { background: var(--tint); }

/* ---- Hero slider controls / dots (was inline) ---- */
.qm-heroslider-btn { position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--e-2); transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast); }
.qm-heroslider-btn:hover { background: var(--qm-primary); color: #fff; border-color: var(--qm-primary); }
.qm-heroslider-btn.prev { left: -6px; }
.qm-heroslider-btn.next { right: -6px; }
.qm-heroslider-dots { display: flex; justify-content: center; gap: 9px; margin-top: var(--sp-4); }
.qm-heroslider-dot { width: 10px; height: 10px; padding: 0; border-radius: 50%; border: 0; background: var(--line-2); cursor: pointer; transition: background var(--t-fast), width var(--t-fast); }
.qm-heroslider-dot:hover { background: var(--qm-primary-100); }
.qm-heroslider-dot.is-active { width: 26px; border-radius: var(--r-pill); background: var(--qm-primary); }
@media (max-width: 991.98px) {
  .qm-heroslider-btn { width: 38px; height: 38px; }
  .qm-heroslider-btn.prev { left: 2px; }
  .qm-heroslider-btn.next { right: 2px; }
}
@media (prefers-reduced-motion: reduce) { .qm-heroslide { animation: none; } }

/* ---- Hero drag teaser (qm-hx-*), was inline ---- */
.qm-hx-art { display: flex; align-items: center; justify-content: center; aspect-ratio: 4/3; width: 100%; }
.qm-hx { width: 100%; max-width: 560px; display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }
.qm-hx-menu, .qm-hx-plate { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-2); padding: var(--sp-4); }
.qm-hx-label { display: block; font-size: .8rem; font-weight: 600; color: var(--ink-500); margin-bottom: var(--sp-3); line-height: 1.3; }
.qm-hx-chips { display: grid; gap: 9px; }
.qm-hx-chip { display: flex; align-items: center; gap: 9px; padding: var(--sp-2); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--card-bg); cursor: grab; touch-action: none; user-select: none; -webkit-user-select: none; transition: transform .15s, box-shadow .15s, border-color .15s; }
.qm-hx-chip:hover, .qm-hx-chip:focus-visible { border-color: var(--qm-primary); box-shadow: var(--e-2); transform: translateY(-2px); outline: none; }
.qm-hx-chip.is-dragging { opacity: .4; }
.qm-hx-chip img { width: 42px; height: 42px; border-radius: 8px; object-fit: cover; flex: 0 0 auto; }
.qm-hx-chip-name { font-size: .82rem; font-weight: 700; color: var(--ink-900); flex: 1; line-height: 1.1; }
.qm-hx-chip-price { font-size: .82rem; font-weight: 700; color: var(--qm-primary-600); }
.qm-hx-ghost { position: fixed; z-index: 2000; pointer-events: none; opacity: .96; box-shadow: var(--e-4); transform: rotate(-3deg); background: var(--surface); border: 1px solid var(--qm-primary); border-radius: var(--r-md); padding: 6px; display: flex; align-items: center; gap: var(--sp-2); }
.qm-hx-ghost img { width: 34px; height: 34px; border-radius: 7px; object-fit: cover; }
.qm-hx-ghost span { font-size: .76rem; font-weight: 700; color: var(--ink-900); }
.qm-hx-plate { display: flex; flex-direction: column; border-style: dashed; border-width: 2px; }
.qm-hx-plate.is-over { border-color: var(--qm-primary); background: var(--qm-primary-50); }
.qm-hx-plate-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: var(--ink-400); gap: 6px; font-size: .78rem; min-height: 120px; }
.qm-hx-plate-empty i { font-size: 1.6rem; color: var(--line-2); }
.qm-hx-plate-stack { flex: 1; display: flex; flex-wrap: wrap; align-content: flex-start; gap: 6px; min-height: 120px; padding-top: 2px; }
.qm-hx-plate-stack img { width: 36px; height: 36px; border-radius: 8px; object-fit: cover; box-shadow: var(--e-1); animation: qm-hx-pop .3s ease; }
@keyframes qm-hx-pop { 0% { transform: scale(.4); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
.qm-hx-foot { display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); margin-top: 10px; padding-top: 10px; font-size: .8rem; color: var(--ink-600); }
.qm-hx-foot strong { font-size: 1rem; color: var(--ink-900); }
@media (max-width: 560px) { .qm-hx { grid-template-columns: 1fr; } }

/* ---- Browse-by-cuisine cards (was inline in cuisine-carousel.php; !important removed) ---- */
.qm-cuisbrowse-track { grid-auto-columns: 176px; }
.qm-cuisbrowse.qm-carousel-static .qm-carousel-btn { display: inline-flex; }
.qm-cuisbrowse-card { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; padding: var(--sp-5) var(--sp-3); border-radius: var(--r-lg); border: 1px solid color-mix(in srgb, var(--cc, var(--qm-primary)) 20%, #fff); background: linear-gradient(180deg, #fff, color-mix(in srgb, var(--cc, var(--qm-primary)) 9%, #fff)); box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-cuisbrowse-card:hover { transform: translateY(-4px); box-shadow: var(--e-3); border-color: var(--cc, var(--qm-primary)); }
.qm-cuisbrowse-ico { width: 72px; height: 72px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: #fff; box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--cc, var(--qm-primary)) 22%, #fff), 0 6px 14px rgba(78,8,29,.12); }
.qm-cuisbrowse-ico img { width: 48px; height: 48px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0,0,0,.18)); }
.qm-cuisbrowse-ico i { font-size: 30px; color: var(--cc, var(--qm-primary)); }
.qm-cuisbrowse-name { font-weight: 800; font-size: 14px; color: var(--ink-900); }
.qm-cuisbrowse-count { font-size: 12px; color: var(--ink-500); font-weight: 600; }

/* ---- Unified cuisine card (Browse-by-cuisine carousel + /cuisines + find-location):
country flag in the centre circle, associated dish icons in the corners. ---- */
.qm-cuisine-card { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; padding: var(--sp-6) var(--sp-4) var(--sp-4); background: var(--card-bg); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); text-decoration: none; overflow: hidden; transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-cuisine-card:hover { transform: translateY(-4px); box-shadow: var(--e-3); border-color: var(--qm-primary-100); }
.qm-cuisine-card--carousel { width: 176px; }
.qm-cuisine-card--grid { width: 100%; }
.qm-cuisine-card-flag { position: relative; z-index: 1; width: 66px; height: 66px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--surface); box-shadow: 0 0 0 1px var(--line-2), 0 6px 14px rgba(78,8,29,.12); overflow: hidden; margin-bottom: var(--sp-3); transition: transform .35s cubic-bezier(.22,.68,.3,1.3), box-shadow var(--t); }
.qm-cuisine-card-flag-img, .qm-cuisine-card-flag img { width: 100%; height: 100%; object-fit: cover; }
.qm-cuisine-card-flag i { font-size: 28px; color: var(--qm-primary); }
.qm-cuisine-name { position: relative; z-index: 1; font-weight: 800; font-size: var(--fs-600); color: var(--ink-900); line-height: 1.2; }
.qm-cuisine-blurb { position: relative; z-index: 1; font-size: 12px; color: var(--ink-500); margin-top: 4px; line-height: 1.4; }
.qm-cuisine-count { position: relative; z-index: 1; font-size: 12px; color: var(--qm-primary); font-weight: 700; margin-top: 6px; }
.qm-cuisine-count.is-soon { color: var(--ink-400); }
.qm-cuisine-corners { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
/* Dish icons sit as a larger, low-opacity ambient backdrop at rest, then brighten,
grow and drift inward toward the centre flag on hover — declutters the card and
adds a lively, staggered hint of motion. */
.qm-cuisine-corner { position: absolute; width: 34px; height: 34px; object-fit: contain; opacity: .3; transform: scale(.82); filter: drop-shadow(0 2px 3px rgba(0,0,0,.12)) saturate(.85); transition: transform .42s cubic-bezier(.22,.68,.3,1.3), opacity .3s ease, filter .3s ease; }
.qm-cuisine-corner--tl { top: 10px; left: 10px; }
.qm-cuisine-corner--tr { top: 10px; right: 10px; }
.qm-cuisine-corner--bl { bottom: 10px; left: 10px; }
.qm-cuisine-corner--br { bottom: 10px; right: 10px; }
.qm-cuisine-card:hover .qm-cuisine-corner { opacity: .96; filter: drop-shadow(0 5px 8px rgba(0,0,0,.2)) saturate(1); }
.qm-cuisine-card:hover .qm-cuisine-corner--tl { transform: translate(14px, 14px) scale(1.08); transition-delay: 0s; }
.qm-cuisine-card:hover .qm-cuisine-corner--tr { transform: translate(-14px, 14px) scale(1.08); transition-delay: .04s; }
.qm-cuisine-card:hover .qm-cuisine-corner--bl { transform: translate(14px, -14px) scale(1.08); transition-delay: .08s; }
.qm-cuisine-card:hover .qm-cuisine-corner--br { transform: translate(-14px, -14px) scale(1.08); transition-delay: .12s; }
.qm-cuisine-card:hover .qm-cuisine-card-flag { transform: scale(1.06); box-shadow: 0 0 0 1px var(--qm-primary-100), 0 10px 22px rgba(78,8,29,.18); }
@media (prefers-reduced-motion: reduce) {
  .qm-cuisine-corner, .qm-cuisine-card-flag { transition: opacity .2s ease; }
  .qm-cuisine-card:hover .qm-cuisine-corner { transform: scale(.82); }
  .qm-cuisine-card:hover .qm-cuisine-card-flag { transform: none; }
}

/* ============================================================
Landing-page spotlight pieces — cuisine flag tile, shop card,
spotlight row, boxed-overlap "Browse by food type" (token-based)
============================================================ */
/* Spotlight row: feature card (left) + bare rail (right). min-width:0 lets the rail track
scroll instead of blowing out the flex grid. */
.qm-spotlight-rail { min-width: 0; }
.qm-spotlight-rail .qm-rail-head { margin-bottom: 12px; }
/* Contain the .row gutter's negative margin so a compact spotlight keeps the marketplace's
84px section rhythm (without this the gutter collapses through and tightens the gap to ~60). */
.qm-spotlight-sec { display: flow-root; }
/* Dense rail: wider product cards so exactly ~5 dish cards show at once on a wide desktop
(restaurant-spotlight + hungry product rails) — the rest scroll, with a sliver peeking. */
.qm-rail-dense .qm-rail-track.qm-carousel-track { grid-auto-columns: 256px; }

/* Cuisine flag tile — country flag as a full-bleed background + scrim */
.qm-cuisflag { position: relative; display: block; height: 100%; min-height: 260px; border-radius: var(--r-lg); overflow: hidden; text-decoration: none; color: #fff; background: var(--qm-grad); background-size: cover; background-position: center; box-shadow: var(--e-2); transition: transform var(--t), box-shadow var(--t); }
/* When the cuisine has a country flag, show it full-bleed. This higher-specificity
   rule is required because the base `background` shorthand above resets the flag that
   the shared .qm-bgimg utility sets. The gradient stays as a fallback layer beneath. */
.qm-cuisflag.qm-bgimg { background-image: var(--qm-bg), var(--qm-grad); }
.qm-cuisflag:hover { transform: translateY(-4px); box-shadow: var(--e-3); }
/* Bottom-weighted legibility scrim: transparent over the flag, darkening only toward
   the text at the bottom — keeps the flag clearly visible (no full-tile colour wash). */
.qm-cuisflag-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(31,11,18,0) 0%, rgba(31,11,18,.12) 42%, rgba(31,11,18,.82) 100%); }
.qm-cuisflag-body { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 5px; padding: var(--sp-5); height: 100%; justify-content: flex-end; }
.qm-cuisflag-eyebrow { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .08em; color: rgba(255,255,255,.85); }
.qm-cuisflag-name { font-size: 1.5rem; font-weight: 800; line-height: 1.1; text-shadow: 0 2px 10px rgba(0,0,0,.35); }
.qm-cuisflag-count { font-size: var(--fs-400); font-weight: 700; color: rgba(255,255,255,.92); }
.qm-cuisflag-dishes { display: flex; gap: 6px; margin-top: 6px; }
.qm-cuisflag-dish { width: 32px; height: 32px; object-fit: contain; background: rgba(255,255,255,.92); border-radius: 9px; padding: 3px; box-shadow: 0 2px 6px rgba(0,0,0,.25); }
.qm-cuisflag-cta { margin-top: 12px; font-size: var(--fs-400); font-weight: 800; display: inline-flex; align-items: center; gap: 6px; }
.qm-cuisflag-cta i { transition: transform var(--t-fast); }
.qm-cuisflag:hover .qm-cuisflag-cta i { transform: translateX(3px); }

/* Shop spotlight card */
.qm-shopspot { display: flex; flex-direction: column; height: 100%; min-height: 260px; background: var(--card-bg); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; text-decoration: none; box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-shopspot:hover { transform: translateY(-4px); box-shadow: var(--e-3); border-color: var(--qm-primary-100); }
.qm-shopspot-media { position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden; }
.qm-shopspot-media img { width: 100%; height: 100%; object-fit: cover; }
.qm-shopspot-badge { position: absolute; top: 10px; left: 10px; background: var(--qm-grad); color: #fff; font-size: 11px; font-weight: 800; padding: 3px 10px; border-radius: var(--r-pill); box-shadow: var(--e-primary); }
.qm-shopspot-body { display: flex; flex-direction: column; gap: 5px; padding: var(--sp-4); flex: 1 1 auto; }
.qm-shopspot-eyebrow { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--qm-primary-600); }
.qm-shopspot-name { font-size: 1.2rem; font-weight: 800; color: var(--ink-900); line-height: 1.15; }
.qm-shopspot-meta { font-size: 12px; color: var(--ink-500); }
.qm-shopspot-tags { display: flex; flex-wrap: wrap; gap: 5px; margin: 2px 0; }
.qm-shopspot-stats { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--ink-600); margin-top: auto; padding-top: 8px; }
.qm-shopspot-rate { font-weight: 700; color: var(--ink-800); }
.qm-shopspot-rate i { color: var(--gold); }
.qm-shopspot-stats small { color: var(--ink-400); }
.qm-shopspot-cta { margin-top: 10px; font-weight: 800; font-size: var(--fs-400); color: var(--qm-primary); display: inline-flex; align-items: center; gap: 6px; }
.qm-shopspot-cta i { transition: transform var(--t-fast); }
.qm-shopspot:hover .qm-shopspot-cta i { transform: translateX(3px); }

/* Boxed / overlap "Browse by food type" strip (cuisine page): floats over the hero's lower edge */
.svg-foodtype-strip.svg-foodtype-strip--overlap { position: relative; z-index: 6; margin-top: -106px; padding: 0; background: transparent; border: 0; }
.svg-foodtype-strip--overlap .svg-foodtype-shell { max-width: 1200px; margin: 0 auto; background: linear-gradient(180deg, #ffffff 0%, #fff4f8 100%); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-3); padding: 14px 16px; }
@media (max-width: 860px) { .svg-foodtype-strip.svg-foodtype-strip--overlap { margin-top: -48px; } }
@media (max-width: 700px) {
  .svg-foodtype-strip.svg-foodtype-strip--overlap { margin-top: -24px; }
  .svg-foodtype-strip--overlap .svg-foodtype-shell { padding: 10px 12px; }
}
/* Large overlap (restaurant-spotlight): the strip rides further up onto the taller hero's
lower edge, centred to the slider. Pull-up eases off as the hero shrinks on smaller screens. */
.svg-foodtype-strip.svg-foodtype-strip--overlap-lg { margin-top: -100px; }
@media (max-width: 1199.98px) { .svg-foodtype-strip.svg-foodtype-strip--overlap-lg { margin-top: -72px; } }
@media (max-width: 991.98px) { .svg-foodtype-strip.svg-foodtype-strip--overlap-lg { margin-top: -44px; } }
@media (max-width: 700px) { .svg-foodtype-strip.svg-foodtype-strip--overlap-lg { margin-top: -12px; } }
/* Restaurant-spotlight keeps the boxed (centred, max-width 1200) shell inherited from --overlap;
the hero info card is shifted right so its left edge lines up with this box (see .qm-herort-card). */

/* ============================================================
Per-page above-the-fold hero sliders (cuisine / restaurant /
hungry) — distinct designs sharing the .qm-heroslider shell
============================================================ */
/* --- Cuisine hero: flag-forward split (copy + food image) --- */
.qm-herocz { position: relative; display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: clamp(20px, 4vw, 48px); align-items: stretch; background: linear-gradient(135deg, var(--qm-primary-50) 0%, #fff 60%); border: 1px solid var(--line); border-radius: var(--r-xl); overflow: hidden; box-shadow: var(--e-1); min-height: 500px; }
.qm-herocz-copy { display: flex; flex-direction: column; justify-content: center; gap: 14px; padding: clamp(28px, 4vw, 56px); }
.qm-herocz-eyebrow { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--qm-primary-600); }
.qm-herocz-flag { width: 26px; height: 18px; border-radius: 3px; object-fit: cover; box-shadow: 0 0 0 1px rgba(0,0,0,.08); }
.qm-herocz-title { font-size: clamp(2rem, 4vw, 3rem); font-weight: 800; line-height: 1.05; color: var(--ink-900); margin: 0; }
.qm-herocz-sub { font-size: 1.05rem; color: var(--ink-600); max-width: 460px; margin: 0; }
.qm-herocz-dishes { display: flex; gap: 10px; }
.qm-herocz-dishes img { width: 38px; height: 38px; object-fit: contain; filter: drop-shadow(0 3px 6px rgba(0,0,0,.18)); }
.qm-herocz-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; }
.qm-herocz-art { position: relative; background-size: cover; background-position: center; border-radius: var(--r-xl); margin: 14px 14px 14px 0; min-height: 300px; }
.qm-herocz-badge { position: absolute; top: 14px; right: 14px; background: rgba(255,255,255,.92); color: var(--ink-900); font-size: 12px; font-weight: 800; padding: 5px 12px; border-radius: var(--r-pill); box-shadow: var(--e-2); }
.qm-herocz-badge i { color: var(--qm-primary); }
@media (max-width: 860px) {
  .qm-herocz { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .qm-herocz-art { margin: 0 14px 14px; min-height: 200px; }
}

/* --- Restaurant hero: full-bleed cover + premium overlay card. Taller than the others so the
"Browse by food type" strip can ride up into its lower band (see --overlap-lg) without crowding
the info card, which is lifted clear of that band. --- */
.qm-herort { position: relative; display: block; min-height: 560px; border-radius: var(--r-xl); overflow: hidden; background-size: cover; background-position: center; text-decoration: none; box-shadow: var(--e-2); }
@media (max-width: 991.98px) { .qm-herort { min-height: 460px; } }
@media (max-width: 700px) { .qm-herort { min-height: 380px; } }
.qm-herort-scrim { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(20,8,12,.85) 0%, rgba(20,8,12,.5) 45%, rgba(20,8,12,.12) 100%); }
.qm-herort-ribbon { position: absolute; top: 18px; left: 18px; z-index: 1; display: inline-flex; align-items: center; gap: 6px; background: var(--qm-grad); color: #fff; font-size: 12px; font-weight: 800; padding: 6px 14px; border-radius: var(--r-pill); box-shadow: var(--e-primary); }
/* Left edge tracks the boxed (max-width 1200, centred) "Browse by food type" shell so the card's
elements line up with it; falls back to a 16px inset once the box goes full-width on narrow screens. */
.qm-herort-card { position: absolute; left: max(16px, calc((100% - 1200px) / 2 + 16px)); bottom: 130px; right: clamp(20px, 4vw, 48px); z-index: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 10px; max-width: 560px; color: #fff; }
@media (max-width: 1199.98px) { .qm-herort-card { bottom: 108px; } }
@media (max-width: 991.98px) { .qm-herort-card { bottom: 92px; } }
@media (max-width: 700px) { .qm-herort-card { bottom: clamp(20px, 4vw, 40px); } }
.qm-herort-eyebrow { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--gold); }
.qm-herort-name { font-size: clamp(2.3rem, 4.6vw, 3.5rem); font-weight: 800; line-height: 1.02; letter-spacing: -.01em; margin: 2px 0 0; text-shadow: 0 2px 18px rgba(0,0,0,.45); }
.qm-herort-meta { font-size: 1.05rem; font-weight: 500; color: rgba(255,255,255,.92); }
.qm-herort-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0; }
/* The hero sits on a photo, so its tags get a hairline ring and a drop shadow to stay
legible — but NOT a background of their own, which would override the shared .qm-tagc*
colour and make these the only food-type tags on the site that are not colour-coded. */
.qm-herort-tags .qm-rest-tag { box-shadow: 0 0 0 1px rgba(255, 255, 255, .38), 0 2px 8px rgba(0, 0, 0, .3); }
.qm-herort-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 18px; margin-top: 6px; font-size: var(--fs-600); font-weight: 700; }
.qm-herort-stat { display: inline-flex; align-items: center; gap: 7px; color: rgba(255,255,255,.92); }
.qm-herort-rate i { color: var(--gold); }
.qm-herort-go { align-self: flex-start; margin-top: 12px; display: inline-flex; align-items: center; gap: 9px; background: var(--qm-grad); color: #fff; padding: 13px 26px; border-radius: var(--r-pill); font-size: var(--fs-600); font-weight: 800; letter-spacing: .01em; box-shadow: var(--e-primary); transition: transform var(--t-fast), box-shadow var(--t-fast); }
.qm-herort-go i { transition: transform var(--t-fast); }
.qm-herort:hover .qm-herort-go { transform: translateY(-2px); box-shadow: 0 16px 32px color-mix(in srgb, var(--qm-primary) 42%, transparent); }
.qm-herort:hover .qm-herort-go i { transform: translateX(3px); }

/* --- Hungry hero: bold gradient + craving chips + dish collage --- */
.qm-herohg { position: relative; display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: clamp(20px, 4vw, 40px); align-items: center; min-height: 580px; border-radius: var(--r-xl); overflow: hidden; padding: clamp(28px, 4vw, 56px); background: radial-gradient(120% 140% at 0% 0%, var(--qm-primary) 0%, var(--qm-primary) 45%, var(--qm-primary-600) 100%); color: #fff; box-shadow: var(--e-2); }
.qm-herohg-eyebrow { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: rgba(255,255,255,.9); }
.qm-herohg-title { font-size: clamp(2.1rem, 4.5vw, 3.4rem); font-weight: 800; line-height: 1.03; margin: 10px 0 8px; }
.qm-herohg-title .qm-hl { color: #ffe08a; }
.qm-herohg-sub { font-size: 1.05rem; color: rgba(255,255,255,.92); max-width: 460px; margin: 0 0 16px; }
.qm-herohg-search { max-width: 460px; background: #fff; border-radius: var(--r-md); padding: 8px; display: flex; gap: 8px; box-shadow: var(--e-3); }
.qm-herohg-search input { flex: 1; min-width: 0; border: 0; outline: none; box-shadow: none; background: transparent; color: var(--ink-900); font-size: var(--fs-600); padding: 0 8px; }
.qm-herohg-search input::placeholder { color: var(--ink-400); }
.qm-herohg-search input:focus { box-shadow: none; }
.qm-herohg-search:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 40%, transparent), var(--e-3); }
.qm-herohg-search .qm-btn { background: var(--qm-grad); color: #fff; flex: 0 0 auto; }
.qm-herohg-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.qm-herohg-chip { background: rgba(255,255,255,.16); border: 1px solid rgba(255,255,255,.28); color: #fff; font-size: var(--fs-400); font-weight: 700; padding: 6px 14px; border-radius: var(--r-pill); text-decoration: none; transition: background var(--t-fast); }
.qm-herohg-chip:hover { background: rgba(255,255,255,.3); }
/* Each craving chip takes its food type's colour from tag_color_class(), the same
   source the restaurant cards and the restaurant detail page use, so Pizza reads the
   same here as it does on a card. Declared after the translucent base above so the
   per-tag fill wins. */
.qm-herohg-chip.qm-tagc1 { background: #c2410c; border-color: color-mix(in srgb, #c2410c 70%, #fff); }
.qm-herohg-chip.qm-tagc2 { background: var(--qm-primary-600); border-color: color-mix(in srgb, var(--qm-primary-600) 70%, #fff); }
.qm-herohg-chip.qm-tagc3 { background: #6d28d9; border-color: color-mix(in srgb, #6d28d9 70%, #fff); }
.qm-herohg-chip.qm-tagc4 { background: #1565c0; border-color: color-mix(in srgb, #1565c0 70%, #fff); }
.qm-herohg-chip.qm-tagc5 { background: #0f766e; border-color: color-mix(in srgb, #0f766e 70%, #fff); }
.qm-herohg-chip.qm-tagc6 { background: #a16207; border-color: color-mix(in srgb, #a16207 70%, #fff); }
.qm-herohg-chip.qm-tagc7 { background: #4338ca; border-color: color-mix(in srgb, #4338ca 70%, #fff); }
.qm-herohg-chip.qm-tagc8 { background: #15803d; border-color: color-mix(in srgb, #15803d 70%, #fff); }
.qm-herohg-chip.qm-tagc1:hover,.qm-herohg-chip.qm-tagc2:hover,.qm-herohg-chip.qm-tagc3:hover,.qm-herohg-chip.qm-tagc4:hover,
.qm-herohg-chip.qm-tagc5:hover,.qm-herohg-chip.qm-tagc6:hover,.qm-herohg-chip.qm-tagc7:hover,.qm-herohg-chip.qm-tagc8:hover { filter: brightness(1.12); }

.qm-herohg-art { position: relative; height: 100%; min-height: 300px; }
.qm-herohg-dish { position: absolute; border-radius: 50%; object-fit: cover; box-shadow: 0 14px 30px rgba(0,0,0,.3); border: 4px solid rgba(255,255,255,.92); }
.qm-herohg-dish.d0 { width: 180px; height: 180px; top: 8%; left: 26%; }
.qm-herohg-dish.d1 { width: 128px; height: 128px; top: 0; right: 6%; }
.qm-herohg-dish.d2 { width: 120px; height: 120px; bottom: 6%; left: 6%; }
.qm-herohg-dish.d3 { width: 150px; height: 150px; bottom: 0; right: 18%; }
.qm-herohg-dish.d4 { width: 92px; height: 92px; top: 42%; right: 0; }
/* Floating dish circles — gentle bob via the independent `translate` property so a hover
scale/rotate composes with it rather than fighting it; each circle drifts on its own rhythm. */
@keyframes qm-hg-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -14px; } }
.qm-herohg-dish { animation: qm-hg-float 6s ease-in-out infinite; transition: scale var(--t), rotate var(--t), box-shadow var(--t); }
.qm-herohg-dish.d0 { animation-duration: 7s; }
.qm-herohg-dish.d1 { animation-duration: 5.5s; animation-delay: -1.2s; }
.qm-herohg-dish.d2 { animation-duration: 6.5s; animation-delay: -.6s; }
.qm-herohg-dish.d3 { animation-duration: 5s; animation-delay: -2s; }
.qm-herohg-dish.d4 { animation-duration: 7.8s; animation-delay: -.4s; }
.qm-herohg-dish:hover { scale: 1.12; rotate: -3deg; box-shadow: 0 22px 44px rgba(0,0,0,.42); z-index: 3; }
@media (prefers-reduced-motion: reduce) { .qm-herohg-dish { animation: none; } }
@media (max-width: 860px) {
  /* minmax(0, 1fr) not a bare 1fr - see .qm-demo-grid above for the same
  fix: without it, the copy column's own content set its min-content as the
  track's floor and the whole hero card overflowed, silently clipped by its
  own overflow:hidden instead of the copy text wrapping/shrinking to fit. */
  .qm-herohg { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .qm-herohg-art { display: none; }
}

/* ---- Classic home (.qmc-*) (was inline in home-classic.php) ---- */
.qmc-hero-section { padding-top: var(--sp-7); }
.qmc-hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; gap: clamp(var(--sp-5), 4vw, var(--sp-8)); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-2); overflow: hidden; }
.qmc-hero-art { position: relative; min-height: 320px; }
.qmc-hero-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qmc-hero-dish { position: absolute; left: var(--sp-4); bottom: var(--sp-4); display: inline-flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-2); background: var(--surface); border-radius: var(--r-pill); box-shadow: var(--e-2); }
.qmc-hero-dish-ic { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: var(--qm-primary); color: #fff; flex: none; }
.qmc-hero-dish-txt { display: flex; flex-direction: column; line-height: 1.2; }
.qmc-hero-dish-txt strong { color: var(--ink-900); font-size: .9rem; }
.qmc-hero-dish-txt small { color: var(--ink-500); font-size: .78rem; }
.qmc-hero-copy { display: flex; flex-direction: column; justify-content: center; padding: clamp(var(--sp-5), 3vw, var(--sp-8)) clamp(var(--sp-4), 3vw, var(--sp-7)); padding-left: 0; }
/* Title and lede share the hero ramp declared beside .qm-hero (one definition). */
.qmc-hero-search { position: relative; display: flex; align-items: center; gap: var(--sp-2); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-pill); padding: var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-5); box-shadow: var(--e-1); margin-bottom: var(--sp-4); }
.qmc-hero-search-ic { color: var(--qm-primary); flex: none; }
.qmc-hero-search input { flex: 1 1 auto; border: 0; background: transparent; padding: var(--sp-2) var(--sp-1); font: inherit; color: var(--ink-900); min-width: 0; }
.qmc-hero-search input:focus { outline: none; }
.qmc-hero-search:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 40%, transparent); }
.qmc-hero-search .qm-btn { flex: none; white-space: nowrap; }
.qmc-perks { list-style: none; margin: 0 0 var(--sp-5); padding: 0; display: grid; gap: var(--sp-3); }
.qmc-perks li { display: flex; align-items: flex-start; gap: var(--sp-3); color: var(--ink-700); }
.qmc-perks li i { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: color-mix(in srgb, var(--qm-primary) 12%, transparent); color: var(--qm-primary); flex: none; margin-top: 1px; }
.qmc-perks li strong { color: var(--ink-900); }
.qmc-hero-cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
@media (max-width: 991.98px) {
  .qmc-hero { grid-template-columns: minmax(0, 1fr); }
  .qmc-hero-art { min-height: 240px; }
  .qmc-hero-copy { padding: clamp(var(--sp-4), 5vw, var(--sp-6)); padding-top: 0; }
}

/* ---- Collections layout helpers ---- */
.qm-coll-intro { margin-bottom: var(--sp-6); }
.qm-coll-row { margin-top: var(--sp-6); }
/* Editorial rail header (title + subtitle left, "See all" right) for Popular Restaurants. */
.qm-coll-rail-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: var(--sp-4); }
.qm-coll-rail-title { font-size: var(--fs-900); font-weight: 700; color: var(--ink-900); margin: 0; }
.qm-coll-rail-sub { font-size: var(--fs-400); color: var(--ink-500); margin: 4px 0 0; }
.qm-coll-rail-head .qm-btn { flex-shrink: 0; }
@media (max-width: 575.98px) {
  .qm-coll-rail-head { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ---- Sort-by-Category caption is now a <p>, not <h6> (keep the old h6 look) ---- */
.svg-foodtype-strip .svg-foodtype-head .svg-foodtype-head-title { margin: 0; font-size: var(--fs-600); font-weight: 700; color: var(--ink-900); letter-spacing: .01em; }

/* ---- Newsletter inline feedback region (used by newsletter.js) ---- */
.qm-newsletter-feedback { margin-top: var(--sp-3); font-size: 14px; font-weight: 600; }
.qm-newsletter-feedback.is-ok { color: var(--ok); }
.qm-newsletter-feedback.is-error { color: var(--qm-primary-600); }

/* ====================== DISCOVERY ====================== */

/* ---- Cuisines A-Z grid (cuisines.php) ---- */
.qm-cuisgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--sp-5); list-style: none; margin: 0; padding: 0; }
.qm-cuisgrid-item { display: flex; }
.qm-cuisgrid-card { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--sp-3, 12px); width: 100%; text-align: center; text-decoration: none; padding: var(--sp-6, 28px) var(--sp-4) var(--sp-5); border-radius: var(--r-lg); border: 1px solid color-mix(in srgb, var(--cc, var(--qm-primary)) 20%, #fff); background: linear-gradient(180deg, #fff, color-mix(in srgb, var(--cc, var(--qm-primary)) 10%, #fff)); box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-cuisgrid-card:hover { transform: translateY(-5px); box-shadow: var(--e-3); border-color: var(--cc, var(--qm-primary)); }
.qm-cuisgrid-dishes { display: flex; gap: 6px; align-items: center; justify-content: center; min-height: 44px; }
.qm-cuisgrid-dishes img { width: 40px; height: 40px; object-fit: contain; filter: drop-shadow(0 2px 3px rgba(0,0,0,.16)); }
.qm-cuisgrid-name { font-weight: 800; font-size: 17px; color: var(--ink-900); margin: 0; }
.qm-cuisgrid-blurb { font-size: 12px; line-height: 1.45; color: var(--ink-700); }
.qm-cuisgrid-count { font-size: 12px; color: var(--ink-500); font-weight: 600; }
/* Contrast-safe 'Browse' accent: darken the per-card tint so text on the pale tinted card clears AA. */
.qm-cuisgrid-go { font-size: var(--fs-400); font-weight: 700; color: color-mix(in srgb, var(--cc, var(--qm-primary)) 78%, #000); display: inline-flex; align-items: center; gap: 6px; margin-top: 2px; }
.qm-cuisgrid-go i { transition: transform var(--t-fast); }
.qm-cuisgrid-card:hover .qm-cuisgrid-go i { transform: translateX(3px); }

/* ---- Active-filter chips (restaurant-list) ---- */
.qm-filter-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2, 8px); margin-bottom: var(--sp-4, 16px); }
.qm-filter-chips-lbl { font-size: .85rem; font-weight: 700; color: var(--ink-700); }
.qm-filter-chip { display: inline-flex; align-items: center; gap: .4rem; padding: .3rem .7rem; font-size: .82rem; font-weight: 600; color: var(--ink-900); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: var(--r-pill); text-decoration: none; transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast); }
.qm-filter-chip i { font-size: .72em; }
.qm-filter-chip:hover, .qm-filter-chip:focus-visible { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-filter-chip-clear { background: transparent; border-color: var(--line); color: var(--ink-600); font-weight: 700; }
.qm-filter-chip-clear:hover, .qm-filter-chip-clear:focus-visible { background: var(--ink-900); border-color: var(--ink-900); color: #fff; }

/* ---- Restaurants results bar + grid/list toggle (was inline in restaurant-list.php) ---- */
.qm-results-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4, 16px); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.qm-results-bar .qm-results-count { margin: 0; font-size: 1rem; font-weight: 700; color: var(--ink-900); }
.qm-view-toggle { display: inline-flex; gap: 4px; padding: 4px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-sm); }
.qm-view-btn { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 34px; border-radius: var(--r-sm); color: var(--ink-600); text-decoration: none; transition: background var(--t-fast), color var(--t-fast); }
.qm-view-btn:hover { color: var(--qm-primary); }
.qm-view-btn.is-active { background: var(--qm-primary); color: #fff; box-shadow: var(--e-2); }

/* ---- Restaurants list-view horizontal row cards (was inline in restaurant-list.php) ---- */
.qm-rest-rows { display: flex; flex-direction: column; gap: var(--sp-4, 16px); }
.qm-rest-row { display: flex; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; box-shadow: var(--e-1); transition: box-shadow var(--t-fast), transform var(--t-fast); }
.qm-rest-row:hover { box-shadow: var(--e-2); transform: translateY(-2px); }
.qm-rest-row-media { position: relative; flex: 0 0 220px; width: 220px; align-self: stretch; min-height: 180px; overflow: hidden; background: var(--line-2); }
.qm-rest-row-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-rest-row:hover .qm-rest-row-media img { transform: scale(1.06); }
.qm-rest-row-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 6px; padding: var(--sp-4, 16px) var(--sp-4); min-width: 0; }
.qm-rest-row-body .qm-rest-name { margin: 0; }
.qm-rest-row-body .qm-rest-name a { color: inherit; text-decoration: none; }
.qm-rest-row-body .qm-rest-name a:hover { color: var(--qm-primary); }
.qm-rest-row-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); flex-wrap: wrap; margin-top: auto; padding-top: var(--sp-3, 12px); }
.qm-rest-row-foot .qm-diet { margin-top: 0; }
@media (max-width: 575.98px) {
  .qm-rest-row { flex-direction: column; }
  .qm-rest-row-media { flex-basis: auto; width: 100%; aspect-ratio: 17/10; min-height: 0; }
}

/* ---- Cuisines A-Z panel overlay tweaks ---- */
.qm-cuis-head .qm-cuis-title { margin: 0; font-weight: 800; font-size: var(--fs-700); color: var(--ink-900); display: block; }
.qm-cuis-panel .qm-cuis-head .qm-cuis-title { color: #fff; }
.qm-cuis-panel .qm-cuis-card { overflow: hidden; }
.qm-cuis-flag { position: absolute; top: 0; right: 0; bottom: 0; width: 62%; z-index: 0; pointer-events: none; background-repeat: no-repeat; background-position: right center; background-size: cover; filter: grayscale(1); opacity: .42; transition: filter .25s ease, opacity .25s ease; -webkit-mask-image: linear-gradient(to left, #000 30%, transparent 100%); mask-image: linear-gradient(to left, #000 30%, transparent 100%); }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-flag { filter: grayscale(0); opacity: .9; }
.qm-cuis-panel .qm-cuis-card .qm-cuis-name, .qm-cuis-panel .qm-cuis-card .qm-cuis-dishes { position: relative; z-index: 1; }
.qm-cuis-panel .qm-cuis-card .qm-cuis-count { position: relative; z-index: 2; display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--r-pill); background: var(--qm-primary-600); color: #fff; font-weight: 800; border: 1px solid rgba(255,255,255,.45); box-shadow: 0 1px 5px rgba(0,0,0,.30); }
/* Dish icons in each right-sidebar cuisine tab lift in a gentle, consistent wave on
hover (matching the flag colourising) for a livelier, more interactive feel. */
.qm-cuis-panel .qm-cuis-dishes > * { transition: transform .28s cubic-bezier(.22,.68,.3,1.3), color .25s ease, filter .25s ease; }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dishes > * { transform: translateY(-4px); }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dishes > *:nth-child(2) { transition-delay: .05s; }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dishes > *:nth-child(3) { transition-delay: .1s; }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dishes > *:nth-child(4) { transition-delay: .14s; }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dish { filter: drop-shadow(0 3px 5px rgba(0,0,0,.4)); }
.qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dish-fa { color: #fff; }
.qm-cuis-panel .qm-cuis-dish:hover { transform: translateY(-4px) scale(1.16); }
@media (prefers-reduced-motion: reduce) {
  .qm-cuis-panel .qm-cuis-dishes > * { transition: none; }
  .qm-cuis-panel .qm-cuis-card:hover .qm-cuis-dishes > * { transform: none; }
}

/* ====================== ABOUT / CONTACT ====================== */

/* ---- Contact cards as <a> links (interactive affordance) + icon chips ---- */
.qm-contact-card[href]:hover, .qm-contact-card[href]:focus-visible { border-color: var(--qm-primary); box-shadow: var(--e-2); transform: translateY(-2px); outline: none; }
.qm-contact-card[href]:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-contact-card { transition: border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); }
.qm-contact-ico { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; border-radius: var(--r-md); background: color-mix(in srgb, var(--qm-primary) 12%, #fff); flex: none; margin-top: 0; }
.qm-contact-ico i { color: var(--qm-primary); font-size: var(--fs-800); margin: 0; width: auto; }
.qm-contact-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.qm-contact-value { color: var(--ink-700); font-weight: 500; word-break: break-word; }
.qm-contact-card-hours .qm-contact-value { white-space: pre-line; }
.qm-contact-note { display: inline-flex; gap: 6px; align-items: center; margin-top: var(--sp-2); font-size: var(--fs-400); color: var(--ink-500); }
.qm-contact-note i { color: var(--qm-primary); font-size: var(--fs-400); width: auto; margin: 0; }
/* Real-map (QMMap/Leaflet) container on the Contact page; height comes from .qm-map. */
.qm-contact-map { margin-top: var(--sp-4); border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--e-1); }
/* Inline-SVG locator palette (token-driven). */
/* Location picker inside a settings card — same framed treatment as the other map
embeds, sized so a pin can be placed comfortably without dominating the form. */
.qm-settings-map { height: 320px; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.qm-map-bg { fill: var(--surface-2); }
.qm-map-grid { stroke: var(--line); stroke-width: 1; fill: none; }
.qm-map-river { stroke: var(--info); stroke-width: 8; fill: none; opacity: .5; }
.qm-map-block { fill: var(--surface); stroke: var(--line); }
.qm-map-pin-body { fill: var(--qm-primary); }
.qm-map-pin-dot { fill: var(--surface); }
/* ---- Contact link list (mailto:/tel:/social rows) ---- */
.qm-contact-links { display: flex; flex-direction: column; gap: var(--sp-3); margin-top: var(--sp-4); }
.qm-contact-link { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); text-decoration: none; transition: border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); }
.qm-contact-link:hover, .qm-contact-link:focus-visible { border-color: var(--qm-primary); box-shadow: var(--e-2); transform: translateY(-2px); outline: none; }
.qm-contact-link:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-contact-link-ico { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: var(--r-md); background: color-mix(in srgb, var(--qm-primary) 12%, #fff); color: var(--qm-primary); flex: none; }
.qm-contact-link-body { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.qm-contact-link-title { font-weight: 600; color: var(--ink-700); }
.qm-contact-link-text { font-size: var(--fs-400); color: var(--ink-500); word-break: break-word; }
.qm-contact-link-arrow { color: var(--ink-400); margin-left: auto; }
/* ---- Contact form helpers + client validation ---- */
.qm-form-intro { color: var(--ink-500); margin-bottom: var(--sp-4); }
.qm-req-mark { color: var(--qm-primary); }
/* Canonical inline form-status banner — shared by contact (.is-error) AND blog-detail (--ok/--err). */
.qm-form-status { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); font-size: 14px; font-weight: 600; border: 1px solid transparent; }
.qm-form-status i { font-size: var(--fs-700); }
.qm-form-status.is-error, .qm-form-status--err { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }
.qm-form-status.is-ok, .qm-form-status--ok { background: var(--ok-bg); border-color: var(--ok); color: var(--ok); }
.qm-field-error { color: var(--danger); font-size: var(--fs-400); margin: var(--sp-1) 0 0; }

/* ====================== CONTENT-MISC ====================== */

/* ---- Deals page (deals.php) ---- */
.qm-deals-intro { max-width: 760px; margin-bottom: var(--sp-6); }
.qm-deals-intro .qm-eyebrow { margin-bottom: var(--sp-2); }
.qm-deal-code { font-family: inherit; line-height: 1.2; }
.qm-deal-code-label { font-weight: 600; opacity: .8; text-transform: uppercase; letter-spacing: .04em; font-size: .82em; }
.qm-deal-code-val { font-weight: 800; letter-spacing: .5px; }
.qm-deal-code.is-copied { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-deal-code:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }

/* ---- Help center (help.php) ---- */
.qm-help-hero { max-width: 760px; margin: 0 auto var(--sp-6); text-align: center; }
/* Help hero uses the shared FAQ search; only the hero's own spacing differs. */
.qm-help-hero .qm-faq-search { margin: var(--sp-4) auto 0; }
/* Accordion frame — shared by the storefront FAQ (.qm-faq) and the dashboard designers
   (.qm-acc: receipt / QR card / TV screen), so the collapsible-section style is defined once. */
.qm-faq .accordion-item, .qm-acc .accordion-item { border: 1px solid var(--line); border-radius: var(--r-md); margin-bottom: var(--sp-3); overflow: hidden; background: var(--surface); }
.qm-faq .accordion-button, .qm-acc .accordion-button { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--ink-900); background: var(--surface); }
.qm-faq .accordion-button:not(.collapsed), .qm-acc .accordion-button:not(.collapsed) { color: var(--qm-primary); background: var(--qm-primary-50); box-shadow: none; }
.qm-faq .accordion-button:focus, .qm-acc .accordion-button:focus { box-shadow: 0 0 0 .2rem var(--qm-primary-100); }
.qm-faq .accordion-button:not(.collapsed)::after, .qm-acc .accordion-button:not(.collapsed)::after { filter: none; }
.qm-faq .accordion-body { color: var(--ink-500); font-size: .92rem; line-height: 1.6; }
.qm-faq-noresult { margin-top: var(--sp-3); color: var(--ink-500); }

/* ---- Find location: restaurants list + live map split (geo.php) ---- */
.qm-findloc-explore { padding-top: var(--sp-7); }
/* ---- What a map pin shows when opened (partials/map-venue-popup.php) ---- */
/* Leaflet's popup is a thin frame; everything inside is ours, so the frame gets out of the way and
   the card sets its own rhythm. One width, so the dish rail scrolls INSIDE the card rather than
   stretching it across the map. */
.qm-map-popup-rich .leaflet-popup-content-wrapper { padding: 0; border-radius: var(--r-lg); overflow: hidden; }
.qm-map-popup-rich .leaflet-popup-content { margin: 0; width: 320px !important; max-width: calc(100vw - 72px) !important; }
/* Leaflet paints links inside its popups its own blue (.leaflet-container a), and its stylesheet is
   a page style so it loads AFTER this file — at equal weight it would win on source order. Matching
   the popup and the container together outranks it, so this card takes the product's own ink. */
.leaflet-container .qm-map-popup-rich a,
.leaflet-popup.qm-map-popup-rich a { color: inherit; }

/* Leaflet's close control is an unstyled serif "x" tucked against the corner. This is our card, so
   it takes the same round, muted control the product's other dialogs use, inset to match the band
   padding beside it. */
/* SPECIFICITY, not just presence. Leaflet styles this as `.leaflet-container a.leaflet-popup-close-button`
   (0,2,1) and its stylesheet loads after ours, so a two-class selector lost on both counts: the
   control kept Leaflet's top:0/right:0 and transparent background, and only the border-radius here
   ever applied — which is why it read as default and why its round hover spilled over the card's
   rounded corner. Matching the container, the popup and the anchor outranks it. `top`/`right` are
   the properties Leaflet sets, so they are the ones that must be overridden; the logical pair does
   not cascade over them. */
.leaflet-container .qm-map-popup-rich a.leaflet-popup-close-button {
  top: 8px; right: 8px; width: 28px; height: 28px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  font: 700 16px/1 inherit; color: var(--ink-500); border-radius: 50%;
  background: var(--surface); transition: background var(--t-fast), color var(--t-fast); }
.leaflet-container .qm-map-popup-rich a.leaflet-popup-close-button:hover {
  background: var(--surface-2); color: var(--ink-900); }
/* The name must not run under it. */
.qm-mappop .qm-mappop-head { padding-inline-end: 44px; }

/* ONE PADDING, ONE DIVIDER. Each band is a step in the same story — who, when, what, how — so they
   share their inset and are separated by the same hairline used everywhere else. */
.qm-mappop { display: flex; flex-direction: column; }
.qm-mappop-band { padding: var(--sp-3); }
.qm-mappop-band + .qm-mappop-band { border-top: 1px solid var(--line); }
/* THE HEAD IS ONE THOUGHT: who this is, how they rate, whether you can order. Those three lines
   were spaced as if each were its own section, which left more air under the last of them than
   above the first — the band looked bottom-heavy against its divider. They sit together now, and
   the band's top and bottom read as equal. */
.qm-mappop .qm-mappop-head { padding-block-end: 12px; }

.qm-mappop .qm-mappop-name { display: block; font-weight: 700; font-size: 1.0625rem; line-height: 1.3; color: var(--ink-900); text-decoration: none; }
.qm-mappop .qm-mappop-name:hover { color: var(--brand); }
/* These three lines are one thought and sit together. The paragraph margin that ships with the rest
   of the product is what pushed them 16px apart and left 26px under the last of them against 13px
   above the first — the band read bottom-heavy. Reset here, and the band's own padding decides. */
.qm-mappop .qm-mappop-meta { margin: 3px 0 0; font-size: 12px; color: var(--ink-700, var(--ink-900)); }
.qm-mappop-rating .fa-star { color: var(--star); }
.qm-mappop-dim { color: var(--ink-500); }
.qm-mappop .qm-mappop-hours { display: flex; align-items: center; gap: var(--sp-2); margin: 7px 0 0; font-size: 12px; }

/* The rail: a title on one side, the shared arrow pair on the other, then the track. */
.qm-mappop-dishes-band { padding-inline-end: 0; }
.qm-mappop-dishes-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding-inline-end: var(--sp-3); margin-bottom: var(--sp-2); }
.qm-mappop-dishes-title { margin: 0; font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink-500); }
.qm-mappop-dishes-band .qm-carousel-nav { position: static; opacity: 1; }
.qm-mappop-dishes-band .qm-carousel-btn { width: 26px; height: 26px; font-size: 11px; }
/* No scrollbar — the arrows move it. */
.qm-mappop-dishes { display: flex; gap: var(--sp-2); overflow-x: auto; scroll-behavior: smooth;
  scrollbar-width: none; -ms-overflow-style: none; padding-inline-end: var(--sp-3);
  /* The rail owns horizontal gestures; without this a swipe pans the map underneath it. */
  touch-action: pan-y; }
.qm-mappop-dishes::-webkit-scrollbar { display: none; }
.qm-mappop-dish { flex: 0 0 auto; width: 104px; text-decoration: none; color: inherit; }
/* A rail you can pull says so, and its images must not be draggable out of the page. */
.qm-mappop-dishes { cursor: grab; }
.qm-mappop-dishes.is-dragging { cursor: grabbing; scroll-snap-type: none; }
.qm-mappop-dishes img { -webkit-user-drag: none; user-select: none; pointer-events: none; }
.qm-mappop-dish img { width: 104px; height: 76px; object-fit: cover; border-radius: var(--r-sm, 8px); display: block; }
.qm-mappop-dish-name { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 12px; line-height: 1.3; margin-top: 6px; color: var(--ink-700, var(--ink-900)); }
.qm-mappop-dish-price { display: block; font-size: 12px; font-weight: 700; color: var(--ink-900); margin-top: 2px; }

/* The two ways to order, equally weighted — this is a choice, not an action with an afterthought. */
.qm-mappop-modes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.qm-mappop-mode { display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--sp-2) var(--sp-1); border: 1px solid var(--line); border-radius: var(--r-md);
  text-decoration: none; font-size: 12px; transition: border-color var(--t-fast), background var(--t-fast); }
.qm-mappop-mode:hover { border-color: var(--brand); background: var(--surface-2); }
.qm-mappop-mode strong { font-size: var(--fs-400, 13px); color: var(--ink-900); }
.qm-mappop-mode span { color: var(--ink-500); }
/* Driver variant of the same card (vendor dashboard's live-delivery pins):
   the head pairs the rider's avatar with their name, and the two mode buttons
   become Call / Text — same band, same buttons, plus their action icons. */
.qm-mappop-rider { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
/* One line per order the rider carries — hairline rhythm, no bullets. */
.qm-mappop-orders { display: flex; flex-direction: column; gap: 5px; }
.qm-mappop-rider-tx .qm-mappop-name { font-size: var(--fs-600, 15px); }
.qm-mappop-driver .qm-mappop-mode strong i { font-size: 11px; color: var(--qm-primary); margin-inline-end: 4px; }

.qm-findloc-split { display: grid; grid-template-columns: minmax(320px, 420px) minmax(0, 1fr); gap: var(--sp-5); align-items: stretch; }
.qm-findloc-list { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-1); }
.qm-findloc-list-head { display: flex; align-items: center; justify-content: space-between; padding: var(--sp-4); border-bottom: 1px solid var(--line); background: var(--surface-2); }
.qm-findloc-list-head :is(h1,h2,h3,h4,h5,h6) { margin: 0; font-size: 1.05rem; font-weight: 800; color: var(--ink-900); }
.qm-findloc-list-count { font-size: 12px; font-weight: 700; color: var(--qm-primary-600); }
/* TALLER ABOVE THE FOLD. The map is the point of this page and 560px left it a strip between the
   heading and whatever came next; the list matches it so the two columns still end together. */
.qm-findloc-list-scroll { overflow-y: auto; max-height: 720px; }
.qm-findloc-item { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line-2); text-decoration: none; color: inherit; transition: background var(--t-fast); }
.qm-findloc-item:last-child { border-bottom: 0; }
.qm-findloc-item:hover { background: var(--qm-primary-50); }
.qm-findloc-item-thumb { flex: 0 0 auto; width: 54px; height: 54px; border-radius: var(--r-md); overflow: hidden; }
.qm-findloc-item-thumb img { width: 100%; height: 100%; object-fit: cover; }
.qm-findloc-item-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-findloc-item-name { font-weight: 700; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-findloc-item-sub { font-size: 12px; color: var(--ink-500); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-findloc-item-meta { font-size: 12px; color: var(--ink-600); }
.qm-findloc-item-meta .fa-star { color: var(--star); }
.qm-findloc-item-meta .fa-clock, .qm-findloc-item-meta .fa-clock-o { color: var(--ink-400); }
.qm-findloc-item-arrow { flex: 0 0 auto; color: var(--ink-300); font-size: 12px; }
.qm-findloc-empty { padding: var(--sp-5); color: var(--ink-500); text-align: center; }
.qm-findloc-mapwrap { position: relative; border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--e-1); min-height: 560px; }
/* "Search this area" control — appears once the visitor pans/zooms the map. */
.qm-findloc-search-area { position: absolute; left: 50%; bottom: var(--sp-4); transform: translateX(-50%); z-index: 1000; display: inline-flex; align-items: center; gap: 8px; padding: 10px 18px; border: 0; border-radius: var(--r-pill); background: var(--ink-900); color: #fff; font-weight: 700; font-size: var(--fs-400, 13px); box-shadow: var(--e-3); cursor: pointer; transition: transform var(--t-fast), background var(--t-fast); }
.qm-findloc-search-area:hover { background: var(--qm-primary-600); transform: translateX(-50%) translateY(-2px); }
.qm-findloc-search-area[hidden] { display: none; }
.qm-findloc-search-area i { font-size: 12px; }
.qm-findloc-reset { margin-left: 8px; border: 0; background: transparent; color: var(--qm-primary-600); font-weight: 800; font-size: 12px; cursor: pointer; padding: 0; text-decoration: underline; }
.qm-findloc-reset:hover { color: var(--qm-primary); }
.qm-findloc-map-canvas { height: 100%; min-height: 720px; }
@media (max-width: 860px) {
  .qm-findloc-split { grid-template-columns: 1fr; }
  /* A map is useless at 340px on a phone — that is a strip, not somewhere you can look around.
     It takes most of the screen instead, which is what it is for on this page. */
  .qm-findloc-mapwrap, .qm-findloc-map-canvas { min-height: 70vh; }
  .qm-findloc-list-scroll { max-height: 420px; }
  .qm-findloc-list-scroll { max-height: 380px; }
}

/* ---- Order builder (order-builder.php) ---- */
/* "Sort by category" navigator label above the board's category pills. */
.qm-ob-catbar-label { display: flex; align-items: center; gap: 6px; padding: var(--sp-3) var(--sp-4) 6px; font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-600); }
.qm-ob-catbar-label i { color: var(--qm-primary); }
.qm-ob-catbar .qm-ob-cats { padding-top: 0; }
.qm-ob { display: grid; grid-template-columns: 1fr 1.5fr 1fr; gap: var(--sp-4); align-items: stretch; }
.qm-ob-rests, .qm-ob-basket { display: flex; flex-direction: column; }
/* Picked a restaurant: the picker collapses to its toggle and should sit compact, not stretch. */
.qm-ob.is-picker-collapsed .qm-ob-rests { align-self: start; }
.qm-ob-col { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-2); overflow: hidden; }
.qm-ob-col-head { display: flex; gap: var(--sp-3); align-items: flex-start; padding: var(--sp-4); border-bottom: 1px solid var(--line); }
.qm-ob-step { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%; background: var(--qm-primary-600); color: #fff; font-weight: 700; display: flex; align-items: center; justify-content: center; font-size: .95rem; }
.qm-ob-col-title { font-size: 1.05rem; margin: 0; color: var(--ink-900); }
.qm-ob-col-sub { margin: 2px 0 0; font-size: .82rem; color: var(--ink-500); }
/* flex-basis 0 + min-height 0: the list adds nothing to the row's height, so the food board sets
   it and the list fills down to the same edge, scrolling inside. */
.qm-ob-rest-list { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: var(--sp-2); }
/* Collapsible picker: once a kitchen is chosen the list collapses to a compact
   "Ordering from …" card and the board (step 2) gets more room. */
.qm-ob-rest-toggle { display: flex; width: 100%; align-items: center; gap: var(--sp-3); padding: var(--sp-3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; text-align: left; box-shadow: var(--e-1); transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.qm-ob-rest-toggle[hidden] { display: none; }
.qm-ob-rest-toggle:hover { border-color: var(--qm-primary); box-shadow: var(--e-2); }
.qm-ob-rest-toggle-thumb { flex: 0 0 auto; width: 46px; height: 46px; border-radius: var(--r-md); overflow: hidden; }
.qm-ob-rest-toggle-thumb img { width: 100%; height: 100%; object-fit: cover; }
.qm-ob-rest-toggle-body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.qm-ob-rest-toggle-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500); }
.qm-ob-rest-toggle-name { font-weight: 800; color: var(--ink-900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qm-ob-rest-toggle-change { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 800; color: var(--qm-primary); }
@media (min-width: 1200px) {
  .qm-ob.is-picker-collapsed { grid-template-columns: minmax(220px, .72fr) minmax(0, 1.9fr) minmax(0, 1fr); }
}
/* Active "Browse by food type" filter on the order-builder + the picker's empty message. */
.svg-foodtype-item.is-ob-active { box-shadow: inset 0 0 0 2px var(--qm-primary), var(--e-2); }
.qm-ob-rest-none { padding: var(--sp-4); text-align: center; color: var(--ink-500); font-size: 13.5px; }
.qm-ob-rest { display: flex; gap: var(--sp-3); width: 100%; text-align: left; align-items: center; padding: var(--sp-2); border: 1px solid transparent; border-radius: var(--r-md); background: transparent; cursor: pointer; transition: .15s; }
.qm-ob-rest + .qm-ob-rest { margin-top: var(--sp-1); }
.qm-ob-rest:hover { background: var(--qm-primary-50); }
.qm-ob-rest.is-active { border-color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 8%, #fff); }
.qm-ob-rest-thumb { flex: 0 0 auto; width: 56px; height: 56px; border-radius: var(--r-md); overflow: hidden; }
.qm-ob-rest-thumb img { width: 100%; height: 100%; object-fit: cover; }
.qm-ob-rest-meta { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.qm-ob-rest-name { font-weight: 600; color: var(--ink-900); font-size: .92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-ob-rest-sub { font-size: .78rem; color: var(--ink-500); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-ob-rest-pt { font-size: .74rem; color: var(--ink-500); }
.qm-ob-rest-pt .fa-star { color: var(--star); }
.qm-ob-cats { display: flex; flex-wrap: wrap; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); }
.qm-ob-cat { appearance: none; border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); font-size: .8rem; font-weight: 600; line-height: 1; padding: 8px 14px; border-radius: var(--r-pill); cursor: pointer; white-space: nowrap; transition: .15s; }
.qm-ob-cat:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-ob-cat[aria-selected="true"] { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-ob-cat:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-ob-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); padding: var(--sp-4); max-height: 560px; overflow-y: auto; }
@media (min-width: 1200px) { .qm-ob-grid { grid-template-columns: repeat(3, 1fr); } }
.qm-ob-food { position: relative; display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); overflow: hidden; cursor: grab; transition: .15s; touch-action: none; -webkit-user-select: none; user-select: none; }
.qm-ob-food:hover { box-shadow: var(--e-2); transform: translateY(-2px); }
.qm-ob-food.is-dragging { opacity: .45; }
.qm-ob-food.in-order { border-color: var(--qm-primary); box-shadow: 0 0 0 1px var(--qm-primary); }
.qm-ob-food.is-hidden-search { display: none; }
.qm-ob-food-media { position: relative; aspect-ratio: 3/2; background: var(--line); }
.qm-ob-food-media img { width: 100%; height: 100%; object-fit: cover; }
.qm-ob-food-tag { position: absolute; top: 6px; left: 6px; background: var(--qm-primary-600); color: #fff; font-size: .66rem; font-weight: 700; padding: 2px 7px; border-radius: var(--r-pill); text-transform: uppercase; letter-spacing: .03em; }
.qm-ob-food-body { display: flex; flex-direction: column; gap: 4px; padding: var(--sp-3); flex: 1; }
.qm-ob-food-head { display: flex; align-items: center; gap: 6px; }
.qm-ob-food-name { font-size: .88rem; font-weight: 600; margin: 0; color: var(--ink-900); line-height: 1.25; }
.qm-ob-food-desc { font-size: .76rem; color: var(--ink-500); margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.qm-ob-food-foot { display: flex; align-items: center; justify-content: space-between; margin-top: auto; padding-top: var(--sp-2); }
.qm-ob-food-price { font-weight: 700; color: var(--ink-900); font-size: .92rem; }
.qm-ob-food-add { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--qm-primary); background: var(--surface); color: var(--qm-primary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: .15s; }
.qm-ob-food-add:hover { background: var(--qm-primary); color: #fff; }
.qm-ob-food-qty { position: absolute; top: 6px; right: 6px; min-width: 22px; height: 22px; border-radius: var(--r-pill); background: var(--qm-grad); color: #fff; font-size: .72rem; font-weight: 800; display: flex; align-items: center; justify-content: center; padding: 0 6px; box-shadow: var(--e-2); z-index: 2; }
.qm-badge-vegdot { flex: 0 0 auto; width: 13px; height: 13px; border-radius: 3px; border: 2px solid var(--badge-veg); position: relative; }
.qm-badge-vegdot::after { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--badge-veg); }
.qm-badge-nonvegdot { flex: 0 0 auto; width: 13px; height: 13px; border-radius: 3px; border: 2px solid var(--badge-nonveg); position: relative; }
.qm-badge-nonvegdot::after { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--badge-nonveg); }
.qm-ob-drop { flex: 1 1 auto; min-height: 300px; overflow-y: auto; margin: var(--sp-3); border: 2px dashed var(--line); border-radius: var(--r-md); transition: .15s; }
.qm-ob-drop.is-over { border-color: var(--qm-primary); background: var(--qm-primary-50); }
.qm-ob-drop.is-target { border-color: color-mix(in srgb, var(--qm-primary) 40%, transparent); }
.qm-ob-drop-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--sp-3); text-align: center; color: var(--ink-500); padding: var(--sp-6) var(--sp-4); min-height: 296px; }
.qm-ob-drop-empty i { font-size: 2.2rem; color: var(--line); }
.qm-ob-drop-empty p { margin: 0; font-size: .86rem; }
.qm-ob-lines { list-style: none; margin: 0; padding: var(--sp-2); }
.qm-ob-line { display: flex; gap: var(--sp-2); align-items: center; padding: var(--sp-2); border-radius: var(--r-md); }
.qm-ob-line + .qm-ob-line { border-top: 1px solid var(--line); }
.qm-ob-line.is-new { animation: qm-ob-pop .25s ease; }
.qm-ob-line-thumb { flex: 0 0 auto; width: 44px; height: 44px; border-radius: var(--r-md); overflow: hidden; }
.qm-ob-line-thumb img { width: 100%; height: 100%; object-fit: cover; }
.qm-ob-line-info { flex: 1; min-width: 0; }
/* Tap the line body (thumb + info) to re-open the customize popup and edit it. */
.qm-ob-line-tap { display: flex; align-items: center; gap: var(--sp-2); flex: 1; min-width: 0; cursor: pointer; border-radius: var(--r-md); }
.qm-ob-line-tap:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.qm-ob-line-opt { display: block; font-size: 12px; color: var(--ink-500); line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qm-ob-line-editico { font-size: 10px; color: var(--qm-primary); margin-left: 4px; opacity: .65; }
.qm-ob-line:hover .qm-ob-line-editico { opacity: 1; }
.qm-ob-line-name { font-size: .84rem; font-weight: 600; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; }
.qm-ob-line-price { font-size: .78rem; color: var(--ink-500); }
.qm-ob-qty { display: flex; align-items: center; gap: 6px; }
.qm-ob-qty button { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: .72rem; line-height: 1; }
.qm-ob-qty button:hover { border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-ob-qty-val { min-width: 18px; text-align: center; font-weight: 600; font-size: .82rem; }
.qm-ob-line-remove { flex: 0 0 auto; border: 0; background: transparent; color: var(--ink-500); cursor: pointer; padding: 4px; }
.qm-ob-line-remove:hover { color: var(--qm-primary); }
.qm-ob-summary { padding: var(--sp-4); border-top: 1px solid var(--line); }
.qm-ob-total-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--sp-3); font-size: .95rem; color: var(--ink-700); }
.qm-ob-count { font-size: .78rem; color: var(--ink-500); font-weight: 400; }
.qm-ob-total { font-size: 1.2rem; color: var(--ink-900); }
.qm-ob-note { margin: var(--sp-2) 0 0; font-size: .8rem; color: var(--ink-500); text-align: center; }
.qm-ob-search { position: relative; padding: var(--sp-3) var(--sp-4) 0; }
.qm-ob-search i { position: absolute; left: calc(var(--sp-4) + 13px); top: calc(var(--sp-3) + 11px); color: var(--ink-400); font-size: .8rem; pointer-events: none; }
.qm-ob-search input { width: 100%; border: 1px solid var(--line); border-radius: var(--r-pill); padding: 9px 14px 9px 36px; font-size: .84rem; color: var(--ink-800); }
.qm-ob-search input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-100); }
.qm-ob-drag-clone { position: fixed; z-index: 2000; pointer-events: none; opacity: .95; box-shadow: var(--e-4); transform: rotate(-2deg) scale(.97); margin: 0; border-color: var(--qm-primary); }
.qm-ob-fly { position: fixed; z-index: 2001; border-radius: 14px; overflow: hidden; pointer-events: none; box-shadow: var(--e-3); }
.qm-ob-fly img { width: 100%; height: 100%; object-fit: cover; }
@keyframes qm-ob-pulse { 0% { transform: scale(1); } 45% { transform: scale(1.16); } 100% { transform: scale(1); } }
.qm-ob-basket.is-pulse .qm-ob-step { animation: qm-ob-pulse .42s ease; }
.qm-ob-progress { margin-bottom: var(--sp-3); }
.qm-ob-progress-bar { height: 7px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; }
.qm-ob-progress-bar span { display: block; height: 100%; width: 0; background: var(--qm-grad); border-radius: var(--r-pill); transition: width .3s ease; }
.qm-ob-progress.is-met .qm-ob-progress-bar span { background: var(--ok); }
.qm-ob-progress-msg { margin: 6px 0 0; font-size: .76rem; color: var(--ink-500); }
.qm-ob-progress.is-met .qm-ob-progress-msg { color: var(--ok); font-weight: 600; }
.qm-ob-clear { display: block; width: 100%; margin-top: var(--sp-2); background: transparent; border: 0; color: var(--ink-500); font-size: .8rem; font-weight: 600; cursor: pointer; }
.qm-ob-clear:hover { color: var(--qm-primary); }
@keyframes qm-ob-pop { 0% { transform: scale(.92); opacity: .2; } 100% { transform: scale(1); opacity: 1; } }
@media (max-width: 1199.98px) and (min-width: 768px) {
  .qm-ob { grid-template-columns: 1fr 1fr; }
  .qm-ob-basket { grid-column: 1 / -1; }
  /* The basket stacks BELOW the grid here, so the drag axis is vertical and the
  dish cards are the page's dominant touch surface: release the base
  touch-action:none or the page cannot be scrolled by finger from them. */
  .qm-ob-food { touch-action: auto; }
}
@media (max-width: 767.98px) {
  .qm-ob { grid-template-columns: 1fr; }
  .qm-ob-rest-list, .qm-ob-grid { max-height: none; }
  .qm-ob-rest-list { flex: 0 0 auto; }
  .qm-ob-drop { flex: 0 0 auto; }
  .qm-ob-qty button { width: 44px; height: 44px; }
  .qm-ob-food { touch-action: auto; }
}

/* ---- Partner / become-a-partner (partner.php) ---- */
.qm-partner-hero { background: var(--qm-grad); color: #fff; padding: var(--sp-10) 0; position: relative; overflow: hidden; }
.qm-partner-hero::after { content: ""; position: absolute; right: -120px; top: -120px; width: 380px; height: 380px; border-radius: 50%; background: rgba(255,255,255,.12); pointer-events: none; }
.qm-partner-hero-inner { position: relative; z-index: 1; display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, .85fr); gap: clamp(24px, 4vw, 48px); align-items: center; }
.qm-partner-hero-copy { max-width: 560px; }
.qm-partner-hero .qm-eyebrow { color: rgba(255,255,255,.85); }
.qm-partner-hero :is(h1,h2,h3,h4,h5,h6) { color: #fff; font-size: clamp(30px, 4.4vw, 50px); font-weight: 800; line-height: 1.08; margin: var(--sp-2) 0 var(--sp-3); letter-spacing: -.02em; }
.qm-partner-hero p { color: rgba(255,255,255,.92); font-size: clamp(15px, 1.6vw, 19px); max-width: 600px; margin-bottom: var(--sp-5); }
.qm-partner-hero-cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-bottom: var(--sp-6); }
/* The primary button wears the SAME gradient as the hero behind it, so its edges
   vanished. Ink-black here — the one surface where the brand button sits on the
   brand paint — and the hover must override .qm-btn's own gradient hover. */
.qm-partner-hero-cta .qm-btn:not(.qm-btn-outline) { background: var(--ink-900, #1c1917); box-shadow: 0 12px 22px rgba(0, 0, 0, .3); }
.qm-partner-hero-cta .qm-btn:not(.qm-btn-outline):hover { background: #000; box-shadow: 0 14px 26px rgba(0, 0, 0, .38); }
.qm-partner-hero-cta .qm-btn-outline { background: transparent; border-color: rgba(255,255,255,.7); color: #fff; }
.qm-partner-hero-cta .qm-btn-outline:hover { background: rgba(255,255,255,.14); border-color: #fff; }
.qm-partner-stats { display: flex; flex-wrap: wrap; gap: var(--sp-7); }
.qm-partner-stats div { display: flex; flex-direction: column; }
.qm-partner-stats strong { font-size: 30px; font-weight: 800; line-height: 1; }
.qm-partner-stats span { font-size: var(--fs-400); color: rgba(255,255,255,.85); margin-top: 4px; }
/* Above-the-fold owner+logo collage — bare (no name/shop captions), same floating
circular treatment as the Hungry-page hero's dish photos (.qm-herohg-dish),
reusing its qm-hg-float keyframe and d0-d4-style staggered sizing/positions. */
.qm-partner-hero-art { position: relative; height: 100%; min-height: 300px; }
.qm-partner-hero-face { position: absolute; display: inline-flex; }
.qm-partner-hero-face img:first-of-type { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; box-shadow: 0 14px 30px rgba(0,0,0,.3); border: 4px solid rgba(255,255,255,.92); animation: qm-hg-float 6s ease-in-out infinite; transition: scale var(--t), rotate var(--t), box-shadow var(--t); }
.qm-partner-hero-face.f0 { width: 170px; height: 170px; top: 6%; left: 24%; }
.qm-partner-hero-face.f1 { width: 120px; height: 120px; top: 0; right: 8%; }
.qm-partner-hero-face.f2 { width: 112px; height: 112px; bottom: 8%; left: 4%; }
.qm-partner-hero-face.f3 { width: 140px; height: 140px; bottom: 0; right: 20%; }
.qm-partner-hero-face.f4 { width: 88px; height: 88px; top: 40%; right: 0; }
.qm-partner-hero-face.f0 img:first-of-type { animation-duration: 7s; }
.qm-partner-hero-face.f1 img:first-of-type { animation-duration: 5.5s; animation-delay: -1.2s; }
.qm-partner-hero-face.f2 img:first-of-type { animation-duration: 6.5s; animation-delay: -.6s; }
.qm-partner-hero-face.f3 img:first-of-type { animation-duration: 5s; animation-delay: -2s; }
.qm-partner-hero-face.f4 img:first-of-type { animation-duration: 7.8s; animation-delay: -.4s; }
.qm-partner-hero-face img:first-of-type:hover { scale: 1.12; rotate: -3deg; box-shadow: 0 22px 44px rgba(0,0,0,.42); z-index: 3; }
.qm-partner-hero-face-logo { position: absolute; right: -6%; bottom: -6%; width: 32%; height: 32%; border-radius: 50%; object-fit: cover; background: #fff; border: 3px solid #fff; box-shadow: 0 4px 10px rgba(0,0,0,.25); }
.qm-partner-hero-face-flag { position: absolute; left: -6%; top: -6%; width: 30%; height: 30%; border-radius: 50%; object-fit: cover; background: #fff; border: 3px solid #fff; box-shadow: 0 4px 10px rgba(0,0,0,.25); }
@media (prefers-reduced-motion: reduce) { .qm-partner-hero-face img:first-of-type { animation: none; } }
@media (max-width: 860px) {
  .qm-partner-hero-inner { grid-template-columns: minmax(0, 1fr); }
  .qm-partner-hero-art { display: none; }
}
.qm-partner-benefit { text-align: left; }
.qm-partner-benefit-ico { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: var(--r-md); background: var(--qm-primary-50); color: var(--qm-primary); font-size: 1.25rem; margin-bottom: var(--sp-3); }
.qm-partner-benefit :is(h1,h2,h3,h4,h5,h6) { font-size: 1.05rem; margin: 0 0 6px; color: var(--ink-900); }
.qm-partner-benefit p { font-size: .92rem; color: var(--ink-500); margin: 0; }
/* Owner trust cards — classic "image-top" card: the portrait keeps its own
framing (no circle crop clipping the chef hats), bleeding to the card's own
rounded top corners; the restaurant identity is a single logo+name chip below,
and the cuisine's country flag (cuisine_flag(), same helper the storefront's
A-Z panel/restaurant cards use) sits as a small corner badge on the photo. */
.qm-partner-face-card { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); overflow: hidden; transition: transform var(--t), box-shadow var(--t); }
.qm-partner-face-card:hover { transform: translateY(-6px); box-shadow: var(--e-3); }
.qm-partner-face-photo { position: relative; display: block; aspect-ratio: 4 / 5; overflow: hidden; background: var(--surface-2); }
.qm-partner-face-photo img:first-of-type { width: 100%; height: 100%; object-fit: cover; display: block; transition: scale var(--t-slow); }
.qm-partner-face-card:hover .qm-partner-face-photo img:first-of-type { scale: 1.06; }
.qm-partner-face-flag { position: absolute; top: 10px; left: 10px; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; border: 2px solid #fff; box-shadow: 0 4px 10px rgba(0,0,0,.3); }
.qm-partner-face-body { padding: var(--sp-4) var(--sp-4) var(--sp-5); text-align: center; }
.qm-partner-face-name { display: block; font-size: 1rem; font-weight: 800; color: var(--ink-900); letter-spacing: -.01em; }
.qm-partner-face-shop { display: inline-flex; align-items: center; gap: 7px; margin-top: 8px; padding: 4px 14px 4px 4px; font-size: .76rem; font-weight: 700; color: var(--qm-primary); background: var(--qm-primary-50); border-radius: var(--r-pill); }
.qm-partner-face-shop img { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; }
@media (prefers-reduced-motion: reduce) { .qm-partner-face-photo img:first-of-type { animation: none; } }
/* Partner page: a clip of the room with the screen laid over it.
   The screen straddles the film's inner corner so the pair reads as one object instead of two
   stacked panes. Percentages are of the MEDIA COLUMN, so the overlap holds at every width
   without a breakpoint of its own. Below lg the row is a single column and an absolute screen
   would land on the copy, so it falls back to a negative-margin tuck. */
/* Service rows. The copy is a designed block beside the media, not a caption: the page's
   eyebrow, a row-level title one step under the section h2, a measured paragraph, the same
   ticked list the pricing tiers use, and the rhythm of a section head. Rows get real air. */
.qm-partner-row + .qm-partner-row { margin-top: clamp(28px, 3.5vw, 56px); }
.qm-partner-copy { padding-block: var(--sp-4); }
/* The block's measure: wide enough for a title to wrap on two lines, narrow enough to read; it
   centres in the column so both orientations keep one distance to media and page edge. */
.qm-partner-copy-inner { max-width: 500px; margin-inline: auto; }
.qm-partner-copy .qm-eyebrow { margin-bottom: 12px; }
.qm-partner-title { font-size: clamp(22px, 2vw, 27px); font-weight: 700; color: var(--ink-900); line-height: 1.2; letter-spacing: -.01em; margin: 0 0 12px; }
.qm-partner-copy p { font-size: var(--fs-600); color: var(--ink-600); line-height: 1.6; margin: 0 0 20px; }
.qm-partner-copy .qm-plan-features { gap: 12px; }
.qm-partner-copy .qm-plan-features li { font-size: .98rem; color: var(--ink-800); }
.qm-partner-media { position: relative; }
.qm-partner-shot { position: relative; z-index: 3; width: 76%; margin-top: -18%; margin-inline-start: 12%; }
@media (min-width: 992px) {
  /* The straddle spends the gutter, so the copy is inset to pay for it. Widening --bs-gutter-x
     instead was the obvious move and the wrong one: Bootstrap pays for a gutter with a negative
     row margin, which at 7rem outran the container's padding and pushed the page sideways at
     992-1100px. Padding one column costs nothing outside it. At the grid's own 48px the
     overhang landed ON the copy (measured -3px at 1600px); this holds 25px+ across 992-1600. */
  .qm-partner-copy { padding-inline: 2.5rem; }
  .qm-partner-shot { position: absolute; width: 58%; margin: 0; inset-block-end: -8%; inset-inline-start: -5%; }
  /* Rows where the media sits on the LEFT: the screen overhangs the other edge, so it always
     leans toward the copy rather than off the side of the page. Logical properties, so Arabic
     mirrors both variants without a second rule. */
  .qm-partner-media--end .qm-partner-shot { inset-inline-start: auto; inset-inline-end: -5%; }
}
.qm-pricing-row { align-items: stretch; }
.qm-plan { position: relative; height: 100%; display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--sp-6) var(--sp-5); box-shadow: var(--e-1); transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; }
.qm-plan:hover { transform: translateY(-4px); box-shadow: var(--e-3); }
.qm-plan.is-popular { border-color: var(--qm-primary); box-shadow: var(--e-3); }
/* Per-tier colour accent: a top edge keyed to the plan tier, matching the tier tag
   pill above the name. Uses the same tokens as .qm-plan-tier-* so the colour reads
   consistently from the admin tables to this pricing page. */
.qm-plan { border-top: 3px solid var(--line); }
.qm-plan[data-tier="starter"]    { border-top-color: var(--slate); }
.qm-plan[data-tier="pro"]        { border-top-color: var(--info); }
.qm-plan[data-tier="business"]   { border-top-color: var(--violet); }
.qm-plan[data-tier="enterprise"] { border-top-color: var(--tier-gold); }
.qm-plan-tier-row { margin-bottom: 6px; }
.qm-plan-flag { position: absolute; top: -12px; left: 50%; transform: translateX(-50%); background: var(--qm-grad); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 5px 14px; border-radius: var(--r-pill); box-shadow: var(--e-primary); white-space: nowrap; }
.qm-plan-name { font-size: 1.15rem; font-weight: 800; color: var(--ink-900); margin: 4px 0 8px; }
.qm-plan-price { display: flex; align-items: baseline; gap: 4px; margin-bottom: 6px; }
.qm-plan-amount { font-size: 2.2rem; font-weight: 800; color: var(--ink-900); line-height: 1; letter-spacing: -.02em; }
.qm-plan-cycle { font-size: .9rem; font-weight: 600; color: var(--ink-500); }
.qm-plan-meta { font-size: .8rem; color: var(--ink-500); margin: 0 0 var(--sp-4); padding-bottom: var(--sp-4); border-bottom: 1px solid var(--line); }
.qm-plan-features { list-style: none; margin: 0 0 var(--sp-5); padding: 0; display: flex; flex-direction: column; gap: 10px; flex: 1 1 auto; }
.qm-plan-features li { display: flex; align-items: flex-start; gap: 9px; font-size: .9rem; color: var(--ink-700); }
.qm-plan-features i { color: var(--ok); margin-top: 3px; font-size: .82em; }
.qm-pricing-note { text-align: center; margin-top: var(--sp-5); font-size: .9rem; color: var(--ink-500); }
.qm-pricing-note i { color: var(--qm-primary); }
/* Monthly/Annual cycle toggle — public pricing and (with the --d2 modifier) the
   dashboard change-plan grid. One segmented pill, the active side brand-filled. */
.qm-cycle-toggle { display: inline-flex; gap: 4px; padding: 4px; border-radius: 999px; background: #fff; border: 1px solid var(--line); box-shadow: var(--e-1); margin-top: var(--sp-4); }
.qm-cycle-toggle button { border: 0; background: transparent; border-radius: 999px; padding: 8px 18px; font-weight: 700; font-size: var(--fs-400, 13px); color: var(--ink-600); cursor: pointer; display: inline-flex; align-items: center; gap: 8px; transition: background var(--t-fast), color var(--t-fast); }
.qm-cycle-toggle button.is-active { background: var(--qm-grad); color: #fff; box-shadow: 0 6px 14px color-mix(in srgb, var(--qm-primary) 28%, transparent); }
.qm-cycle-save { font-size: 11px; font-weight: 800; padding: 2px 8px; border-radius: 999px; background: color-mix(in srgb, var(--ok) 14%, #fff); color: var(--ok); }
.qm-cycle-toggle button.is-active .qm-cycle-save { background: rgba(255, 255, 255, .22); color: #fff; }
.qm-plan-billed { display: block; margin-top: 2px; font-size: 12px; color: var(--ink-500); }
/* The undiscounted year, struck through beside the annual price — the discount
   made visible at the number itself rather than left to arithmetic. */
.qm-plan-was { display: inline-block; margin-inline-end: 8px; color: var(--ink-400); font-weight: 600; font-size: var(--fs-600, 15px); text-decoration: line-through; }
.qm-d2 .qm-billing-method-price .qm-plan-was { font-size: 12px; }
.qm-d2 .qm-cycle-toggle--d2 { display: flex; width: max-content; margin-inline: auto; margin-top: 0; box-shadow: none; }
.qm-d2 .qm-cycle-toggle--d2 button { font-size: var(--fs-400, 13px); padding: 7px 16px; }
/* The renew panel's term radios: two charge cards, the checked one ringed. */
.qm-d2 .qm-cycle-radios { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 14px; }
.qm-d2 .qm-cycle-radio { display: flex; align-items: flex-start; gap: 10px; border: 1px solid var(--d2-border); border-radius: var(--r-md); padding: 12px 14px; cursor: pointer; margin: 0; }
.qm-d2 .qm-cycle-radio:has(input:checked) { border-color: var(--qm-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 12%, transparent); }
.qm-d2 .qm-cycle-radio input { margin-top: 3px; flex: 0 0 auto; }
.qm-d2 .qm-cycle-radio span { display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-400, 13px); min-width: 0; }
.qm-d2 .qm-cycle-radio small { color: var(--ink-500, #78716c); }
@media (max-width: 575.98px) { .qm-d2 .qm-cycle-radios { grid-template-columns: 1fr; } }
.qm-partner-step { text-align: center; padding: var(--sp-4); }
.qm-partner-step-num { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; border-radius: 50%; background: var(--qm-grad); color: #fff; font-size: 1.3rem; font-weight: 800; margin-bottom: var(--sp-3); box-shadow: var(--e-primary); }
.qm-partner-step :is(h1,h2,h3,h4,h5,h6) { font-size: 1.05rem; margin: 0 0 6px; color: var(--ink-900); }
.qm-partner-step p { font-size: .92rem; color: var(--ink-500); margin: 0; }

/* ====================== BLOG ====================== */

/* ---- Blog filter bar (blog.php) ---- */
.qm-blog-filterbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); }
.qm-blog-filtermsg { margin: 0; font-size: 14px; color: var(--ink-700); }
.qm-blog-clear { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 700; color: var(--qm-primary-600); text-decoration: none; }
.qm-blog-clear:hover { color: var(--qm-primary-700); text-decoration: underline; }

/* ---- Blog list layout: shared .qm-blog-card with --row modifier ---- */
.qm-blog-list { display: flex; flex-direction: column; gap: var(--sp-4); }
.qm-blog-card--row { flex-direction: row; }
.qm-blog-card--row .qm-blog-card-media { flex: 0 0 38%; max-width: 38%; aspect-ratio: auto; align-self: stretch; }
.qm-blog-card--row .qm-blog-card-body { flex: 1 1 auto; min-width: 0; justify-content: center; }
.qm-blog-card-cat--inline { position: static; align-self: flex-start; margin-bottom: var(--sp-2); }
@media (max-width: 575.98px) {
  .qm-blog-card--row { flex-direction: column; }
  .qm-blog-card--row .qm-blog-card-media { flex-basis: auto; max-width: 100%; aspect-ratio: 16/9; }
}

/* ---- Blog detail: article footer, tags & share row (blog-detail.php) ---- */
.qm-article-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-4); margin: var(--sp-6) var(--sp-6) 0; padding: var(--sp-5) 0 var(--sp-6); border-top: 1px solid var(--line); }
.qm-tags-row { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.qm-tags-row a { text-decoration: none; }
.qm-share-row { display: flex; align-items: center; gap: var(--sp-2); }
.qm-share-label { font-weight: 700; color: var(--ink-700); font-size: 14px; }
.qm-share-btn { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%; background: var(--line-2); color: var(--ink-700); text-decoration: none; transition: background var(--t-fast), color var(--t-fast); }
.qm-share-btn:hover, .qm-share-btn:focus-visible { background: var(--qm-primary); color: #fff; }

/* ---- Blog detail: author bio card ---- */
.qm-author-card { display: flex; gap: var(--sp-4); align-items: center; margin-top: var(--sp-4); padding: var(--sp-5); }
.qm-author-avatar { flex: 0 0 auto; width: 68px; height: 68px; border-radius: 50%; overflow: hidden; background: var(--qm-grad); }
.qm-author-avatar img { width: 100%; height: 100%; object-fit: cover; }
.qm-author-eyebrow { text-transform: uppercase; letter-spacing: .06em; font-size: 12px; font-weight: 700; color: var(--qm-primary); }
.qm-author-name { font-size: 19px; margin: 2px 0 6px; color: var(--ink-900); }
.qm-author-bio { margin: 0; color: var(--ink-500); font-size: 14.5px; line-height: 1.6; }

/* ---- Blog detail: comments list ---- */
.qm-comments { margin-top: var(--sp-4); padding: var(--sp-5); }
.qm-comments-title { font-size: var(--fs-900); color: var(--ink-900); margin: 0 0 var(--sp-5); }
.qm-comments-empty { color: var(--ink-500); margin: 0; }
.qm-comment-list { display: flex; flex-direction: column; gap: var(--sp-5); }
.qm-comment { display: flex; gap: var(--sp-3); }
.qm-comment-avatar { flex: 0 0 auto; width: 46px; height: 46px; border-radius: 50%; overflow: hidden; background: var(--qm-primary); }
.qm-comment-avatar img { width: 100%; height: 100%; object-fit: cover; }
.qm-comment-avatar .qm-brandmark { width: 100%; height: 100%; border-radius: 0; font-size: 17px; }
.qm-comment-body { flex: 1 1 auto; min-width: 0; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); }
.qm-comment-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px var(--sp-3); }
.qm-comment-author { font-weight: 700; color: var(--ink-900); }
.qm-comment-date { color: var(--ink-500); font-size: 12.5px; }
.qm-comment-text { margin: 6px 0 8px; color: var(--ink-700); font-size: 14.5px; line-height: 1.55; }
.qm-comment-reply { background: none; border: 0; padding: 0; color: var(--qm-primary); font-weight: 600; font-size: 13.5px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.qm-comment-reply:hover { text-decoration: underline; }
.qm-comment-replies { margin: var(--sp-5) 0 0 var(--sp-8); display: flex; flex-direction: column; gap: var(--sp-5); padding-left: var(--sp-4); border-left: 2px solid var(--line); }
@media (max-width: 575.98px) { .qm-comment-replies { margin-left: var(--sp-4); } }

/* ---- Blog detail: comment form ---- */
.qm-comment-form { margin-top: var(--sp-4); padding: var(--sp-5); }
.qm-replying-to { background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); color: var(--ink-700); padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md); font-size: 14px; display: flex; align-items: center; gap: var(--sp-3); }
.qm-cancel-reply { background: none; border: 0; color: var(--qm-primary); font-weight: 600; cursor: pointer; padding: 0; font-size: 13.5px; }
.qm-field-label { display: block; font-weight: 600; color: var(--ink-900); margin-bottom: 6px; font-size: 14px; }
.qm-field-label .qm-req { color: var(--qm-primary); }
.qm-optional { color: var(--ink-500); font-weight: 400; font-size: 12.5px; }
.qm-input { width: 100%; padding: 10px 14px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: var(--ink-900); font-size: var(--fs-600); }
.qm-input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-50); }
.qm-form-note { margin: 0 0 var(--sp-3); color: var(--ink-500); font-size: var(--fs-400); line-height: 1.5; }

/* ---- Blog detail: inline comment-feedback status block uses the canonical
.qm-form-status / --ok / --err defined in the ABOUT/CONTACT section above. ---- */

/* ---- Blog detail: prev / next post nav ---- */
.qm-post-nav { display: flex; justify-content: space-between; gap: var(--sp-4); margin-top: var(--sp-4); }
.qm-post-nav-link { flex: 1 1 0; max-width: 48%; text-decoration: none; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); display: flex; flex-direction: column; gap: 5px; transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.qm-post-nav-link:hover { border-color: var(--qm-primary); box-shadow: var(--e-3); }
.qm-post-nav-next { text-align: right; }
.qm-post-nav-dir { color: var(--qm-primary); font-weight: 700; font-size: 12.5px; text-transform: uppercase; letter-spacing: .04em; }
.qm-post-nav-title { color: var(--ink-900); font-weight: 600; font-size: 14.5px; }
@media (max-width: 575.98px) { .qm-post-nav { flex-direction: column; } .qm-post-nav-link { max-width: 100%; } }

/* ---- Blog sidebar (partials/blog-sidebar.php) ---- */
.qm-blog-aside .qm-aside-block { padding: var(--sp-5); margin-bottom: var(--sp-4); }
.qm-blog-aside .qm-aside-block:last-child { margin-bottom: 0; }
.qm-aside-title { font-size: 17px; font-weight: 700; color: var(--ink-900); margin: 0 0 var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line); }
.qm-aside-search { display: flex; gap: var(--sp-2); }
.qm-aside-search input { flex: 1 1 auto; min-width: 0; padding: 9px 13px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: var(--ink-900); font-size: 14px; }
.qm-aside-search input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-50); }
.qm-aside-cats { list-style: none; margin: 0; padding: 0; }
.qm-aside-cats li + li { margin-top: var(--sp-1); }
.qm-aside-cats a { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); padding: 8px 10px; border-radius: var(--r-md); color: var(--ink-700); text-decoration: none; font-weight: 600; font-size: 14.5px; transition: background var(--t-fast), color var(--t-fast); }
.qm-aside-cats a:hover, .qm-aside-cats a.is-active { background: var(--qm-primary-50); color: var(--qm-primary); }
.qm-aside-recent { list-style: none; margin: 0; padding: 0; }
.qm-aside-recent li + li { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.qm-aside-recent a { display: flex; gap: var(--sp-3); text-decoration: none; }
.qm-aside-thumb { flex: 0 0 auto; }
.qm-aside-thumb img { width: 64px; height: 64px; object-fit: cover; border-radius: var(--r-md); display: block; }
.qm-aside-recent-body { display: flex; flex-direction: column; justify-content: center; gap: 3px; min-width: 0; }
.qm-aside-recent-title { color: var(--ink-900); font-weight: 600; font-size: 14px; line-height: 1.35; }
.qm-aside-recent a:hover .qm-aside-recent-title { color: var(--qm-primary); }
.qm-aside-recent-date { color: var(--ink-500); font-size: 12.5px; }
.qm-aside-empty { color: var(--ink-500); font-size: 14px; }
.qm-aside-news-copy { margin: 0 0 var(--sp-3); color: var(--ink-500); font-size: 13.5px; line-height: 1.55; }
.qm-aside-news-form { display: flex; gap: var(--sp-2); }
.qm-aside-news-form input { flex: 1 1 auto; min-width: 0; padding: 9px 13px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: var(--ink-900); font-size: 14px; }
.qm-aside-news-form input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-50); }

/* ---- Blog toolbar lead (blog.php). The grid-vs-two-sidebars
choice is an admin setting (Admin -> Settings -> Blog layout), not a
front-end visitor toggle, so no toggle UI lives here. ---- */
.qm-blog-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.qm-blog-toolbar-lead { margin: 0; color: var(--ink-500); font-size: 14.5px; }

/* (Removed the grid2 blog style — .qm-blog-2col + .qm-blog-popular + .qm-popular-* were used
   only by the deleted blog-grid-2.php / blog-sidebar-popular.php. Shared blog classes kept.) */

/* ---- Right "Featured" card (partials/blog-sidebar.php featured widget) ---- */
.qm-blog-aside .qm-aside-featured { padding: 0; overflow: hidden; }
.qm-featured-card { display: block; text-decoration: none; }
.qm-featured-media { position: relative; display: block; aspect-ratio: 16/10; overflow: hidden; background: var(--line-2); }
.qm-featured-media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--t-slow); }
.qm-featured-card:hover .qm-featured-media img { transform: scale(1.05); }
.qm-featured-flag { position: absolute; top: var(--sp-3); left: var(--sp-3); background: var(--qm-primary-600); color: #fff; font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; padding: 4px 11px; border-radius: var(--r-pill); }
.qm-featured-body { display: block; padding: var(--sp-4) var(--sp-5) var(--sp-5); }
.qm-featured-cat { display: inline-block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--qm-primary); margin-bottom: 6px; }
.qm-featured-title { display: block; font-size: var(--fs-700); font-weight: 700; line-height: 1.4; color: var(--ink-900); margin-bottom: 6px; }
.qm-featured-card:hover .qm-featured-title { color: var(--qm-primary); }
.qm-featured-date { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-400); }
.qm-featured-date i { color: var(--qm-primary); }

/* ---- Tags cloud (partials/blog-sidebar.php tags widget) ---- */
.qm-aside-tags { display: flex; flex-wrap: wrap; gap: 6px; }

/* ====================== CART / CHECKOUT ====================== */

/* ---- Guest note (checkout.php) ---- */
.qm-guest-note { background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); color: var(--ink-700); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-4); font-size: 14px; }
.qm-guest-note a { color: var(--qm-primary-600); font-weight: 700; }

/* ---- Tip chips (checkout.php) ---- */
.qm-tip-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.qm-tip-chip { position: relative; cursor: pointer; margin: 0; }
.qm-tip-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.qm-tip-chip span { display: inline-flex; flex-direction: column; align-items: center; line-height: 1.2; border: 1.5px solid var(--line); border-radius: var(--r-md); padding: 9px 18px; font-weight: 700; font-size: 14px; color: var(--ink-700); transition: border-color .15s, background .15s, color .15s; }
.qm-tip-chip small { font-weight: 600; font-size: 11px; color: var(--ink-400); margin-top: 2px; }
.qm-tip-chip input:checked + span { border-color: var(--qm-primary); background: var(--qm-primary-50); color: var(--qm-primary-600); }
.qm-tip-chip input:focus-visible + span { outline: 2px solid var(--qm-primary); outline-offset: 2px; }

/* ---- Checkout loyalty redeem block (checkout.php) ---- */
.qm-checkout-loyalty { border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); background: linear-gradient(180deg, #fff, var(--qm-primary-50)); }
.qm-checkout-loyalty-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); font-weight: 700; font-size: 14px; color: var(--ink-900); margin-bottom: var(--sp-3); }
.qm-checkout-loyalty-head i { color: var(--qm-primary); }
.qm-loyalty-rewards { display: flex; flex-direction: column; gap: var(--sp-2); }
.qm-loyalty-reward { display: flex; align-items: flex-start; gap: var(--sp-3); border: 1.5px solid var(--line); border-radius: var(--r-sm); padding: 9px 11px; cursor: pointer; margin: 0; transition: border-color .15s, background .15s; }
.qm-loyalty-reward input { margin-top: 3px; flex: 0 0 auto; accent-color: var(--qm-primary); }
.qm-loyalty-reward:has(input:checked) { border-color: var(--qm-primary); background: #fff; }
.qm-loyalty-reward.is-locked { opacity: .62; cursor: not-allowed; }
.qm-loyalty-reward-body { display: flex; flex-direction: column; line-height: 1.35; }
.qm-loyalty-reward-body strong { font-size: 13.5px; color: var(--ink-900); }
.qm-loyalty-reward-body strong i { color: var(--qm-primary); font-size: .9em; margin-right: 3px; }
.qm-loyalty-reward-body small { font-size: 12px; color: var(--ink-500); }
.qm-order-reward span:last-child { color: var(--qm-primary); font-weight: 700; }

/* ---- Disabled / neutralized order buttons ---- */
#place-order-btn[disabled] { opacity: .55; cursor: not-allowed; box-shadow: none; }
.qm-btn.is-disabled { opacity: .55; cursor: not-allowed; box-shadow: none; pointer-events: none; }

/* ---- Cart table horizontal-scroll safety wrapper ---- */
.qm-cart-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---- Coupon apply row + feedback (cart/checkout) ---- */
.qm-coupon-row { display: flex; gap: var(--sp-2); align-items: stretch; }
.qm-coupon-row .form-control { flex: 1 1 auto; }
.qm-coupon-row .qm-btn { flex: 0 0 auto; }
.qm-coupon-note { font-size: 12.5px; margin-top: 6px; color: var(--ink-500); }
.qm-coupon-note.is-ok { color: var(--ok); font-weight: 600; }
.qm-coupon-note.is-error { color: var(--danger); font-weight: 600; }

/* ---- Order-summary heading hierarchy + muted-meta helpers ---- */
.qm-order-summary-title { font-size: 17px; font-weight: 700; color: var(--ink-900); margin-bottom: var(--sp-3); }
/* The single helper/hint style. Three classes previously served this one role at
   two sizes and two colours (.qm-hint 12.5px/ink-400, .qm-help-text 13px/ink-500,
   .form-text unthemed Bootstrap grey) — the signature of separate authoring
   sessions rather than a design decision. All 176 sites now land here.
   12.5px was drift, not optical tuning: it appeared at weights 400, 600 and 800
   and on uppercase text, correlating with nothing, and carried no note in a file
   that annotates every WCAG ratio. */
.qm-hint { font-size: var(--fs-400); color: var(--ink-400); margin: 6px 0 0; }
.qm-h2-sub { font-size: var(--fs-400); font-weight: 400; }

/* ---- Info tooltip ------------------------------------------------------------
   A primary-colour info marker that reveals its note on hover/focus, sitting beside
   a heading where the guidance is secondary to the page itself (replaces an always-on
   .qm-hint line). CSS-only: the note is read from [data-qm-tip] and mirrored into the
   trigger's aria-label. Reusable — same treatment for every "icon + information" spot. */
.qm-tip { position: relative; display: inline-flex; align-items: center; padding: 0; border: 0; background: none; font: inherit; color: var(--ink-400); cursor: help; line-height: 1; transition: color .14s ease; }
.qm-tip::after {
  content: attr(data-qm-tip);
  position: absolute; top: calc(100% + 9px); left: 50%; transform: translateX(-50%) translateY(4px);
  z-index: 40; width: max-content; max-width: 260px; white-space: normal; text-align: left;
  padding: 8px 11px; border-radius: var(--r-sm);
  background: var(--ink-900); color: #fff; font-size: var(--fs-400); font-weight: 400; line-height: 1.45;
  box-shadow: 0 10px 26px rgba(31, 11, 18, .28);
  opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .14s ease, transform .14s ease;
}
.qm-tip::before {
  content: ""; position: absolute; top: calc(100% + 4px); left: 50%; transform: translateX(-50%);
  z-index: 41; border: 5px solid transparent; border-bottom-color: var(--ink-900);
  opacity: 0; visibility: hidden; transition: opacity .14s ease;
}
.qm-tip:hover::after, .qm-tip:focus-visible::after { opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0); }
.qm-tip:hover::before, .qm-tip:focus-visible::before { opacity: 1; visibility: visible; }
.qm-tip:hover, .qm-tip:focus-visible { color: var(--ink-600); }
.qm-tip:focus-visible { outline: 2px solid var(--ink-500); outline-offset: 3px; border-radius: 50%; }
/* In a modal the note opens UPWARD: the footer sits right under the last rows, so a downward
   bubble was cut off mid-sentence. Above the marker there is always the modal's own body. */
.modal .modal-body .qm-tip::after { top: auto; bottom: calc(100% + 9px); transform: translateX(-50%) translateY(-4px); }
.modal .modal-body .qm-tip:hover::after, .modal .modal-body .qm-tip:focus-visible::after { transform: translateX(-50%) translateY(0); }
.modal .modal-body .qm-tip::before { top: auto; bottom: calc(100% + 4px); border-bottom-color: transparent; border-top-color: var(--ink-900); }
/* On a phone the bubble is wider than the space beside its marker, and a centred 260px note
   hanging off an icon near the card's right edge pushed the whole dashboard 34px wider than
   the viewport — on every settings page, because the marker sits in a card head there.
   Below this tier it hangs from the marker's right edge instead, which keeps the note on
   screen; the arrow follows it. Centred everywhere there is room, as designed. */
@media (max-width: 640px) {
  .qm-tip::after { left: auto; right: 0; transform: translateY(4px); }
  .qm-tip:hover::after, .qm-tip:focus-visible::after { transform: translateY(0); }
  .qm-tip::before { left: auto; right: 6px; transform: none; }
  .modal .modal-body .qm-tip::after { transform: translateY(-4px); }
  .modal .modal-body .qm-tip:hover::after, .modal .modal-body .qm-tip:focus-visible::after { transform: translateY(0); }
}

/* ---- Item-analytics fact strip (first/last sold, repeat, cancelled) ---- */
.qm-stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); }
/* Label over value, matching the KPI cards above it - these are the same kind of
   fact, just secondary, so they reuse .qm-kpi-label rather than a private style. */
.qm-stat-fact { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
@media (max-width: 700px) { .qm-stat-row { grid-template-columns: repeat(2, 1fr); } }

/* ---- Team activity: clickable leaderboard cell + recent-activity log ---- */
.qm-tbl-link { color: inherit; text-decoration: none; }
.qm-tbl-link:hover { color: var(--qm-primary); }
.qm-tbl-link:hover .qm-tbl-id-text strong { text-decoration: underline; }
/* Capped + scrollable (matches .qm-notif-list's convention) so a long feed
   doesn't grow unbounded and stretch its h-100 sibling card into dead space. */
.qm-activity-log { list-style: none; margin: 0; padding: 0; max-height: 380px; overflow-y: auto; }
.qm-activity-item { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; border-top: 1px solid var(--line); }
.qm-activity-item:first-child { border-top: 0; }
.qm-activity-ico { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  background: var(--qm-primary-50); color: var(--qm-primary); font-size: 14px; }

/* ---- Quill rich-text editor (blog post body), themed to the form system ---- */
.ql-toolbar.ql-snow { border-color: var(--field-border); border-top-left-radius: var(--r-sm); border-top-right-radius: var(--r-sm); background: var(--surface-2); }
.ql-container.ql-snow { border-color: var(--field-border); border-bottom-left-radius: var(--r-sm); border-bottom-right-radius: var(--r-sm); font-family: inherit; font-size: 14px; }
.ql-editor { min-height: 220px; color: var(--ink-900); }
.ql-editor.ql-blank::before { color: var(--ink-400); font-style: normal; }
.ql-snow .ql-stroke { stroke: var(--ink-600); }
.ql-snow .ql-fill, .ql-snow .ql-stroke.ql-fill { fill: var(--ink-600); }
.ql-snow .ql-picker { color: var(--ink-600); }
.ql-snow.ql-toolbar button:hover .ql-stroke, .ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke: var(--qm-primary); }
.ql-snow.ql-toolbar button:hover .ql-fill, .ql-snow.ql-toolbar button.ql-active .ql-fill { fill: var(--qm-primary); }
.ql-snow.ql-toolbar button:hover, .ql-snow.ql-toolbar button.ql-active { color: var(--qm-primary); }
.ql-snow a { color: var(--qm-primary); }

/* ---- Unified Leaflet map (delivery zones, tracking, location pickers) ---- */
.qm-map { height: 360px; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); background: var(--surface-2); position: relative; z-index: 0; }
/* Connecting route drawn through map markers (e.g. a pickup -> drop-off delivery
   line). Leaflet renders it as an SVG <path>; author CSS wins over its presentation
   attributes, so the brand colour applies here. */
.qm-map-route-line { stroke: var(--qm-primary, #ff003d); }
.leaflet-container { font: inherit; background: var(--surface-2); }
/* Local zero-DNS base: a soft, map-like grid so the map reads as a map with no tiles. */
.qm-map-local .leaflet-container {
  background-color: #eaf1ee;
  background-image:
  linear-gradient(rgba(120, 90, 100, .07) 1px, transparent 1px),
  linear-gradient(90deg, rgba(120, 90, 100, .07) 1px, transparent 1px);
  background-size: 26px 26px;
}
/* On-brand teardrop marker (DivIcon) - never relies on Leaflet's default PNG. */
.qm-map-pin-wrap { background: none; border: 0; }
.qm-map-pin { display: grid; place-items: center; width: 28px; height: 28px; color: #fff;
  background: var(--pin, var(--qm-primary)); border: 2px solid #fff;
  border-radius: 50% 50% 50% 0; transform: rotate(-45deg);
  box-shadow: 0 4px 10px rgba(78, 8, 29, .35); }
.qm-map-pin > i { transform: rotate(45deg); font-size: 12px; }
/* Brandmark map marker: a restaurant logo / customer avatar (or its initials monogram)
   framed as a circular pin with a white ring, shadow and a small pointer tail.
   Selector is prefixed with .leaflet-marker-pane (not just .qm-map-brand) so its
   specificity beats leaflet.css's own ".leaflet-marker-pane img { width: auto }"
   tile-image reset - otherwise a non-square brandmark image (e.g. the admin
   general-fallback banner) keeps its native aspect ratio and stretches the pin
   sideways instead of being cropped to a 40x40 circle. */
.qm-map-brand-wrap { background: none; border: 0; }
.qm-map-brand { position: relative; display: block; width: 40px; height: 40px; filter: drop-shadow(0 3px 5px rgba(0, 0, 0, .4)); }
.leaflet-marker-pane .qm-map-brand .qm-brandmark { width: 40px; height: 40px; border-radius: 50%; box-shadow: 0 0 0 3px #fff; }
.qm-map-brand::after { content: ""; position: absolute; left: 50%; bottom: -4px; width: 12px; height: 12px; background: #fff; transform: translateX(-50%) rotate(45deg); border-radius: 0 0 3px 0; z-index: -1; }
/* Themed Leaflet chrome */
.leaflet-bar a, .leaflet-bar a:hover { color: var(--ink-700); border-bottom-color: var(--line); }
.leaflet-control-attribution { font-size: 10px; background: rgba(255, 255, 255, .75); color: var(--ink-400); }
.leaflet-touch .leaflet-bar a { border-radius: 0; }
.leaflet-bar { border: 1px solid var(--line); box-shadow: var(--e-1); border-radius: var(--r-sm); overflow: hidden; }
/* Zoom +/- — branded controls instead of the library's monospace defaults. */
.leaflet-bar a, .leaflet-touch .leaflet-bar a { width: 34px; height: 34px; line-height: 34px; background: var(--surface); color: var(--ink-600); font-family: var(--f-sans); font-weight: 700; font-size: var(--fs-700); border-bottom: 1px solid var(--line-2); transition: background var(--t-fast), color var(--t-fast); }
.leaflet-bar a:last-child { border-bottom: 0; }
.leaflet-bar a:hover { background: var(--qm-primary-50); color: var(--qm-primary-600); }
.leaflet-bar a.leaflet-disabled { background: var(--surface-2); color: var(--ink-300); }
/* Popups + tooltips follow the card language. */
.leaflet-popup-content-wrapper { border-radius: var(--r-md); box-shadow: var(--e-3); border: 1px solid var(--line); font-family: var(--f-sans); }
.leaflet-popup-content { font-size: var(--fs-400); color: var(--ink-700); }
.leaflet-popup-tip { box-shadow: var(--e-1); }
.leaflet-tooltip { border-radius: var(--r-sm); border: 1px solid var(--line); box-shadow: var(--e-2); font-family: var(--f-sans); font-size: 12px; color: var(--ink-800); }
/* The radius drag-handle for circle editing */
.qm-map-handle { background: none; border: 0; }
.qm-map-handle span { display: block; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; border: 3px solid var(--qm-primary); box-shadow: 0 2px 6px rgba(78, 8, 29, .3); cursor: ew-resize; }
/* Tier legend below the editor map */
.qm-zone-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 12px; }
.qm-zone-legend-item { display: flex; align-items: center; gap: 7px; font-size: var(--fs-400); color: var(--ink-700); }
.qm-zone-swatch { width: 14px; height: 14px; border-radius: 4px; flex: 0 0 auto; }

/* ---- Navigation builder: drag-to-reorder affordance (Admin -> Navigation) ----
   Purely an affordance. The server reads the numbered Order boxes, so the screen
   reorders correctly with scripting off and none of this applied. */
.qm-table tr.is-dragging { opacity: .45; }
.qm-icon-btn[data-nav-handle] { cursor: grab; }
.qm-icon-btn[data-nav-handle]:active { cursor: grabbing; }

/* ---- SweetAlert2 confirm dialogs + toasts, themed to the design system ---- */
.swal2-container { z-index: 20000; }
/* Hold the sticky rails in place while a dialog owns the page.
   Opening a dialog locks the page with `body { overflow: hidden }`, which is the
   right behaviour for a modal but also stops the body being the scroll container.
   A `position: sticky` element resolves against its scrolling ancestor, so with
   that gone it silently falls back to its static position -- one scroll-offset
   above the viewport -- and the sidebar appears clipped at the top with dead space
   below. Shifting these rails back by exactly that offset (published as
   --qm-scroll-lock by qm-confirm.js) leaves them where the reader left them.
   Only rails that stick to the PAGE are listed; anything sticky inside its own
   scrolling box (the floor-plan timeline, the cuisine index) is unaffected.
   The :not() pair mirrors SweetAlert's own lock rule exactly -- a TOAST also carries
   swal2-shown but locks nothing, so compensating for it shifted every rail DOWN by the
   scroll offset instead of holding it still. Kept in step with qm-confirm.js. */
/* The rail-shifting compensation that used to live here is GONE, and must not come back.
   It existed only because the page was locked on <body>, which knocks every sticky rail
   loose; the lock now sits on <html> (see html.qm-scroll-lock) so the rails never move
   in the first place. Compensating for a lock is always a sign the lock is on the wrong
   element -- and it only ever covered SweetAlert, leaving the POS modals, the customize
   popup and the lightbox visibly broken. */
.swal2-popup.qm-swal { border-radius: var(--r-lg); box-shadow: 0 26px 58px rgba(78, 8, 29, .24); font-family: inherit; padding: 28px 26px; }
.qm-swal .swal2-title { font-size: var(--fs-900); font-weight: 700; color: var(--ink-900); padding-top: 4px; }
.qm-swal .swal2-html-container, .qm-swal-text { font-size: 14.5px; color: var(--ink-600); line-height: 1.5; }
.qm-swal .swal2-actions { gap: 10px; margin-top: 20px; }
.qm-swal .qm-btn { min-width: 130px; box-shadow: none; }
.qm-swal .swal2-icon { margin-top: 8px; }
/* Recolour SweetAlert's stock icons into the brand palette */
.qm-swal .swal2-icon.swal2-warning,
.qm-swal .swal2-icon.swal2-error,
.qm-swal .swal2-icon.swal2-info,
.qm-swal .swal2-icon.swal2-question { border-color: color-mix(in srgb, var(--qm-primary) 40%, transparent); color: var(--qm-primary); }
.qm-swal .swal2-icon.swal2-error [class^='swal2-x-mark-line'] { background-color: var(--qm-primary); }
.qm-swal .swal2-icon.swal2-success { border-color: color-mix(in srgb, var(--ok) 40%, transparent); color: var(--ok); }
.qm-swal .swal2-icon.swal2-success [class^='swal2-success-line'] { background-color: var(--ok); }
.qm-swal .swal2-icon.swal2-success .swal2-success-ring { border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
/* Toast variant - a compact branded card */
/* Hold the toast clear of the fixed 68px site header it shares this corner with.
   SweetAlert anchors a top-end toast at inset:0 and its container is 360px wide, so on a
   phone the card spans the whole width and lands squarely in the header band -- and the
   card carries pointer-events:all at z-index 20000, so it does not merely overlap, it
   hit-tests ABOVE the header: burger, cart, account, location and logo all stopped
   responding for as long as a toast was up, and at 390px the burger is the only route
   into the navigation. .625em is SweetAlert's own container padding, kept. Raising
   .header past 20000 instead would float the site header over genuine modal dialogs. */
.swal2-container.qm-swal-toast-wrap { padding-top: calc(68px + .625em); }
.swal2-popup.qm-swal.qm-swal-toast { padding: 14px 16px; border-radius: var(--r-md); border: 1px solid var(--line); box-shadow: 0 12px 28px rgba(78, 8, 29, .16); }
.qm-swal-toast .swal2-title { font-size: 13.5px; font-weight: 600; color: var(--ink-800); margin: 0; }
.qm-swal-toast .swal2-timer-progress-bar { background: var(--qm-primary); }
.qm-tip-lead { margin-top: -6px; }
.qm-input-narrow { max-width: 200px; }
/* A single-digit field — a choice group's min/max — sitting in a row whose text field grows.
   Narrower than .qm-input-narrow, which is sized for a price. */
.qm-mg-num { flex: 0 0 88px; }

/* ---- Order-track local map (colors via classes for brand re-theme) ---- */
.qm-map-route { stroke: var(--qm-primary); }

/* ---- Order-track timeline state badge (text + icon, not color-only) ---- */
.qm-tl-status { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.qm-tl-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: var(--r-pill); }
.qm-tl-badge i { font-size: .9em; }
.qm-tl-badge-done { background: var(--ok-bg); color: var(--ok); }
.qm-tl-badge-current { background: var(--qm-primary-50); color: var(--qm-primary-600); }
.qm-tl-badge-upcoming { background: #eef0f2; color: #5c6268; }

/* ---- Order-track rider bob (CSS animation; JS adds .is-moving only when motion allowed) ---- */
@keyframes qm-rider-bob { 0%, 100% { transform: translate(-50%, -50%); } 50% { transform: translate(-50%, calc(-50% - 4px)); } }
.qm-track-driver.is-moving { animation: qm-rider-bob .9s ease-in-out infinite; }

/* ====================== AUTH ====================== */

/* ---- Shared inline-validation error (auth.js + add-restaurant.js) ---- */
.ar-field-error { color: var(--danger); font-size: 12.5px; font-weight: 600; margin: var(--sp-1) 0 0; }

/* ---- Toggle-gated field groups: a checkbox's dependent fields dim (but stay
   editable) while the toggle is off, so an unchecked feature doesn't look
   identical to an active one. Purely visual - inputs still submit normally. ---- */
.qm-toggle-fields { transition: opacity var(--t-fast); }
.qm-toggle-fields.is-off { opacity: .5; }

/* ---- Password show/hide ---- */
.qm-pw-wrap { position: relative; }
.qm-pw-wrap .form-control { padding-right: 44px; }
.qm-pw-toggle { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); width: 34px; height: 34px; display: inline-flex; align-items: center; justify-content: center; background: transparent; border: 0; color: var(--ink-400); cursor: pointer; border-radius: var(--r-sm); }
.qm-pw-toggle:hover { color: var(--qm-primary); }
.qm-pw-toggle:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }

/* ---- Auth hints ---- */
.qm-auth-hint { font-size: 12.5px; color: var(--ink-400); margin: var(--sp-1) 0 0; }
.qm-auth-hint-center { text-align: center; margin: var(--sp-2) 0 var(--sp-1); }

/* ---- Login demo-account picker sub-elements ---- */
.qm-demo-group { margin-bottom: var(--sp-3); }
.qm-demo-group-label { display: block; font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-400); margin: 0 2px var(--sp-2); }
.qm-demo-btn-ico { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary-50); color: var(--qm-primary); font-size: var(--fs-700); }
.qm-demo-btn:hover .qm-demo-btn-ico { background: var(--qm-primary); color: #fff; }
.qm-demo-btn-body { display: flex; flex-direction: column; min-width: 0; }
.qm-demo-btn-name { font-size: 13.5px; font-weight: 700; color: var(--ink-900); line-height: 1.2; }
.qm-demo-btn-desc { font-size: 11.5px; color: var(--ink-500); line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* ---- Add-restaurant multistep (.ar-*) ---- */
.ar-hero-title { margin-top: var(--sp-2); }
.ar-hero-cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-5) 0 var(--sp-4); }
.ar-hero-points { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-2); }
.ar-hero-points li { display: flex; align-items: center; gap: var(--sp-2); color: var(--ink-700); font-weight: 600; }
.ar-hero-points i { color: var(--qm-primary); }
.ar-hero-media { border-radius: var(--r-md); overflow: hidden; box-shadow: var(--e-2); }
.ar-hero-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.ar-block-head { max-width: 640px; margin: 0 auto var(--sp-6); }
.ar-benefit { height: 100%; padding: var(--sp-5); text-align: center; }
.ar-benefit-icon { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; border-radius: var(--r-md); background: var(--qm-primary-50); color: var(--qm-primary); font-size: 1.4rem; margin-bottom: var(--sp-3); }
.ar-benefit-title { font-size: 1.05rem; font-weight: 700; margin-bottom: var(--sp-2); color: var(--ink-900); }
.ar-benefit-text { color: var(--ink-500); margin: 0; font-size: .92rem; }
.ar-how-step { height: 100%; padding: var(--sp-6) var(--sp-5); text-align: center; position: relative; }
.ar-how-num { position: absolute; top: var(--sp-3); right: var(--sp-4); font-size: 2.4rem; font-weight: 800; color: color-mix(in srgb, var(--qm-primary) 12%, transparent); line-height: 1; }
.ar-how-icon { display: inline-flex; align-items: center; justify-content: center; width: 64px; height: 64px; border-radius: 50%; background: var(--qm-primary); color: #fff; font-size: 1.5rem; margin-bottom: var(--sp-4); }
.ar-how-title { font-size: 1.1rem; font-weight: 700; margin-bottom: var(--sp-2); color: var(--ink-900); }
.ar-how-text { color: var(--ink-500); margin: 0; }
.ar-plan { height: 100%; padding: var(--sp-6) var(--sp-5); text-align: center; position: relative; display: flex; flex-direction: column; }
.ar-plan-featured { border: 2px solid var(--qm-primary); box-shadow: var(--e-2); }
.ar-plan-flag { position: absolute; top: -14px; left: 50%; transform: translateX(-50%); }
.ar-plan-name { font-size: 1.25rem; font-weight: 700; color: var(--ink-900); margin-bottom: var(--sp-3); }
.ar-plan-price { margin-bottom: var(--sp-2); }
.ar-plan-amount { font-size: 2.2rem; font-weight: 800; color: var(--qm-primary); }
.ar-plan-cycle { color: var(--ink-500); font-weight: 600; }
.ar-plan-commission { color: var(--ink-700); margin-bottom: var(--sp-4); font-size: .92rem; }
.ar-plan-features { list-style: none; padding: 0; margin: 0 0 var(--sp-5); text-align: left; display: grid; gap: var(--sp-2); flex: 1; }
.ar-plan-features li { display: flex; align-items: flex-start; gap: var(--sp-2); color: var(--ink-700); font-size: .92rem; }
.ar-plan-features i { color: var(--qm-primary); margin-top: 3px; }
.ar-stepper { list-style: none; padding: 0; margin: 0 0 var(--sp-6); display: flex; justify-content: space-between; gap: var(--sp-2); position: relative; }
.ar-stepper::before { content: ""; position: absolute; top: 18px; left: 12%; right: 12%; height: 2px; background: var(--line); z-index: 0; }
.ar-stepper-item { display: flex; flex-direction: column; align-items: center; gap: var(--sp-2); flex: 1; position: relative; z-index: 1; text-align: center; }
.ar-stepper-dot { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; border-radius: 50%; background: var(--surface); border: 2px solid var(--line); color: var(--ink-500); font-weight: 700; transition: all .2s ease; }
.ar-stepper-item.is-active .ar-stepper-dot, .ar-stepper-item.is-done .ar-stepper-dot { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.ar-stepper-label { font-size: .82rem; font-weight: 600; color: var(--ink-500); }
.ar-stepper-item.is-active .ar-stepper-label { color: var(--ink-900); }
.ar-step-status { font-size: .82rem; font-weight: 600; color: var(--ink-500); text-align: center; margin: 0 0 var(--sp-4); }
.ar-step { display: none; border: 0; padding: 0; margin: 0; min-inline-size: auto; }
.ar-step.is-active { display: block; }
.ar-step-title { font-size: 1.1rem; font-weight: 700; color: var(--ink-900); margin-bottom: var(--sp-4); padding: 0; }
.ar-field { margin-bottom: var(--sp-4); }
.ar-step-nav { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); margin-top: var(--sp-5); }
.ar-plan-choices { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.ar-plan-choice { display: block; cursor: pointer; margin: 0; }
.ar-plan-choice input { position: absolute; opacity: 0; pointer-events: none; }
.ar-plan-choice-body { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-3); padding: var(--sp-4); border: 2px solid var(--line); border-radius: var(--r-md); transition: border-color .15s ease, background .15s ease; }
.ar-plan-choice input:checked + .ar-plan-choice-body { border-color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 4%, transparent); }
.ar-plan-choice input:focus-visible + .ar-plan-choice-body { outline: 2px solid var(--qm-primary); outline-offset: 2px; }
.ar-plan-choice-name { font-weight: 700; color: var(--ink-900); }
.ar-plan-choice-price { font-weight: 700; color: var(--qm-primary); }
.ar-plan-choice-price small { color: var(--ink-500); font-weight: 600; }
.ar-form-note { color: var(--ink-500); font-size: .9rem; display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.ar-form-note i { color: var(--qm-primary); }
.ar-terms { display: flex; align-items: flex-start; gap: var(--sp-3); color: var(--ink-700); font-size: .92rem; margin-bottom: var(--sp-2); padding: 2px 0; cursor: pointer; }
/* A 13px box beside a full-width primary button is a mis-click waiting to happen. */
.ar-terms input { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; accent-color: var(--qm-primary); cursor: pointer; }
/* Terms and privacy policy inherited the sentence colour and carried no underline, so
   nothing marked them as links. They are the two things a signer is entitled to read. */
.ar-terms a { color: var(--qm-primary); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
.ar-terms a:hover { color: var(--qm-primary-600, #d10032); }
/* Room between consenting and committing. It was 16px, and people were hitting the button
   while reaching for the checkbox. */
.ar-submit { margin-top: 30px; }
.ar-signin { text-align: center; margin-top: var(--sp-5); color: var(--ink-500); }

/* ====================== CUSTOMER ACCOUNT ====================== */

/* ---- Shared small utilities (account/edit/payment views) ---- */
/* Font-size-only small utility — sets NO color, so a Bootstrap alert (demo-mode
warning in admin/settings.php) keeps its own warning color and margins. */
.qm-text-sm { font-size: var(--fs-400); }
.qm-avatar-img { border-radius: 50%; object-fit: cover; }
.qm-mt-3 { margin-top: var(--sp-3); }
.qm-mt-4 { margin-top: var(--sp-4); }
.qm-btn-danger { background: #fff; color: var(--danger); border-color: var(--danger); box-shadow: none; }
.qm-btn-danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); }

/* ---- Customer profile avatar field (account.php) ---- */
.qm-avatar-field { display: flex; align-items: flex-start; gap: var(--sp-3); }
.qm-avatar-field .qm-avatar { flex: 0 0 auto; border: 1px solid var(--line); }
.qm-avatar-field > div { flex: 1 1 auto; min-width: 0; }

/* ---- Shared admin/vendor account edit (account/edit.php) ---- */
/* No width cap: the .qm-settings grid's panel column already sets the measure, and
   the notification card sharing that column has never had one - capping only this
   card left a ragged gap down the right of the page. */
.qm-account-edit .qm-avatar-row { display: flex; align-items: center; gap: var(--sp-4); margin-bottom: var(--sp-4); }
.qm-account-edit .qm-avatar-field { flex: 1 1 auto; min-width: 0; }
/* The avatar preview is a fixed round photo that never gets squeezed by the adjacent
   field text (upload hint / "will be removed" notice). Applies to every avatar-row —
   the shared account page and the staff-photo modal. */
.qm-avatar-row .qm-img-preview { flex: 0 0 auto; }
.qm-avatar-row .qm-avatar-img { width: 72px; height: 72px; }
.qm-account-edit .qm-account-divider { margin: var(--sp-5) 0 var(--sp-4); border: 0; border-top: 1px solid var(--line); }
.qm-account-edit .qm-section-subtitle { margin: 0 0 var(--sp-1); }
.qm-account-edit .qm-account-actions { margin-top: var(--sp-5); display: flex; justify-content: flex-end; }

/* ---- Account sidebar nav: mobile collapse via <details> (account-nav.php) ---- */
.qm-acct-nav-details > summary { list-style: none; }
.qm-acct-nav-details > summary::-webkit-details-marker { display: none; }
.qm-acct-nav-summary { display: none; align-items: center; justify-content: space-between; cursor: pointer; padding: 11px 14px; border-radius: var(--r-sm); font-weight: 700; color: var(--ink-800); }
.qm-acct-nav-summary i:first-child { color: var(--qm-primary); margin-right: 8px; }
.qm-acct-nav-summary-caret { transition: transform var(--t-fast); }
.qm-acct-nav-details[open] .qm-acct-nav-summary-caret { transform: rotate(180deg); }
.qm-acct-nav-label { margin: 14px 14px 4px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); }
.qm-acct-nav-label:first-child { margin-top: 4px; }
.qm-acct-nav a { display: flex; align-items: center; gap: 10px; }
.qm-acct-nav a .qm-head-chip, .qm-acct-nav .qm-acct-nav-logout button .qm-head-chip { font-size: 12px; }
/* Active row already carries a solid brand-colour background - a chip keeping
its own accent tint would clash, so it turns translucent-white instead. */
.qm-acct-nav a.active .qm-head-chip { background: rgba(255, 255, 255, .22); color: #fff; }
.qm-acct-nav-logout { margin-top: 6px; border-top: 1px solid var(--line); padding-top: 6px; }
.qm-acct-nav-logout button { display: flex; align-items: center; gap: 10px; width: 100%; }
@media (max-width: 991px) {
  .qm-acct-nav-summary { display: flex; }
  .qm-acct-nav-details:not([open]) .qm-acct-nav { display: none; }
}

/* ---- CANONICAL TAB STRIP — .qm-tabs / .qm-tab / .qm-tab-count ----
Single token-based tab component (underline style). Used by orders.php and
the retrofitted taxonomy / comments / menu-ingredient strips (these replace the
old divergent .qm-cat-tabs, .qm-comment-tabs, .qm-ing-tabs). Works for <a> and
<button> tabs; .qm-settings-nav keeps its own sticky vertical nav. */
.qm-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-4) 0 var(--sp-5); border-bottom: 1px solid var(--line); padding-bottom: 0; }
.qm-tab { display: inline-flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2) var(--sp-3); font-family: inherit; font-weight: 600; font-size: .92rem; color: var(--ink-500); text-decoration: none; -webkit-appearance: none; appearance: none; background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; transition: color var(--t-fast), border-color var(--t-fast); }
.qm-tab:hover { color: var(--ink-900); }
.qm-tab.is-active { color: var(--qm-primary); border-bottom-color: var(--qm-primary); }
.qm-tab-count { display: inline-flex; align-items: center; justify-content: center; min-width: 1.4rem; height: 1.4rem; padding: 0 .4rem; font-size: .72rem; font-weight: 700; line-height: 1; border-radius: var(--r-pill); background: var(--surface-2); color: var(--ink-700); }
.qm-tab.is-active .qm-tab-count { background: var(--qm-primary-600); color: #fff; }
/* On tablet/mobile .qm-tabs becomes a scrolling strip with reachability arrows — see the
   .qm-tabs-wrap rules with .qm-snav-wrap above (wired by settings-tabs.js). */
.qm-row-actions { display: inline-flex; align-items: center; gap: var(--sp-2); justify-content: flex-end; }

/* ---- Saved addresses (addresses.php) ---- */
.qm-addr-list { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: grid; gap: var(--sp-3); }
.qm-addr { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-start; justify-content: space-between; }
.qm-addr-body { min-width: 0; }
.qm-addr-head { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: 6px; }
.qm-addr-label { font-weight: 700; }
.qm-addr-line { margin: 0 0 2px; font-size: .92rem; }
.qm-addr-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-start; }
.qm-addr-editform { flex: 1 1 100%; border-top: 1px solid var(--line); margin-top: var(--sp-2); padding-top: var(--sp-3); }
.qm-addr-editform-actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); }

/* ---- Payment methods (payment.php) ---- */
.qm-pay-reassure { display: flex; align-items: center; gap: 8px; font-size: .88rem; margin: 6px 0 var(--sp-3); }
.qm-pay-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-2); }
.qm-pay-card { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); background: var(--surface); }
.qm-pay-card.is-default { border-color: var(--qm-primary); }
.qm-pay-card-top { display: flex; align-items: center; gap: var(--sp-2); font-weight: 700; }
.qm-pay-card-top i { font-size: 1.4rem; color: var(--qm-primary); }
.qm-pay-card-top .qm-pill { margin-left: auto; }
.qm-pay-number { font-size: 1.1rem; letter-spacing: .06em; margin: var(--sp-2) 0 4px; }
/* A saved PayPal account is named by its e-mail, not by four digits: the card spacing that makes
   •••• 4242 legible makes an address look broken, and an address has to be allowed to wrap. */
.qm-pay-number.is-account { font-size: .95rem; letter-spacing: 0; overflow-wrap: anywhere; }
/* Brand marks: the real SVG logos where the product ships them, the icon font otherwise. Sized to
   match so a row of mixed brands still sits on one line. */
.qm-pay-card-top .qm-pay-ico { height: 22px; width: auto; display: block; }
.qm-pay-glyph { font-size: 1.35rem; line-height: 1; color: var(--ink-500); }
/* THE WEIGHT THE ICON FONT ACTUALLY SHIPS, pinned so it cannot be inherited.
   Font Awesome selects the glyph by family and weight: Brands exists only at 400 and Free Solid
   only at 900, and nothing in the icon's own CSS states either -- so the weight of whatever it
   sits inside wins. Dropped into a 600 context (.qm-pay-label, on every checkout tender row) the
   browser had no 600 to load and synthesised one, smearing the outline: the same Amex mark read
   thicker and blurrier in a payment picker than in the saved-cards list two clicks away. */
.qm-pay-glyph.fab { font-weight: 400; }
.qm-pay-glyph.fas { font-weight: 900; }
/* Each brand's own colour, taken from its published mark, so a card reads as that card. Used only
   for the brands this product ships no artwork for; Visa, Mastercard and PayPal render as real SVGs.
   Drop amex.svg / discover.svg into assets/img/payment and name them in card_brand_mark() to
   promote them to artwork too. */
.qm-pay-glyph--amex { color: #006fcf; }
.qm-pay-glyph--discover { color: #ff6000; }
.qm-pay-glyph--diners { color: #0079be; }
.qm-pay-glyph--jcb { color: #0b4ea2; }
.qm-pay-glyph--unionpay { color: #005b9a; }
.qm-pay-meta { margin: 0 0 2px; font-size: .85rem; color: var(--ink-500); }
.qm-pay-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }

/* A pin card on a phone: the map is only as wide as the screen, so the card follows it down and
   the dishes shrink with it rather than being clipped by the frame. */
@media (max-width: 480px) {
  .qm-mappop-band { padding: var(--sp-2) var(--sp-3); }
  .qm-mappop-name { font-size: 1rem; }
  .qm-mappop-dish, .qm-mappop-dish img { width: 92px; }
  .qm-mappop-dish img { height: 68px; }
  .qm-mappop-modes { gap: var(--sp-1); }
}

/* The payment state and the button that resolves it read as one line — the column has the width
   for it now that reach has moved to the Active placements card. */
.qm-pay-cell { white-space: nowrap; }
.qm-pay-cell > .qm-pill, .qm-pay-cell > form { display: inline-block; vertical-align: middle; }
.qm-pay-cell > form { margin: 0 0 0 var(--sp-2); }

/* ---- What is running right now (partials/active-placements.php) ---- */
/* Sits in a chart cell beside the occupancy timeline, so it takes the chart card's own head
   anatomy and simply lists instead of plotting. */
.qm-active-ads { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.qm-active-ads-list { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.qm-active-ad { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-3) 0; border-bottom: 1px solid var(--line); }
.qm-active-ad-shot { flex: 0 0 auto; width: 56px; height: 42px; object-fit: cover; border-radius: var(--r-sm, 8px); }
.qm-active-ad-body { flex: 1 1 auto; min-width: 0; }
.qm-active-ad:last-child { border-bottom: 0; }
.qm-active-ad-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); }
.qm-active-ad-slot { font-weight: 600; font-size: var(--fs-400, 13px); color: var(--ink-900); }
/* The rate is the number this card exists for, so it is the number that reads first. */
.qm-active-ad-ctr { font-weight: 700; font-size: var(--fs-600, 15px); color: var(--ok, #1a9d5a); white-space: nowrap; }
.qm-active-ad-ctr.is-muted { font-weight: 500; font-size: 12px; color: var(--ink-500); }
.qm-active-ad-headline { margin: 2px 0 0; font-size: 12.5px; color: var(--ink-700, var(--ink-900));
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }
.qm-active-ad-reach { margin: 2px 0 0; font-size: 11.5px; color: var(--ink-500); }
.qm-active-ads-empty { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); flex: 1 1 auto; color: var(--ink-500); text-align: center; padding: var(--sp-4) 0; }
.qm-active-ads-empty i { font-size: 1.5rem; opacity: .5; }
.qm-active-ads-empty p { margin: 0; font-size: 13px; }

/* ---- Saved payment methods, as a list (partials/payment-methods.php) ---- */
/* One line per method. A brand, what tells this one from the next, when it runs out — and the
   controls at the END of the line, so several methods read as a list rather than as competing
   panels each shouting two buttons. */
.qm-pm-list { list-style: none; margin: 0; padding: 0; }
/* EVERY ROW THE SAME HEIGHT, whether or not it has a second line. Amazon Pay carries no account
   detail at all, so left to itself it sat noticeably shorter than the cards beside it and the list
   looked mis-set. A minimum height holds the rhythm; the content stays vertically centred inside it. */
.qm-pm { display: flex; align-items: center; gap: var(--sp-3); min-height: 68px;
  padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--line);
  border-radius: var(--r-sm, 8px); transition: background-color var(--t, .15s); }
.qm-pm:last-child { border-bottom: 0; }
/* The whole row is pressable, so the whole row answers the pointer. */
.qm-pm:hover { background: var(--surface-2); }
/* While a row is being changed it says so on the row, not by blanking the page. */
.qm-pm.is-busy { opacity: .55; pointer-events: none; }
.qm-pm.is-busy .qm-pm-actions::after { content: ''; width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--line); border-top-color: var(--brand); animation: qm-pm-spin .6s linear infinite; }
@keyframes qm-pm-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .qm-pm.is-busy .qm-pm-actions::after { animation: none; } }
.qm-pm.is-default:hover { background: transparent; }
.qm-pm-mark { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 40px; }
.qm-pm-mark .qm-pay-ico { height: 24px; width: auto; display: block; }
.qm-pm-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-pm-detail { font-size: 14px; color: var(--ink-500); overflow-wrap: anywhere; }
.qm-pm-actions { flex: 0 0 auto; display: inline-flex; align-items: center; gap: var(--sp-3); }
.qm-pm-check { color: var(--ok, #1a9d5a); font-size: 1.05rem; }
/* A quiet text action: setting the default is a small correction, not a call to action. */
/* The row itself is the control, so it carries the whole line and the hit area. */
.qm-pm-choose { flex: 1 1 auto; min-width: 0; margin: 0; }
.qm-pm-select { display: flex; align-items: center; gap: var(--sp-3); width: 100%; min-height: 44px; text-align: left;
  background: none; border: 0; padding: 0; color: inherit; font: inherit; cursor: pointer; }
.qm-pm-select.is-current { cursor: default; }
.qm-pm-choose:hover .qm-pm-select strong { color: var(--brand); }
.qm-pm-remove { background: none; border: 0; padding: 4px; color: var(--ink-500); cursor: pointer; line-height: 1; }
.qm-pm-remove:hover { color: var(--danger, #c0392b); }
.qm-pm.is-expired .qm-pm-body strong { color: var(--ink-500); }

/* ---- Add new payment method (partials/payment-methods.php) ---- */
/* One row per method the site can keep on file. Same anatomy as the vendor checkout chooser so
   picking how to pay and picking what to save look like the same product. */
.qm-addpay { display: grid; gap: var(--sp-2); }
.qm-addpay-form { margin: 0; }
/* Same rhythm as the saved list above it: one height for every row, whatever it is called. */
.qm-addpay-choice { display: flex; text-decoration: none; align-items: center; gap: var(--sp-3); width: 100%; min-height: 64px; text-align: left;
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); color: inherit; cursor: pointer; transition: background-color var(--t, .15s), border-color var(--t, .15s); }
.qm-addpay-choice:hover, .qm-addpay-choice:focus-visible { border-color: var(--brand); background: var(--surface-2); }
.qm-addpay-mark { display: inline-flex; align-items: center; justify-content: center; gap: 4px; flex: 0 0 auto; width: 56px; }
.qm-addpay-mark .qm-pay-ico { height: 20px; width: auto; }
.qm-addpay-text { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.qm-addpay-go { flex: 0 0 auto; color: var(--ink-500); }
.qm-addpay-panel { margin-top: var(--sp-3); }
.qm-addpay-panel-title { font-size: 1.05rem; margin: 0 0 var(--sp-3); }
.qm-pay-glyph--link { color: #00d66f; }
/* Keyed by Stripe's own payment_method_type, which is what card_brand_mark() appends. */
.qm-pay-glyph--amazon_pay { color: #ff9900; }
.qm-pay-glyph--cashapp { color: #00d54b; }

/* ---- Vendor subscription billing (vendor/billing.php) ---- */
/* THE PLAN HERO. The card the whole page hangs off: an identity band (the plan's
   name and its price at real scale, on a soft brand wash) over the facts as a
   measured stat band — label / value / note per column, the same voice as the
   dashboard stat rows. */
.qm-billing-plan { border: 1px solid color-mix(in srgb, var(--qm-primary) 20%, var(--line)); border-radius: var(--r-lg); padding: 0; overflow: hidden; background: var(--surface); }
.qm-billing-plan-name {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap;
  margin: 0; padding: 18px 24px;
  background: linear-gradient(120deg, color-mix(in srgb, var(--qm-primary) 7%, #fff), color-mix(in srgb, var(--qm-primary) 2%, #fff) 70%);
  border-bottom: 1px solid var(--line);
}
.qm-billing-plan-name strong { font-size: 26px; font-weight: 800; letter-spacing: -.01em; }
/* The admin partner hero writes its name as an <h2> beside the brandmark. */
.qm-billing-plan-name h2 { font-size: 26px; font-weight: 800; letter-spacing: -.01em; }
.qm-billing-plan-name .qm-tbl-id { gap: 14px; }
/* Facts that hold their own components (an owner link, a plan tag) sit as
   normal-weight content; the value scale stays for plain figures. */
.qm-billing-plan .qm-billing-meta dd .qm-tbl-id-text strong { font-size: 14px; }
.qm-billing-price { font-weight: 800; font-size: 22px; text-align: end; line-height: 1.2; }
.qm-billing-price small { font-weight: 600; font-size: 12.5px; }
.qm-billing-plan > .qm-billing-meta { padding: 18px 24px 20px; }
.qm-billing-plan .qm-billing-meta dt { font-size: 11px; font-weight: 700; color: var(--ink-400, #a8a29e); }
.qm-billing-plan .qm-billing-meta dd { font-size: var(--fs-700, 16px); font-weight: 700; }
.qm-billing-plan .qm-billing-meta dd .qm-cell-sub { font-size: 12.5px; color: var(--ink-500, #78716c); }
/* The hero's one action rides its own quiet foot band, clear of the stat row. */
.qm-billing-plan-foot { display: flex; justify-content: flex-end; padding: 10px 24px; border-top: 1px solid var(--line); background: color-mix(in srgb, var(--qm-primary) 2%, #fff); }
/* align-items: start, because one column carries the "Cancel automatic renewal" form and the rest do
   not. Stretched, that one column set the height of every track and the short ones grew a ragged
   pocket of dead space beneath their value — the row read as misaligned when only one cell was tall.
   Column gap is wider than row gap: these are separate facts side by side, and --sp-3 between them
   let "204 days left" sit close enough to the NEXT column to look like part of it. */
.qm-billing-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sp-4) var(--sp-6); margin: 0; align-items: start; }
.qm-billing-meta div { margin: 0; }
.qm-billing-meta dt { font-size: .8rem; color: var(--ink-500); text-transform: uppercase; letter-spacing: .04em; margin-bottom: var(--sp-1); }
.qm-billing-meta dd { margin: 0; font-weight: 600; line-height: 1.35; }
/* THE CAPTION IS A SECOND FACT, NOT A CONTINUATION OF THE VALUE. .qm-cell-sub is a bare <span>
   written immediately after the figure inside the same <dd>, so inline it rendered with nothing
   between them at all: "7%applied to each order at the time it is paid", "$79.00 USD204 days left",
   and a date, its days-left and "Renews automatically" all run together on one line — which then
   wrapped mid-phrase and made the card look cluttered. On its own line each column reads
   label / value / note. Scoped to this card: .qm-cell-sub is shared by ~30 other views where it
   trails text that already provides its own separator. */
.qm-billing-meta dd .qm-cell-sub { display: block; margin-top: var(--sp-1); font-weight: 400; line-height: 1.35; }
/* The action belongs to the fact above it, with room to be seen as an action rather than more text. */
.qm-billing-meta dd form { margin-top: var(--sp-3); }
/* auto-FIT, not auto-fill. auto-fill keeps the empty tracks it could not fill, so three plan cards
   in a row wide enough for four left a card's worth of blank space on the right — the gap was the
   grid reserving room for a card that does not exist. auto-fit collapses those tracks and the real
   cards stretch across the full width instead. */
.qm-billing-methods { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--sp-4); }
/* The tier's own inclusions. Inherits .qm-plan-features wholesale — same list, same ticks, same
   spacing as the public pricing page — and only trims the bottom margin, which there separates the
   list from a call-to-action this card does not have. */
.qm-billing-method-features { margin-bottom: var(--sp-3); }
/* Nine inclusions in one column made every card a tower; two columns halve the
   scroll and read like the pricing page's own feature grids. */
@media (min-width: 768px) {
  .qm-billing-method-features { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 16px; }
}

/* The plan/renewal row. .qm-card carries its own margin-bottom, but inside a grid column with
   h-100 that margin is trapped under the card and the row itself contributes nothing — so Payment
   history sat flush against the two cards above it. The gap belongs to the row; the cards inside
   it stretch instead. */
.qm-billing-row { margin-bottom: var(--sp-5); }
.qm-billing-row > [class^="col-"] > .qm-card,
.qm-billing-row > [class*=" col-"] > .qm-card { margin-bottom: 0; }
/* A plan move is a PRICING CARD: framed, breathing, its direction worn openly —
   the upgrade leads with the brand ring, a downgrade stays quiet. */
.qm-billing-method { border: 1px solid var(--line); border-radius: var(--r-lg); padding: 22px 22px 18px; background: var(--surface); display: flex; flex-direction: column; transition: box-shadow var(--t-fast); }
.qm-billing-method:hover { box-shadow: var(--e-2); }
.qm-billing-method--up { border-color: color-mix(in srgb, var(--qm-primary) 42%, var(--line)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 8%, transparent); }
.qm-billing-method-hd { display: flex; align-items: center; gap: 10px; font-weight: 700; margin-bottom: 10px; }
.qm-billing-method-hd > span:not(.qm-pill) { font-size: 17px; font-weight: 800; }
.qm-billing-method-hd .qm-pill { margin-inline-start: auto; }
.qm-billing-method-hd i {
  font-size: 12px; color: var(--qm-primary); width: 30px; height: 30px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--qm-primary) 10%, #fff);
}
.qm-billing-method-note { font-size: .86rem; margin-bottom: var(--sp-3); flex: 1 1 auto; }
.qm-billing-method-price { flex: 0 0 auto; margin-bottom: var(--sp-2); }
.qm-billing-method-price strong { font-size: 26px; font-weight: 800; letter-spacing: -.01em; }
.qm-billing-method-price small { font-size: 12.5px; }

/* ---- My rewards / loyalty wallets (loyalty.php) ---- */
.qm-loyalty-empty { text-align: center; padding: var(--sp-9) var(--sp-5); }
.qm-loyalty-empty-ico { width: 64px; height: 64px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-h2); background: var(--qm-primary-50); color: var(--qm-primary); margin-bottom: 14px; }
.qm-loyalty-empty :is(h1,h2,h3,h4,h5,h6) { font-size: 19px; font-weight: 800; color: var(--ink-900); margin: 0 0 6px; }
.qm-loyalty-empty p { font-size: 14px; color: var(--ink-500); margin: 0 0 8px; }
.qm-loyalty-wallet-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.qm-loyalty-wallet-title { margin: 0; font-size: 17px; font-weight: 800; line-height: 1.2; }
.qm-loyalty-wallet-rest { display: inline-flex; align-items: center; gap: 12px; text-decoration: none; color: var(--ink-900); }
.qm-loyalty-wallet-rest img { object-fit: cover; background: var(--surface); }
.qm-loyalty-wallet-rest:hover { color: var(--qm-primary); }
.qm-loyalty-wallet-progress { margin: 16px 0 4px; }
.qm-loyalty-wallet-progress .qm-loyalty-strip-bar { width: 100%; height: 8px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; }
.qm-loyalty-wallet-progress .qm-loyalty-strip-bar > span { display: block; height: 100%; background: var(--qm-primary); border-radius: var(--r-pill); }
.qm-loyalty-wallet-cap { font-size: var(--fs-400); color: var(--ink-500); margin: 8px 0 0; display: flex; align-items: center; gap: 7px; }
.qm-loyalty-wallet-cap i { color: var(--qm-primary); }
.qm-loyalty-wallet-cap strong { color: var(--ink-900); }
.qm-reward-grid { margin-top: 18px; }
.qm-reward-need i { color: var(--ok); }
.qm-loyalty-ledger { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 14px; }
.qm-loyalty-ledger-title { font-size: var(--fs-400); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); margin: 0 0 8px; }
.qm-loyalty-ledger-list { list-style: none; margin: 0; padding: 0; }
.qm-loyalty-ledger-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; font-size: 13.5px; border-bottom: 1px solid var(--line); }
.qm-loyalty-ledger-row:last-child { border-bottom: 0; }
.qm-loyalty-ledger-note { flex: 1 1 auto; color: var(--ink-700); }
.qm-loyalty-ledger-pts { font-weight: 800; font-variant-numeric: tabular-nums; }
.qm-loyalty-ledger-pts.is-up { color: var(--ok); }
.qm-loyalty-ledger-pts.is-down { color: var(--qm-primary); }
.qm-loyalty-ledger-date { color: var(--ink-400); font-size: 12.5px; white-space: nowrap; }

/* ---- Perks / rewards overview (perks.php) ---- */
.qm-perks-summary { background: var(--tint); }
.qm-perks-muted { color: var(--ink-500); font-size: 14px; }
.qm-perks-lead { margin-bottom: 18px; }
.qm-perks-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.qm-perks-points { color: var(--qm-primary); }
.qm-perks-tier { text-decoration: none; }
.qm-perks-tier i { margin-right: 4px; }
.qm-perks-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 22px; }
.qm-perks-stat { padding: var(--sp-5); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.qm-perks-stat-num { display: block; font-size: var(--fs-h2); font-weight: 800; color: var(--ink-900); line-height: 1.1; }
.qm-perks-stat-label { display: block; font-size: 12.5px; color: var(--ink-500); margin-top: 4px; }
.qm-perks-earn { margin: 18px 0 0; font-size: var(--fs-400); color: var(--ink-500); display: flex; align-items: flex-start; gap: 8px; line-height: 1.6; }
.qm-perks-earn i { color: var(--qm-primary); margin-top: 2px; flex: 0 0 auto; }
.qm-perks-earn a { font-weight: 700; }
.qm-perks-highlight { display: flex; align-items: center; gap: 18px; }
.qm-perks-highlight-ico { flex: 0 0 auto; width: 54px; height: 54px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary-50); color: var(--qm-primary); font-size: 22px; }
.qm-perks-earn-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 10px; }
.qm-perks-earn-item { padding: 20px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.qm-perks-earn-ico { width: 44px; height: 44px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary-50); color: var(--qm-primary); font-size: var(--fs-800); margin-bottom: 12px; }
.qm-perks-earn-item :is(h1,h2,h3,h4,h5,h6) { font-size: var(--fs-600); font-weight: 700; color: var(--ink-900); margin: 0 0 6px; }
.qm-perks-earn-item p { font-size: var(--fs-400); color: var(--ink-500); margin: 0; line-height: 1.6; }
@media (max-width: 767.98px) { .qm-perks-stats, .qm-perks-earn-grid { grid-template-columns: 1fr; } }

/* ---- Kiosk staff idle-lock overlay (partials/staff-lock.php) ---- */
.qm-lock { position: fixed; inset: 0; z-index: 4000; display: flex; align-items: center; justify-content: center; padding: 20px; background: var(--qm-grad-dark); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.qm-lock[hidden] { display: none; }
.qm-lock-card { width: 100%; max-width: 360px; background: var(--surface); border-radius: var(--r-xl); padding: 28px 24px 22px; text-align: center; box-shadow: var(--e-4); animation: qmLockIn .22s ease; }
@keyframes qmLockIn { from { transform: translateY(14px) scale(.97); opacity: 0; } to { transform: none; opacity: 1; } }
.qm-lock-card.qm-lock-shake { animation: qmLockShake .4s; }
@keyframes qmLockShake { 0%, 100% { transform: translateX(0); } 20%, 60% { transform: translateX(-9px); } 40%, 80% { transform: translateX(9px); } }
.qm-lock-avatar { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 4px var(--qm-primary-50); }
.qm-lock-name { font-size: 19px; font-weight: 800; color: var(--ink-900); margin-top: 12px; }
.qm-lock-dept { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--qm-primary); margin-top: 3px; }
.qm-lock-sub { font-size: 13.5px; color: var(--ink-500); margin-top: 8px; }
/* Preview-only hint (demo mode): shows the seeded demo PIN so a prospective buyer
   can unlock the live preview. Never rendered when demo mode is off. */
.qm-lock-demo { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; padding: 5px 12px; border-radius: var(--r-pill); background: var(--qm-primary-50); color: var(--qm-primary); font-size: 12.5px; font-weight: 600; }
.qm-lock-demo strong { font-weight: 800; letter-spacing: .12em; }
.qm-lock-dots { display: flex; gap: 14px; justify-content: center; margin: 18px 0 6px; }
.qm-lock-dots span { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--qm-primary); transition: background .12s; }
.qm-lock-dots span.is-on { background: var(--qm-primary); }
.qm-lock-msg { min-height: 18px; font-size: var(--fs-400); font-weight: 600; color: var(--qm-primary); }
.qm-lock-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 10px; }
.qm-lock-key { height: 64px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); font-size: 24px; font-weight: 700; color: var(--ink-900); cursor: pointer; transition: transform .06s, background .12s, border-color .12s; -webkit-tap-highlight-color: transparent; user-select: none; }
.qm-lock-key:hover { border-color: var(--qm-primary); }
.qm-lock-key:active { transform: scale(.95); background: var(--qm-primary-50); }
.qm-lock-key-ghost { background: var(--surface-2); font-size: var(--fs-800); color: var(--ink-600); }
.qm-lock-out { margin-top: 16px; }
.qm-lock-switch { background: none; border: 0; color: var(--ink-500); font-size: var(--fs-400); font-weight: 600; cursor: pointer; text-decoration: underline; }
.qm-lock-switch:hover { color: var(--qm-primary); }
/* Standalone full-page lock (app/views/vendor/lock-screen.php): no dashboard chrome
   behind it, so paint the dark background on the body to avoid a white flash before
   the fixed .qm-lock overlay is laid out. */
.qm-lock-body { margin: 0; min-height: 100vh; background: var(--qm-grad-dark); }

/* ============================================================================
Footer chrome + FAQ heading reset (consolidated from per-view styles in
footer.php / public/page.php). Tokenized brand colors.
========================================================================== */

/* ---- Footer — SaaS modern light skin (was inline in footer.php) ---- */
.qm-ftr { background: var(--surface-2); color: var(--ink-600); border-top: 1px solid var(--line); }
.qm-ftr a { color: var(--ink-700); text-decoration: none; transition: color .2s ease; }
.qm-ftr a:hover { color: var(--qm-primary); }
.qm-ftr-benefits { border-bottom: 1px solid var(--line); background: var(--surface); }
.qm-ftr-benefits .container-fluid { padding-top: 32px; padding-bottom: 32px; }
.qm-ftr-bgrid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 18px; }
.qm-ftr-benefit { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; }
.qm-ftr-benefit i { width: 54px; height: 54px; display: inline-flex; align-items: center; justify-content: center; font-size: 22px; line-height: 1; color: var(--qm-primary); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: 50%; transition: all .2s ease; }
.qm-ftr-benefit:hover i { background: var(--qm-grad); border-color: transparent; color: #fff; transform: translateY(-3px); box-shadow: var(--e-primary); }
.qm-ftr-benefit span { font-size: var(--fs-400); font-weight: 700; line-height: 1.4; color: var(--ink-900); }
.qm-ftr-gallerywrap { border-bottom: 1px solid var(--line); }
.qm-ftr-gallery-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 26px 0 14px; }
.qm-ftr-gallery-head .qm-eyebrow i { margin-right: .35rem; }
/* Continuously drifting strip (shared .qm-marquee). Items are a fixed width and the track
   scrolls, so the row stays full-bleed at any viewport without a column count to maintain.
   The generous vertical padding is deliberate: the strip clips horizontally, so this is the
   room a hovered tile grows into instead of being cut off. */
.qm-ftr-gallery { padding: 18px 0 30px; }
.qm-ftr-gallery .qm-marquee-item { flex: 0 0 240px; max-width: 52vw; margin-right: 14px;
  position: relative; z-index: 1;
  transition: transform var(--t-med, .28s) cubic-bezier(.2, .9, .3, 1.2); }
/* Hover lifts the tile clear of its neighbours and deepens the shadow so it reads as
   stepping forward rather than just scaling. */
.qm-ftr-gallery .qm-marquee-item:hover,
.qm-ftr-gallery .qm-marquee-item:focus-within { transform: scale(1.13); z-index: 5; }
.qm-ftr-gallery .qm-ftr-gitem { box-shadow: 0 6px 16px rgba(31, 11, 18, .16); transition: box-shadow var(--t-med, .28s) ease; }
.qm-ftr-gallery .qm-marquee-item:hover .qm-ftr-gitem,
.qm-ftr-gallery .qm-marquee-item:focus-within .qm-ftr-gitem { box-shadow: 0 20px 44px rgba(31, 11, 18, .38); }
/* The whole tile scales now, so the old inner zoom would double up. */
.qm-ftr-gallery .qm-ftr-gitem:hover img { transform: none; }
@media (prefers-reduced-motion: reduce) {
  .qm-ftr-gallery .qm-marquee-item { transition: none; }
}
.qm-ftr-gitem { display: block; overflow: hidden; position: relative; aspect-ratio: 5/4; border-radius: 12px; width: 100%; padding: 0; border: 0; background: none; cursor: pointer; }
.qm-ftr-gitem img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }
.qm-ftr-gitem::after { content: "\f00e"; font-family: "Font Awesome 5 Free"; font-weight: 900; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 22px; opacity: 0; transition: .3s; }
.qm-ftr-gitem:hover img { transform: scale(1.09); }
.qm-ftr-gitem:hover::after { opacity: 1; background: color-mix(in srgb, var(--qm-primary) 50%, transparent); }
/* Shared image lightbox — opens any [data-lightbox-src] item full-screen
   (js/qm-lightbox.js); used by the restaurant-detail gallery and the footer
   "From our kitchens" gallery. */
.qm-lightbox { position: fixed; inset: 0; z-index: 4000; display: flex; align-items: center; justify-content: center; padding: var(--sp-5, 20px); background: rgba(20,8,14,.85); }
.qm-lightbox[hidden] { display: none; }
.qm-lightbox-img, .qm-lightbox-video { max-width: min(92vw, 1000px); max-height: 88vh; border-radius: var(--r-md); box-shadow: 0 20px 60px rgba(0,0,0,.5); }
.qm-lightbox-video { background: #000; }
/* Wide diagrams (the business-model money-flow banners) opt in via data-lightbox-wide. The 1000px
   cap above frames a portrait-ish food photo; a 2:1 banner held to it renders barely taller than
   its thumbnail, which defeats the point of opening it. Galleries never set the flag. */
.qm-lightbox.is-wide .qm-lightbox-img, .qm-lightbox.is-wide .qm-lightbox-video { max-width: 96vw; }
.qm-lightbox-close { position: absolute; top: var(--sp-5, 20px); right: var(--sp-5, 20px); width: 44px; height: 44px; border: none; border-radius: 50%; background: #fff; color: var(--ink-900, #1f0b12); font-size: 1.6rem; line-height: 1; cursor: pointer; box-shadow: var(--e-2); }
.qm-lightbox-close:hover { background: var(--qm-primary, #ff003d); color: #fff; }
/* The single page-scroll lock for every overlay in the product: modals, the lightbox,
   the media picker and SweetAlert dialogs all raise it through QM.lockScroll().
   It sits on <html> because <body> must never be the one locked -- see the note on
   lockScroll() in app.js: a body-level lock re-parents the sticky sidebar's scroll
   context and drags it off-screen by exactly the current scroll offset. */
html.qm-scroll-lock { overflow: hidden; }
/* SweetAlert2 ships its own body-level lock and applies it as an INLINE style from JS,
   so only !important can take it back -- a plain rule loses to the inline declaration and
   the sidebar still jumps. Measured: without !important the confirm dialog still shifted
   the rail by the full scroll offset. Its dialogs raise QM.lockScroll() like everything
   else, so nothing is lost by disarming this. */
body.swal2-shown, body.swal2-height-auto, body.swal2-toast-shown { overflow: visible !important; }
.qm-ftr-main { padding: 48px 0 32px; }
/* Dots beside footer headers + links, matching the mega-menu treatment. */
.qm-ftr-col h2 { display: flex; align-items: center; color: var(--ink-900); font-size: var(--fs-600); font-weight: 800; margin-bottom: 18px; letter-spacing: .01em; }
.qm-ftr-col h2::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--qm-primary); margin-right: 9px; }
.qm-ftr-col ul { list-style: none; padding: 0; margin: 0; }
.qm-ftr-col li { margin-bottom: 11px; font-size: 14px; line-height: 1.4; }
.qm-ftr-col li > a { position: relative; display: inline-block; padding-left: 15px; }
.qm-ftr-col li > a::before { content: ""; position: absolute; left: 0; top: .5em; width: 5px; height: 5px; border-radius: 50%; background: var(--qm-primary); opacity: .4; transition: opacity .2s ease; }
.qm-ftr-col li > a:hover::before { opacity: 1; }
.qm-ftr-col li .lbl { display: block; color: var(--ink-400); font-size: 12px; margin-bottom: 1px; }
/* Sign out in the footer is a POST (same as the header's), so it is a button dressed exactly
   as the column's links: same colour, hover and leading dot. */
.qm-ftr-col li > .qm-ftr-form { display: inline; }
.qm-ftr-col li > .qm-ftr-form > .qm-ftr-btn { position: relative; display: inline-block; padding: 0 0 0 15px; margin: 0; border: 0; background: none; font: inherit; line-height: inherit; color: var(--ink-700); cursor: pointer; transition: color .2s ease; }
.qm-ftr-col li > .qm-ftr-form > .qm-ftr-btn::before { content: ""; position: absolute; left: 0; top: .5em; width: 5px; height: 5px; border-radius: 50%; background: var(--qm-primary); opacity: .4; transition: opacity .2s ease; }
.qm-ftr-col li > .qm-ftr-form > .qm-ftr-btn:hover { color: var(--qm-primary); }
.qm-ftr-col li > .qm-ftr-form > .qm-ftr-btn:hover::before { opacity: 1; }
.qm-ftr-brand-logo { display: inline-block; margin-bottom: 16px; }
.qm-ftr-brand-logo img { height: 38px; width: auto; }
.qm-ftr-about { font-size: 13.5px; line-height: 1.7; color: var(--ink-500); margin-bottom: 18px; max-width: 330px; }
.qm-ftr-social { display: flex; gap: 9px; }
/* The glyph is sized in em by SocialNetwork::icon(), so font-size is what scales it. At 14px in a
   38px chip it sat at about a third of the circle and read as an afterthought; 19px in 42px is the
   proportion the rest of the product's icon chips use. */
.qm-ftr-social a { width: 42px; height: 42px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary-50); border: 1px solid var(--line); color: var(--qm-primary); font-size: 19px; transition: all .2s ease; }
.qm-ftr-social a:hover { background: var(--qm-grad); border-color: transparent; color: #fff; transform: translateY(-2px); box-shadow: var(--e-primary); }
/* The brand colour is written onto the svg itself, so `color: #fff` above cannot reach it — without
   this the logo stays brand-coloured on the gradient it is hovering over. Covers the outline set's
   stroke too. */
.qm-ftr-social a svg { display: block; }
.qm-ftr-social a:hover svg { fill: #fff; }
.qm-ftr-social a:hover svg[stroke] { fill: none; stroke: #fff; }
/* Below xl the brand column (logo / about / social) stacks full-width above the link
   columns, so the social row would otherwise butt straight against the next column's
   heading ("Discover"). Give it a clear gap only while it is stacked; at xl it sits beside
   the columns and needs none. */
@media (max-width: 1199.98px) {
  .qm-ftr-social { margin-bottom: 32px; }
}
.qm-ftr-badges { display: flex; flex-direction: row; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
.qm-ftr-badges a { display: inline-block; border-radius: 8px; overflow: hidden; width: fit-content; box-shadow: var(--e-1); }
.qm-ftr-badges img { height: 42px; width: auto; display: block; }
.qm-ftr-copy { background: var(--surface); border-top: 1px solid var(--line); padding: 18px 0; }
.qm-ftr-copy .qm-ftr-pay { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; }
.qm-ftr-copy .qm-ftr-pay img { height: 26px; width: auto; }
.qm-ftr-pay-mark { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); font-weight: 600; color: var(--ink-700); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 5px 12px; }
.qm-ftr-pay-mark i { color: var(--qm-primary); font-size: var(--fs-400); }
.qm-ftr-copy .qm-ftr-cr { color: var(--ink-400); font-size: var(--fs-400); }
@media (max-width: 991.98px) {
  .qm-ftr-bgrid { grid-template-columns: repeat(3, 1fr); gap: 24px 14px; }
  .qm-ftr-gallery .qm-marquee-item { flex-basis: 200px; }
}
@media (max-width: 575.98px) {
  .qm-ftr-bgrid { grid-template-columns: repeat(2, 1fr); }
  .qm-ftr-gallery { padding: 12px 0 22px; }
  .qm-ftr-gallery .qm-marquee-item { flex-basis: 168px; margin-right: 10px; }
}

/* ---- FAQ question heading-wrapper reset (was inline in public/page.php) ---- */
.qm-faq-h { margin: 0; font-size: inherit; font-weight: inherit; line-height: inherit; }

/* =====================================================================
BLOG MODERN — template-blend: creative subheader, dense
colorful two-sidebar layout, rich cards. Used by public/blog-modern.php,
public/blog-detail-modern.php and the blog-modern-sidebar-* partials.
The detail page reuses .qm-article-body / .qm-comments / .qm-author-card.
===================================================================== */

/* --- Creative subheader --- */
.qm-blogm-head { position: relative; overflow: hidden; background: var(--qm-grad); color: #fff; padding: clamp(38px, 6vw, 64px) 0 clamp(34px, 5vw, 56px); margin-bottom: var(--sp-7); }
.qm-blogm-head::before { content: ""; position: absolute; top: -70px; right: -40px; width: 260px; height: 260px; border-radius: 50%; background: rgba(255,255,255,.12); }
.qm-blogm-head::after { content: ""; position: absolute; bottom: -110px; left: 6%; width: 220px; height: 220px; border-radius: 50%; background: rgba(255,255,255,.08); }
.qm-blogm-head-inner { position: relative; z-index: 1; text-align: center; max-width: 760px; margin: 0 auto; padding: 0 var(--sp-4); }
.qm-blogm-eyebrow { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: #fff; background: rgba(255,255,255,.18); padding: 6px 15px; border-radius: var(--r-pill); margin-bottom: var(--sp-3); }
.qm-blogm-title { font-size: clamp(28px, 4.4vw, 44px); font-weight: 800; line-height: 1.12; letter-spacing: -.02em; margin: 0 0 var(--sp-3); color: #fff; }
.qm-blogm-lead { font-size: clamp(15px, 1.6vw, 17px); color: rgba(255,255,255,.92); margin: 0 0 var(--sp-4); }
.qm-blogm-crumb { display: inline-flex; flex-wrap: nowrap; align-items: center; gap: 8px; max-width: 100%; font-size: 13.5px; font-weight: 600; background: rgba(255,255,255,.14); padding: 7px 16px; border-radius: var(--r-pill); }
.qm-blogm-crumb > a, .qm-blogm-crumb .sep { flex: 0 0 auto; white-space: nowrap; }
.qm-blogm-crumb a { color: #fff; text-decoration: none; opacity: .9; }
.qm-blogm-crumb a:hover { opacity: 1; text-decoration: underline; }
.qm-blogm-crumb .sep { opacity: .6; }
.qm-blogm-crumb .active { opacity: .85; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 575.98px) {
  .qm-blogm-head-inner { padding: 0 var(--sp-3); }
  .qm-blogm-crumb { font-size: 12.5px; padding: 6px 12px; }
}

/* --- Layout --- */
.qm-blogm-wrap { padding-bottom: var(--sp-9); }
.qm-blogm-side { display: flex; flex-direction: column; gap: var(--sp-5); }
@media (min-width: 992px) { .qm-blogm-side.is-sticky { position: sticky; top: calc(68px + var(--sp-6)); } }

/* --- Filter bar (reuses tokens) --- */
.qm-blogm-filter { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-5); background: var(--qm-primary-50); border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); font-size: 14px; color: var(--ink-700); }
.qm-blogm-filter a { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; color: var(--qm-primary-600); text-decoration: none; }
.qm-blogm-filter a:hover { text-decoration: underline; }

/* --- Rich card --- */
.qm-blogm-card { position: relative; display: flex; flex-direction: column; height: 100%; background: linear-gradient(180deg, #fff 0%, #fff7fa 100%); border: 1px solid color-mix(in srgb, var(--qm-primary) 18%, transparent); border-radius: 18px; overflow: hidden; box-shadow: var(--e-1); transition: transform var(--t), box-shadow var(--t); }
.qm-blogm-card:hover { transform: translateY(-6px); box-shadow: var(--e-3); }
.qm-blogm-card-media { display: block; position: relative; aspect-ratio: 16/10; overflow: hidden; background: var(--line-2); }
.qm-blogm-card-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-blogm-card:hover .qm-blogm-card-media img { transform: scale(1.08); }
.qm-blogm-card-cat { position: absolute; top: 14px; left: 14px; background: var(--qm-grad); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 5px 12px; border-radius: var(--r-pill); text-decoration: none; box-shadow: 0 8px 16px color-mix(in srgb, var(--qm-primary) 28%, transparent); }
.qm-blogm-card-cat:hover { color: #fff; filter: brightness(1.05); }
.qm-blogm-card-body { display: flex; flex-direction: column; flex: 1; padding: var(--sp-5); }
.qm-blogm-card-title { font-size: var(--fs-800); font-weight: 700; line-height: 1.34; margin: 0 0 var(--sp-2); color: var(--ink-900); }
.qm-blogm-card-title a { color: inherit; text-decoration: none; }
.qm-blogm-card-title a:hover { color: var(--qm-primary); }
.qm-blogm-card-excerpt { font-size: 14px; color: var(--ink-500); line-height: 1.62; margin: 0 0 var(--sp-3); flex: 1; }
.qm-blogm-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--sp-4); }
.qm-blogm-tag { font-size: 11.5px; font-weight: 600; padding: 4px 11px; border-radius: var(--r-pill); text-decoration: none; border: 1px solid transparent; transition: transform var(--t-fast), box-shadow var(--t-fast); }
.qm-blogm-tag:hover { transform: translateY(-2px); box-shadow: 0 8px 14px rgba(30,23,42,.12); }
.qm-blogm-tag:nth-child(2n+1) { background: #ffe9ef; color: var(--qm-primary-600); border-color: color-mix(in srgb, var(--qm-primary) 30%, transparent); }
.qm-blogm-tag:nth-child(2n) { background: #fff1db; color: #b34f15; border-color: rgba(237,141,23,.35); }
.qm-blogm-card-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.qm-blogm-author { display: flex; align-items: center; gap: 10px; min-width: 0; }
.qm-blogm-author-img { width: 38px; height: 38px; border-radius: 50%; object-fit: cover; flex-shrink: 0; border: 2px solid #fff; box-shadow: 0 2px 8px rgba(78,8,29,.14); }
.qm-blogm-author-text { display: flex; flex-direction: column; min-width: 0; }
.qm-blogm-author-name { font-size: var(--fs-400); font-weight: 700; color: var(--ink-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-blogm-author-date { font-size: 11.5px; color: var(--ink-400); }
.qm-blogm-more { flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-400); font-weight: 700; color: var(--qm-primary); text-decoration: none; background: var(--qm-primary-50); padding: 7px 13px; border-radius: var(--r-pill); transition: gap var(--t-fast), background var(--t-fast); }
.qm-blogm-more:hover { background: var(--qm-primary-100); gap: 9px; }

/* --- Sidebar widgets (dense + colorful) --- */
.qm-blogm-widget { background: var(--surface); border: 1px solid color-mix(in srgb, var(--qm-primary) 16%, transparent); border-radius: 16px; padding: var(--sp-5); box-shadow: var(--e-1); }
.qm-blogm-widget-title { display: flex; align-items: center; gap: 9px; font-size: var(--fs-700); font-weight: 800; color: var(--ink-900); margin: 0 0 var(--sp-4); padding-bottom: var(--sp-3); border-bottom: 1px solid var(--line); }
.qm-blogm-widget-title::before { content: ""; width: 4px; height: 18px; border-radius: 4px; background: var(--qm-grad); }
/* Popular posts */
.qm-blogm-pop { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.qm-blogm-pop-item { display: flex; gap: 12px; padding: var(--sp-3) 0; border-bottom: 1px solid var(--line); }
.qm-blogm-pop-item:last-child { border-bottom: 0; padding-bottom: 0; }
.qm-blogm-pop-item:first-child { padding-top: 0; }
.qm-blogm-pop-thumb { flex-shrink: 0; width: 72px; height: 72px; border-radius: 12px; object-fit: cover; }
.qm-blogm-pop-body { min-width: 0; }
.qm-blogm-pop-title { font-size: 13.5px; font-weight: 700; line-height: 1.35; color: var(--ink-900); text-decoration: none; display: block; }
.qm-blogm-pop-title:hover { color: var(--qm-primary); }
.qm-blogm-pop-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: 11.5px; color: var(--ink-400); margin: 4px 0 0; }
.qm-blogm-pop-author { font-weight: 600; color: var(--ink-600); }
.qm-blogm-pop-sep { color: var(--ink-300); }
.qm-blogm-pop-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.qm-blogm-pop-tag { font-size: 11px; font-weight: 600; padding: 2px 9px; border-radius: var(--r-pill); text-decoration: none; background: var(--qm-primary-50); color: var(--qm-primary-600); }
.qm-blogm-pop-tag:hover { background: var(--qm-primary-100); }
/* Tag cloud */
.qm-blogm-cloud { display: flex; flex-wrap: wrap; gap: 8px; }
.qm-blogm-cloud a { font-size: 12.5px; font-weight: 600; padding: 7px 13px; border-radius: var(--r-pill); text-decoration: none; transition: transform var(--t-fast), box-shadow var(--t-fast); }
.qm-blogm-cloud a:hover { transform: translateY(-2px); box-shadow: 0 10px 16px rgba(30,23,42,.12); }
.qm-blogm-cloud a:nth-child(3n+1) { background: #e8f7ff; color: #0c5f92; }
.qm-blogm-cloud a:nth-child(3n+2) { background: #ffe8ef; color: var(--qm-primary-600); }
.qm-blogm-cloud a:nth-child(3n+3) { background: #ffeede; color: #c35a23; }
/* Categories */
.qm-blogm-cats { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.qm-blogm-cats a { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 11px 14px; border-radius: 12px; background: linear-gradient(145deg, #fff 0%, #fff4f7 100%); border: 1px solid color-mix(in srgb, var(--qm-primary) 26%, transparent); color: var(--ink-800); font-weight: 600; font-size: 14px; text-decoration: none; transition: transform var(--t-fast), border-color var(--t-fast); }
.qm-blogm-cats a:hover, .qm-blogm-cats a.is-active { transform: translateX(3px); border-color: var(--qm-primary); color: var(--qm-primary); }
.qm-blogm-cats-count { flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--qm-primary); background: #fff; border: 1px solid color-mix(in srgb, var(--qm-primary) 30%, transparent); border-radius: var(--r-pill); padding: 1px 9px; }
/* Featured restaurants */
.qm-blogm-rest { display: flex; flex-direction: column; gap: var(--sp-3); }
.qm-blogm-rest-item { display: flex; align-items: center; gap: 12px; text-decoration: none; padding: 8px; border-radius: 12px; transition: background var(--t-fast); }
.qm-blogm-rest-item:hover { background: var(--qm-primary-50); }
.qm-blogm-rest-thumb { width: 56px; height: 56px; border-radius: 12px; object-fit: cover; flex-shrink: 0; }
.qm-blogm-rest-name { font-size: 14px; font-weight: 700; color: var(--ink-900); }
.qm-blogm-rest-rate { font-size: 12px; color: var(--ink-500); margin-top: 2px; }
.qm-blogm-rest-rate i { color: var(--gold); }
/* Featured post card (right) */
.qm-blogm-feat { display: block; text-decoration: none; border-radius: 14px; overflow: hidden; position: relative; }
.qm-blogm-feat-media { position: relative; display: block; aspect-ratio: 16/10; overflow: hidden; }
.qm-blogm-feat-media img { width: 100%; height: 100%; object-fit: cover; transition: transform var(--t-slow); }
.qm-blogm-feat:hover .qm-blogm-feat-media img { transform: scale(1.06); }
.qm-blogm-feat-flag { position: absolute; top: 12px; left: 12px; background: var(--qm-grad); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; padding: 4px 11px; border-radius: var(--r-pill); }
.qm-blogm-feat-body { display: block; padding: var(--sp-4) 0 0; }
.qm-blogm-feat-cat { display: inline-block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--qm-primary); margin-bottom: 5px; }
.qm-blogm-feat-title { display: block; font-size: var(--fs-700); font-weight: 700; line-height: 1.34; color: var(--ink-900); margin: 0 0 6px; }
.qm-blogm-feat:hover .qm-blogm-feat-title { color: var(--qm-primary); }
.qm-blogm-feat-date { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-400); }
.qm-blogm-feat-date i { color: var(--qm-primary); margin-right: 4px; }
/* Promo / CTA */
.qm-blogm-promo { position: relative; overflow: hidden; border-radius: 16px; padding: var(--sp-6) var(--sp-5); text-align: center; color: #fff; background: linear-gradient(150deg, #250e15, #34121d 70%, #1a0b10); }
.qm-blogm-promo::after { content: ""; position: absolute; top: -50px; right: -40px; width: 160px; height: 160px; border-radius: 50%; background: color-mix(in srgb, var(--qm-primary) 25%, transparent); }
.qm-blogm-promo-title { position: relative; z-index: 1; font-size: 19px; font-weight: 800; margin: 0 0 var(--sp-2); color: #fff; }
.qm-blogm-promo-text { position: relative; z-index: 1; font-size: 13.5px; color: #f7dfe7; margin: 0 0 var(--sp-4); }
.qm-blogm-promo-btn { position: relative; z-index: 1; display: inline-flex; align-items: center; gap: 8px; background: var(--qm-grad); color: #fff; font-weight: 700; font-size: 14px; padding: 10px 20px; border-radius: var(--r-pill); text-decoration: none; box-shadow: 0 12px 22px color-mix(in srgb, var(--qm-primary) 32%, transparent); }
.qm-blogm-promo-btn:hover { color: #fff; transform: translateY(-2px); }
/* Newsletter */
.qm-blogm-news-copy { font-size: 13.5px; color: var(--ink-500); line-height: 1.6; margin: 0 0 var(--sp-3); }
.qm-blogm-news-form { display: flex; flex-direction: column; gap: 9px; }
.qm-blogm-news-form input { width: 100%; padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r-md); font: inherit; font-size: 14px; background: var(--surface); }
.qm-blogm-news-form input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-50); }
/* Search widget: input + icon button inline (NOT the newsletter's stacked column). */
.qm-blogm-search { display: flex; gap: 9px; }
.qm-blogm-search input { flex: 1 1 auto; min-width: 0; padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r-md); font: inherit; font-size: 14px; background: var(--surface); }
.qm-blogm-search input:focus { outline: none; border-color: var(--qm-primary); box-shadow: 0 0 0 3px var(--qm-primary-50); }

/* --- Detail page --- */
.qm-blogm-article { background: var(--surface); border: 1px solid var(--line); border-radius: 18px; overflow: hidden; box-shadow: var(--e-1); }
.qm-blogm-hero { width: 100%; height: clamp(240px, 38vw, 420px); object-fit: cover; display: block; }
.qm-blogm-article-head { padding: var(--sp-6) var(--sp-6) 0; }
.qm-blogm-article-title { font-size: clamp(24px, 3.4vw, 34px); font-weight: 800; line-height: 1.18; letter-spacing: -.02em; color: var(--ink-900); margin: var(--sp-3) 0 var(--sp-4); }
.qm-blogm-article-meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding-bottom: var(--sp-4); border-bottom: 1px solid var(--line); }
.qm-blogm-article-cat { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #fff; background: var(--qm-grad); padding: 5px 12px; border-radius: var(--r-pill); text-decoration: none; }

/* --- Responsive --- */
@media (max-width: 991.98px) {
  .qm-blogm-side.is-sticky { position: static; }
}

/* ============================================================
Reservation occasions (colours) + calendar
============================================================ */

/* Occasion chips + legend (reservations list/header) */
/* The colour KEY for the booking chips above it: a quiet card bar, not loose
   chips hovering on the page canvas. */
.qm-occasion-legend {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 12px 18px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--e-1);
}
.qm-occasion-legend-lbl { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-500); margin-right: 2px; }
.qm-occasion-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 11px 3px 8px; font-size: 12px; font-weight: 600; color: var(--ink-700); background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-pill); }
.qm-occasion-chip-sm { padding: 1px 9px 1px 6px; font-size: 11px; margin-top: 3px; }
/* Bookings list: occasion chip sits inline beside the guest name (its own row),
   phone and email then stack on separate lines below. */
.qm-resv-nameline { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; }
.qm-resv-nameline .qm-occasion-chip { margin-top: 0; }
.qm-occasion-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; box-shadow: inset 0 0 0 1px rgba(0,0,0,.08); }

/* Occasion picker on the booking form */
.qm-occasion-pick { display: flex; align-items: center; gap: 8px; }
.qm-occasion-pick .form-select { flex: 1 1 auto; }
.qm-occasion-swatch { width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }

/* Colour swatch palette (manage-occasions modal) */
.qm-swatches { display: flex; flex-wrap: wrap; gap: 9px; }
.qm-swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; padding: 0; cursor: pointer; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); transition: transform var(--t-fast); }
.qm-swatch:hover { transform: scale(1.12); }
.qm-swatch.is-sel { border-color: var(--ink-900); box-shadow: inset 0 0 0 2px #fff; }
/* Occasion-label dots (reservations lists) reuse .qm-swatch only for its
--qm-sw background-colour trick, not its 28px palette-picker size - the
combined-class selector needs to out-specificity .qm-swatch's own rule above
to keep the small dot instead of a big colour-picker circle. */
.qm-occasion-dot.qm-swatch { width: 10px; height: 10px; border: 0; padding: 0; cursor: default; box-shadow: inset 0 0 0 1px rgba(0,0,0,.08); }
.qm-occasion-dot.qm-swatch:hover { transform: none; }

/* Floor-plan table: today's-booking badge (occasion colour) */
.qm-table-card.has-event { border-color: var(--ev); }
.qm-tc-event { position: absolute; top: -8px; left: -8px; width: 22px; height: 22px; border-radius: 50%; background: var(--ev, #64748b); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; box-shadow: 0 2px 5px rgba(0,0,0,.22); border: 2px solid var(--surface); z-index: 2; cursor: help; }

/* Flatpickr (date/time pickers) themed to the design system — the popup was
   library-default before; this block brands every picker product-wide. */
body .flatpickr-calendar { border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-3); font-family: var(--f-sans); }
body .flatpickr-calendar.arrowTop::before, body .flatpickr-calendar.arrowTop::after { border-bottom-color: var(--line); }
body .flatpickr-months .flatpickr-month { color: var(--ink-900); }
body .flatpickr-current-month .flatpickr-monthDropdown-months, body .flatpickr-current-month input.cur-year { font-weight: 700; color: var(--ink-900); }
body .flatpickr-months .flatpickr-prev-month, body .flatpickr-months .flatpickr-next-month { width: 32px; height: 32px; top: 6px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: var(--ink-500); transition: background var(--t-fast), color var(--t-fast); }
body .flatpickr-months .flatpickr-prev-month:hover, body .flatpickr-months .flatpickr-next-month:hover { background: var(--qm-primary-50); color: var(--qm-primary-600); }
body .flatpickr-months .flatpickr-prev-month svg, body .flatpickr-months .flatpickr-next-month svg { width: 12px; height: 12px; fill: currentColor; }
body span.flatpickr-weekday { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); }
body .flatpickr-day { border-radius: var(--r-pill); font-weight: 600; color: var(--ink-700); }
body .flatpickr-day:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-50); color: var(--qm-primary-700); }
body .flatpickr-day.today { border-color: var(--qm-primary); color: var(--qm-primary-600); }
body .flatpickr-day.today:hover { background: var(--qm-primary-50); color: var(--qm-primary-700); }
body .flatpickr-day.selected, body .flatpickr-day.selected:hover { background: var(--qm-grad); border-color: transparent; color: #fff; box-shadow: var(--e-primary); }
body .flatpickr-day.flatpickr-disabled, body .flatpickr-day.flatpickr-disabled:hover { color: var(--ink-300); text-decoration: line-through; background: transparent; }
body .flatpickr-day.prevMonthDay, body .flatpickr-day.nextMonthDay { color: var(--ink-400); }
body .flatpickr-time input { font-weight: 700; color: var(--ink-800); }
body .flatpickr-time .numInputWrapper span.arrowUp::after { border-bottom-color: var(--ink-500); }
body .flatpickr-time .numInputWrapper span.arrowDown::after { border-top-color: var(--ink-500); }
body .flatpickr-time .numInputWrapper span:hover { background: var(--qm-primary-50); }

/* FullCalendar (v5) themed to the QuickMunch design system */
#res-calendar {
  --fc-border-color: var(--line);
  --fc-today-bg-color: var(--qm-primary-50);
  --fc-neutral-bg-color: var(--surface-2);
  --fc-page-bg-color: var(--surface);
  --fc-button-bg-color: #fff;
  --fc-button-border-color: var(--field-border);
  --fc-button-text-color: var(--ink-700);
  --fc-button-hover-bg-color: var(--qm-primary-50);
  --fc-button-hover-border-color: var(--qm-primary-100);
  --fc-button-active-bg-color: var(--qm-primary);
  --fc-button-active-border-color: var(--qm-primary);
  --fc-event-border-color: transparent;
  --fc-now-indicator-color: var(--qm-primary);
}

/* ---- Toolbar: bold title left, controls trail right ---- */
/* The grid's hairlines come from the app's own line token — FullCalendar's
   default #ddd read as broken, half-there ruling on the pink canvas. */
.fc { --fc-border-color: var(--line); --fc-today-bg-color: var(--qm-primary-50); }
.fc .fc-toolbar.fc-header-toolbar { margin-bottom: var(--sp-5); flex-wrap: wrap; gap: 12px; align-items: center; }
.fc .fc-toolbar-title { font-size: 1.6rem; font-weight: 800; color: var(--ink-900); letter-spacing: -.01em; }

/* Base button (prev / next / today) — clean pills */
.fc .fc-button { border-radius: var(--r-pill); padding: 7px 15px; min-height: 38px; font-weight: 600; font-size: var(--fs-400); line-height: 1.1; text-transform: none; box-shadow: none; transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), transform var(--t-fast); }
.fc .fc-button:hover { transform: translateY(-1px); color: var(--qm-primary-600); }
.fc .fc-button:focus-visible { outline: 0; box-shadow: 0 0 0 4px color-mix(in srgb, var(--qm-primary) 18%, transparent); }
.fc .fc-toolbar .fc-button-primary:disabled { background: var(--surface-2); border-color: var(--line); color: var(--ink-400); opacity: 1; border-radius: var(--r-pill); }
.fc .fc-toolbar .fc-today-button:disabled { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-700); font-weight: 700; opacity: .75; }

/* prev / next as round icon controls (kept OUT of a button-group in the JS
   toolbar config so these rules actually win) with sized, centred chevrons. */
/* Extra class in the selector (.fc-button.fc-prev-button) out-specifies
   FullCalendar's own runtime-injected .fc .fc-button rules, which otherwise
   tie and win on source order (FC injects its <style> after app.css). */
.fc .fc-button.fc-prev-button, .fc .fc-button.fc-next-button { width: 40px; height: 40px; padding: 0; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: #fff; border: 1px solid var(--line); color: var(--ink-700); box-shadow: var(--e-1); }
.fc .fc-button.fc-prev-button:hover, .fc .fc-button.fc-next-button:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-100); }
.fc .fc-button .fc-icon-chevron-left, .fc .fc-button .fc-icon-chevron-right { font-size: var(--fs-800); line-height: 1; width: auto; height: auto; vertical-align: middle; }
.fc .fc-button.fc-prev-button:hover .fc-icon, .fc .fc-button.fc-next-button:hover .fc-icon { color: var(--qm-primary-600); }

/* Today — branded ghost pill (was a washed-out grey) */
/* Today — branded ghost pill; the .fc-toolbar prefix out-specifies FullCalendar's
   own runtime-injected .fc-button rules (which otherwise tie and win on order). */
.fc .fc-toolbar .fc-today-button { border-radius: var(--r-pill); }
.fc .fc-toolbar .fc-today-button:not(:disabled) { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-700); font-weight: 700; }
.fc .fc-toolbar .fc-today-button:not(:disabled):hover { background: var(--qm-primary-100); border-color: var(--qm-primary-100); color: var(--qm-primary-700); }

/* Segmented view switcher (Month | Week | List) — mirrors the List|Calendar .btn-group */
/* Segmented Month|Week|List — pill container with the inner buttons ALSO pill-
   rounded (extra .fc-toolbar in the selector beats FullCalendar's runtime
   first/last-child 4px-corner rules, which caused the active button's corners
   not to match the rounded container). */
.fc .fc-toolbar .fc-button-group { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 3px; gap: 2px; }
.fc .fc-toolbar .fc-button-group .fc-button { background: transparent; border: 0; color: var(--ink-500); font-weight: 700; padding: 6px 16px; min-height: 32px; box-shadow: none; border-radius: var(--r-pill); }
.fc .fc-toolbar .fc-button-group .fc-button:first-child, .fc .fc-toolbar .fc-button-group .fc-button:last-child { border-radius: var(--r-pill); }
.fc .fc-toolbar .fc-button-group .fc-button:hover { background: #fff; color: var(--qm-primary-600); transform: none; }
.fc .fc-toolbar .fc-button-group .fc-button-primary:not(:disabled).fc-button-active { background: var(--qm-grad); color: var(--qm-on-primary); box-shadow: var(--e-primary); }
.fc .fc-toolbar .fc-button-group .fc-button.fc-button-active:hover { color: var(--qm-on-primary); }

/* Refresh — round icon button mirroring .qm-icon-btn (text is the refresh glyph from JS) */
.fc .fc-refresh-button { width: 40px; height: 40px; min-height: 40px; padding: 0; border-radius: 50%; background: #fff; border: 1px solid var(--line); color: var(--ink-700); font-size: 17px; line-height: 1; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--e-1); }
.fc .fc-refresh-button:hover { background: var(--qm-primary-50); border-color: var(--qm-primary-100); color: var(--qm-primary-600); }

/* ---- Column headers + grid ---- */
.fc .fc-col-header-cell { background: var(--surface-2); }
.fc .fc-col-header-cell-cushion { padding: 13px 4px; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-500); text-decoration: none; }
.fc .fc-col-header-cell.fc-day-sat .fc-col-header-cell-cushion,
.fc .fc-col-header-cell.fc-day-sun .fc-col-header-cell-cushion { color: var(--qm-primary-600); }
.fc .fc-scrollgrid { border-radius: var(--r-md); overflow: hidden; }

/* Day cells — weekend tint, non-current dimming, hover affordance */
.fc .fc-daygrid-day { transition: background var(--t-fast); }
.fc .fc-daygrid-day.fc-day-sat, .fc .fc-daygrid-day.fc-day-sun { background: color-mix(in srgb, var(--surface-2) 60%, #fff); }
.fc .fc-daygrid-day:hover { background: var(--qm-primary-50); }
.fc .fc-daygrid-day.fc-day-other { background: var(--surface-2); }
.fc .fc-daygrid-day.fc-day-other .fc-daygrid-day-number { color: var(--ink-400); font-weight: 600; }
.fc .fc-daygrid-day-number { padding: 8px 11px; font-size: 13.5px; font-weight: 700; color: var(--ink-700); text-decoration: none; }

/* Strong "Today" — brand pill on the date number (was faint pink + red digit) */
.fc .fc-day-today { background: var(--qm-primary-50) !important; }
.fc .fc-day-today:hover { background: var(--qm-primary-50); }
.fc .fc-day-today .fc-daygrid-day-number { background: var(--qm-grad); color: var(--qm-on-primary); border-radius: var(--r-pill); min-width: 26px; height: 26px; margin: 5px 6px 0 0; padding: 0 8px; display: inline-flex; align-items: center; justify-content: center; font-weight: 800; box-shadow: var(--e-primary); }

/* Roomy month cells — nothing enforced a minimum before, which made busy
   months read cramped; events keep the +N-more overflow behaviour. */
.fc .fc-daygrid-day-frame { min-height: 128px; }
.fc .fc-daygrid-day-events { margin-top: 2px; }
/* Drag-target highlight follows the brand tint instead of the library blue. */
.fc .fc-highlight { background: var(--qm-primary-50); }

/* ---- Event pills ---- */
.fc .fc-daygrid-event { border-radius: var(--r-pill); padding: 4px 12px; font-size: 12px; font-weight: 600; border: 0; box-shadow: var(--e-1); margin: 3px 8px 3px; transition: transform var(--t-fast), filter var(--t-fast), box-shadow var(--t-fast); }
.fc .fc-daygrid-event:hover { transform: translateY(-1px); filter: brightness(.96); box-shadow: var(--e-2); }
.fc .fc-h-event .fc-event-title, .fc .fc-h-event .fc-event-time { font-weight: 700; color: #fff; }
.fc .fc-h-event .fc-event-time { opacity: .85; margin-right: 2px; }
.fc .fc-event:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 2px; }

/* "+N more" chip + popover */
.fc .fc-more-link { font-size: 11.5px; font-weight: 700; color: var(--qm-primary); background: var(--qm-primary-50); border-radius: var(--r-pill); padding: 2px 10px; margin: 2px 8px; }
.fc .fc-more-link:hover { background: var(--qm-primary-100); }
.fc .fc-popover { border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--e-3); overflow: hidden; }
.fc .fc-popover-header { background: var(--surface-2); padding: 8px 10px; font-weight: 700; color: var(--ink-800); }
.fc .fc-popover-body { background: var(--surface); padding: 8px 6px; min-width: 200px; }
.fc .fc-popover-body .fc-daygrid-event { margin: 3px 4px; }

/* ---- timeGridWeek: slots, axis and events themed (were library-default) ---- */
.fc .fc-timegrid-now-indicator-line { border-color: var(--qm-primary); border-width: 2px; }
.fc .fc-timegrid-now-indicator-arrow { border-color: var(--qm-primary); color: var(--qm-primary); }
.fc .fc-timegrid-slot { height: 2.1em; }
.fc .fc-timegrid-slot-label-cushion, .fc .fc-timegrid-axis-cushion { font-size: 11.5px; font-weight: 600; color: var(--ink-400); }
.fc .fc-timegrid-event { border-radius: var(--r-sm); border: 0; box-shadow: var(--e-1); padding: 2px 6px; }
.fc .fc-timegrid-event .fc-event-main { font-size: 11.5px; font-weight: 600; }

/* ---- List view: themed typography + empty state ---- */
.fc .fc-list { border-radius: var(--r-md); overflow: hidden; border-color: var(--line); }
.fc .fc-list-event:hover td { background: var(--qm-primary-50); }
.fc .fc-list-day-cushion { background: var(--surface-2); font-weight: 700; color: var(--ink-800); }
.fc .fc-list-event-dot { border-color: currentColor; }
.fc .fc-list-event-time { font-size: 12.5px; font-weight: 600; color: var(--ink-500); }
.fc .fc-list-event-title { font-size: 13.5px; font-weight: 600; color: var(--ink-800); }
.fc .fc-list-empty { background: var(--surface); color: var(--ink-400); font-weight: 600; }
.fc a.fc-daygrid-day-number, .fc a.fc-col-header-cell-cushion, .fc a.fc-list-event-title { color: inherit; }

/* ---- Closed dates (blackouts) — tinted background days on the grid ---- */
.fc .fc-bg-event.qm-fc-closed { background: repeating-linear-gradient(-45deg, color-mix(in srgb, var(--danger) 10%, #fff), color-mix(in srgb, var(--danger) 10%, #fff) 7px, color-mix(in srgb, var(--danger) 5%, #fff) 7px, color-mix(in srgb, var(--danger) 5%, #fff) 14px); opacity: 1; }
.fc .fc-bg-event.qm-fc-closed .fc-event-title { position: absolute; top: 3px; left: 4px; right: 4px; color: var(--danger); font-style: normal; font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ============================================================
Reservations (lean, scoped)
============================================================ */
/* Booking source pill (Staff / Website / Walk-in …) */
.qm-src-pill { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; font-size: 11px; font-weight: 600; color: var(--ink-500); background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--r-pill); padding: 1px 8px; vertical-align: middle; }
.qm-src-pill i { font-size: 9px; }
/* Payment-brand logo (Visa / Mastercard / PayPal / Stripe), rendered by payment_method_icon().
   The source SVGs are white cards, so a hairline border + soft radius lets them read on a light
   surface; height is set inline (default 20px), width follows the native 47:30 ratio. */
.qm-pay-ico { width: auto; vertical-align: middle; border: 1px solid var(--line); border-radius: 4px; box-shadow: 0 1px 2px rgba(15, 23, 42, .08); }
.qm-pay-ico + .qm-pay-ico { margin-inline-start: 4px; }
/* Each booking source gets its own colour so the channel is readable at a glance
   (admin keeps the neutral grey base). Tinted-pill pattern = semantic token. */
.qm-src-pill.qm-src-website { color: var(--info); background: var(--info-bg); border-color: color-mix(in srgb, var(--info) 28%, transparent); }
.qm-src-pill.qm-src-phone { color: var(--ok); background: var(--ok-bg); border-color: color-mix(in srgb, var(--ok) 28%, transparent); }
.qm-src-pill.qm-src-walk_in { color: var(--warn); background: var(--warn-bg); border-color: color-mix(in srgb, var(--warn) 28%, transparent); }
.qm-src-pill.qm-src-staff { color: var(--qm-primary); background: var(--qm-primary-50); border-color: color-mix(in srgb, var(--qm-primary) 24%, transparent); }
/* Order channels reuse the same tinted-pill scale (vendor Orders list): in-store POS + self-serve kiosk. */
.qm-src-pill.qm-src-pos { color: var(--violet); background: var(--violet-bg); border-color: color-mix(in srgb, var(--violet) 28%, transparent); }
.qm-src-pill.qm-src-kiosk { color: var(--teal); background: var(--teal-bg); border-color: color-mix(in srgb, var(--teal) 28%, transparent); }
/* When the source pill opens a line (Orders identity cell) it needs no leading inline gap. */
.qm-cell-body > .qm-src-pill:first-child { margin-inline-start: 0; }
/* Bookings list: Source + Note are their own columns, so the source pill no longer
   needs the inline gap it used inside the guest cell, and long notes wrap in place. */
.qm-resv-srccol .qm-src-pill { margin-inline-start: 0; }
.qm-resv-notecol { max-width: 300px; white-space: normal; }
.qm-resv-notecol .qm-cell-sub + .qm-cell-sub { margin-top: 3px; }
/* Staff-only internal note row in the bookings list */
.qm-staff-note { color: var(--ink-500); }
.qm-staff-note .fa-lock { font-size: 10px; opacity: .7; margin-right: 2px; }
/* Booking-rules sub-group in Settings -> Dine-in */
.qm-resv-rules { margin-top: var(--sp-4, 16px); }
.qm-settings-sub-h { font-size: var(--fs-400); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-500); margin: 0 0 10px; }
/* Generic loading state for any primary button mid-submit (double-submit guard) */
.qm-btn.is-loading { opacity: .7; pointer-events: none; }
/* Calendar status ring (P3): pending bookings read as hollow/dashed pills */
.fc-event.qm-ev-pending { opacity: .85; border-style: dashed !important; }
.fc-event.qm-ev-waitlisted { opacity: .7; }
/* Reservation list filter bar (P3) */
.qm-resv-filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
/* Clickable status chips on the reservations list */
a.qm-pill { text-decoration: none; transition: box-shadow var(--t-fast) ease; }
a.qm-pill:hover { box-shadow: 0 0 0 2px var(--line-2); }
.qm-pill.is-active { box-shadow: 0 0 0 2px var(--qm-primary); }
/* Calendar loading veil */
#res-calendar-card.is-loading { opacity: .65; transition: opacity var(--t-fast) ease; }
/* Mobile floor plan + reservations (P4) */
@media (max-width: 768px) {
  /* Split scroll from size: the WRAPPER (constrained to the card width) is the
  scroll viewport; the INNER grid keeps its roomy min-width and overflows the
  wrapper, which scrolls. Co-locating overflow+min-width on one element clipped
  the right half unreachable at 375px. Mirrors .qm-cart-table-wrap. The drag
  math in tables-floor.js still lands drops correctly (getBoundingClientRect
  captures the wrapper scroll offset). */
  .qm-floor-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scroll-padding: 12px;
  }
  /* Let the browser own the horizontal pan here, or the grid's base
  touch-action:none swallows the swipe and the right half stays unreachable. */
  .qm-floor-grid { min-width: 640px; touch-action: pan-x; }
  /* On touch, surface the per-card actions (no hover) and give every
  interactive target ≥44px (WCAG 2.5.5 / 2.1 AA target size). */
  .qm-table-card { width: 132px; touch-action: none; }
  .qm-table-card .qm-tc-actions { opacity: 1; gap: 6px; top: 8px; right: 8px; }
  .qm-table-card .qm-tc-actions button { width: 44px; height: 44px; font-size: var(--fs-600); }
  .qm-table-card .qm-tc-status { min-height: 44px; padding: 6px 12px; font-size: 12px; }
  .qm-floor-ctl { min-height: 44px; }
  /* Reservations calendar: stack toolbar, keep touch targets large */
  .fc .fc-toolbar.fc-header-toolbar { flex-direction: column; align-items: stretch; gap: 8px; }
  .fc .fc-toolbar-title { font-size: 1.15rem; text-align: center; }
  .fc .fc-toolbar .fc-button-group { justify-content: center; }
  .fc .fc-toolbar .fc-button, .fc .fc-toolbar .fc-button-group .fc-button { min-height: 40px; }
  .fc .fc-toolbar .fc-button.fc-prev-button, .fc .fc-toolbar .fc-button.fc-next-button, .fc .fc-refresh-button { width: 44px; height: 44px; }
  .fc .fc-daygrid-event { font-size: 11px; padding: 2px 7px; }
}

/* ============================================================
Reservation add/edit form — guided, sectioned layout (scoped)
============================================================ */
.qm-resv-form-card { padding: 0; overflow: hidden; position: relative; }
.qm-resv-form-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--qm-grad); }
.qm-resv-form-head { padding: var(--sp-5) var(--sp-6) var(--sp-4); border-bottom: 1px solid var(--line-2); background: var(--surface-2); }
.qm-resv-form-head .qm-resv-form-eyebrow { display: inline-block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; color: var(--qm-primary-600); margin: 0 0 4px; }
.qm-resv-form-sub { color: var(--ink-500); font-size: 13.5px; margin: 0; max-width: 560px; }
.qm-resv-section { padding: var(--sp-5) var(--sp-6); }
.qm-resv-section + .qm-resv-section { border-top: 1px solid var(--line-2); }
.qm-resv-section .qm-settings-sub-h { display: flex; align-items: center; gap: 6px; margin-bottom: var(--sp-3); }
.qm-resv-section .qm-settings-sub-h i { color: var(--qm-primary); font-size: 12px; }
.qm-resv-actions { display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-4) var(--sp-6) var(--sp-5); border-top: 1px solid var(--line-2); background: var(--surface-2); }
.qm-resv-actions .qm-resv-note { color: var(--ink-400); }
@media (max-width: 768px) {
  .qm-resv-form-head, .qm-resv-section, .qm-resv-actions { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .qm-resv-actions { flex-wrap: wrap; }
  .qm-resv-actions .qm-btn { flex: 1 1 auto; justify-content: center; }
  .qm-resv-actions .qm-resv-note { flex-basis: 100%; }
}


/* ---- The product's ONE toggle switch -------------------------------------
   Every on/off control in the dashboards (Settings, Kiosks, admin pages) is a
   .form-switch input, fully re-skinned here: a soft pill track with an
   elevated knob that slides onto the brand gradient when on. Checkboxes keep
   their own square style but pick up the brand accent. */
.form-check-input:checked { background-color: var(--qm-primary); border-color: var(--qm-primary); }
.form-check-input:focus { border-color: var(--qm-primary); box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--qm-primary) 15%, transparent); }

.form-switch { padding-left: 0; min-height: 26px; display: flex; align-items: center; gap: 10px; }
.form-switch .form-check-label { cursor: pointer; }
/* Switch placement, decided once here rather than per page.
   The setting reads on the left and the control sits hard right, so a column of toggles
   lines up on a single edge and the eye can run down it. Done with `order` so the markup
   keeps the input before its label — that pairing is what makes the label clickable and
   what assistive technology reads — while the row renders label-first. Every consumer
   inherits it, which is why this belongs in the component and not in each view. */
.qm-dash .form-switch { justify-content: space-between; }
.qm-dash .form-switch .form-check-input { order: 2; }
.qm-dash .form-switch .form-check-label { order: 1; }
/* Inside a table cell the switch is usually the cell's whole content, so stretching it to
   the column width would strand it far from the row it belongs to. */
.qm-dash td .form-switch, .qm-dash th .form-switch { justify-content: flex-start; }
.form-switch .form-check-input {
  appearance: none; -webkit-appearance: none;
  width: 46px; height: 26px; margin-left: 0; margin-top: 0; flex-shrink: 0;
  border: 0; border-radius: var(--r-pill); cursor: pointer; position: relative;
  background-color: #dfe3e8; background-image: none;
  box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08);
  transition: background-color var(--t-fast);
}
.form-switch .form-check-input::after {
  content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px;
  border-radius: 50%; background: #fff;
  box-shadow: 0 2px 6px rgba(31, 11, 18, .28);
  transition: transform var(--t-fast);
}
.form-switch .form-check-input:checked {
  background-color: var(--qm-primary);
  background-image: var(--qm-grad);
  box-shadow: inset 0 1px 3px color-mix(in srgb, var(--qm-primary-600) 25%, transparent);
}
.form-switch .form-check-input:checked::after { transform: translateX(20px); }
.form-switch .form-check-input:focus { box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08), 0 0 0 4px color-mix(in srgb, var(--qm-primary) 15%, transparent); }
.form-switch .form-check-input:disabled { opacity: .5; cursor: not-allowed; }
/* Status variant of the switch (admin users): the track colour carries the
   meaning — red when off (suspended), green when on (active) — instead of the
   default grey/brand-gradient. Same pill + knob; colour only. */
.form-switch.qm-switch-status .form-check-input { background-color: var(--danger); background-image: none; box-shadow: inset 0 1px 3px color-mix(in srgb, var(--qm-primary-600) 18%, transparent); }
.form-switch.qm-switch-status .form-check-input:checked { background-color: var(--ok); background-image: none; box-shadow: inset 0 1px 3px rgba(6, 95, 70, .22); }

/* Email-template editor: live rendered-email preview frame (renders the branded email). */

/* --- Every dashboard checkbox IS the toggle switch -------------------------
   Promotes
   EVERY checkbox inside .qm-dash (vendor / admin / driver) to the same toggle so
   on/off controls read consistently. Scoped to .qm-dash so storefront + account
   checkboxes keep their own treatment. The various wrappers (.qm-check inline-
   flex, Bootstrap .form-check, bare labels) are normalised so the pill and its
   label always sit on one aligned row. Field names/values are unchanged.
   A RADIO inside a .qm-switch-row gets the same pill: an exclusive choice presented as one
   switch per option (the business-model picker) should look like every other on/off control,
   and keeping it a native radio means the browser enforces "only one" with no JS. Radios
   elsewhere in the dashboard are untouched - the switch look is opt-in via .qm-switch-row. */
.qm-dash input[type="checkbox"],
.qm-switch-row input[type="checkbox"],
.qm-switch-row input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  flex: 0 0 auto; width: 46px; height: 26px; min-width: 46px; margin: 0; padding: 0;
  border: 0; border-radius: var(--r-pill); cursor: pointer; position: relative; vertical-align: middle;
  background-color: #dfe3e8; background-image: none;
  box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08);
  transition: background-color var(--t-fast);
}
.qm-dash input[type="checkbox"]::after,
.qm-switch-row input[type="checkbox"]::after,
.qm-switch-row input[type="radio"]::after {
  content: ''; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px;
  border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(31, 11, 18, .28);
  transition: transform var(--t-fast);
}
.qm-dash input[type="checkbox"]:checked,
.qm-switch-row input[type="checkbox"]:checked,
.qm-switch-row input[type="radio"]:checked {
  background-color: var(--qm-primary); background-image: var(--qm-grad);
  box-shadow: inset 0 1px 3px color-mix(in srgb, var(--qm-primary-600) 25%, transparent);
}
.qm-dash input[type="checkbox"]:checked::after,
.qm-switch-row input[type="checkbox"]:checked::after,
.qm-switch-row input[type="radio"]:checked::after { transform: translateX(20px); }
.qm-dash input[type="checkbox"]:focus-visible,
.qm-switch-row input[type="checkbox"]:focus-visible,
.qm-switch-row input[type="radio"]:focus-visible { outline: 0; box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08), 0 0 0 4px color-mix(in srgb, var(--qm-primary) 15%, transparent); }
.qm-dash input[type="checkbox"]:disabled,
.qm-switch-row input[type="checkbox"]:disabled,
.qm-switch-row input[type="radio"]:disabled { opacity: .5; cursor: not-allowed; }
/* Normalise the wrappers so the pill aligns with its label on one row. */
.qm-dash .qm-check { align-items: center; gap: 10px; }
.qm-dash .form-check { padding-left: 0; display: flex; align-items: center; gap: 10px; min-height: 0; }
.qm-dash .form-check .form-check-input { margin: 0; float: none; }
.qm-dash .form-check-label { margin: 0; }
/* Exception: checkboxes that pair with an image or icon (the ingredient-builder
   picker with its ingredient photos, and the floor-plan lock) are SELECTORS, not
   plain on/off switches — keep their native brand-accent checkbox, never the pill. */
.qm-dash .qm-ing-builder input[type="checkbox"],
.qm-dash #qm-floor-lock {
  appearance: auto; -webkit-appearance: auto;
  width: auto; height: auto; min-width: 0; flex: 0 0 auto;
  border-radius: 0; background: none; box-shadow: none;
  accent-color: var(--qm-primary); vertical-align: middle;
}
.qm-dash .qm-ing-builder input[type="checkbox"]::after,
.qm-dash #qm-floor-lock::after { content: none; }

/* ---- Restaurant brandmark: one standardized logo across dashboard brand,
   settings preview, storefront hero, cards and checkout. Renders the uploaded
   logo when present, else a branded initials monogram (never the platform logo). */
.qm-brandmark {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; overflow: hidden; border-radius: var(--r-md, 12px);
  background: var(--surface-2); vertical-align: middle;
}
.qm-brandmark-img { object-fit: cover; }
.qm-brandmark-mono {
  color: #fff; font-weight: 800; line-height: 1;
  letter-spacing: .5px; text-transform: uppercase;
}
.qm-brandmark--sm { width: 32px; height: 32px; border-radius: 9px;  font-size: var(--fs-400); }
.qm-brandmark--md { width: 44px; height: 44px; border-radius: 11px; font-size: 17px; }
.qm-brandmark--lg { width: 64px; height: 64px; border-radius: 15px; font-size: 24px; }
.qm-brandmark--xl { width: 92px; height: 92px; border-radius: 22px; font-size: 34px; }
.qm-brandmark--round { border-radius: 50%; }
/* Deterministic, brand-neutral monogram palette (chosen by name hash) so each
   restaurant reads as distinct and never borrows the marketplace's brand colour.
   Doubled class keeps the gradient winning over any surface-specific background
   (e.g. the hero logo frame's white fill). */
.qm-brandmark.qm-brandmark-c1 { background: linear-gradient(135deg, #ff6b6b, #e8590c); }
.qm-brandmark.qm-brandmark-c2 { background: linear-gradient(135deg, #7b61ff, #4dabf7); }
.qm-brandmark.qm-brandmark-c3 { background: linear-gradient(135deg, #20c997, #0ca678); }
.qm-brandmark.qm-brandmark-c4 { background: linear-gradient(135deg, #f783ac, #e64980); }
.qm-brandmark.qm-brandmark-c5 { background: linear-gradient(135deg, #ffa94d, #f59f00); }
.qm-brandmark.qm-brandmark-c6 { background: linear-gradient(135deg, #4dabf7, #1c7ed6); }

/* Country flag shown before an option's name in a flag-enabled Choices select (cuisine picker). */
.qm-choice-flag { width: 20px; height: 20px; object-fit: cover; border-radius: 50%; margin-right: 9px; vertical-align: middle; flex: 0 0 auto; box-shadow: inset 0 0 0 1px rgba(0,0,0,.1); }
.choices__item .qm-choice-flag { display: inline-block; }

/* ============ Vendor · Order detail ============ */
.qm-od-topbar { display: flex; justify-content: flex-end; margin-bottom: var(--sp-4); }
.qm-od-h { display: flex; align-items: center; gap: 10px; font-size: var(--fs-600); font-weight: 700; color: var(--ink-800); margin: 0 0 var(--sp-4); }
.qm-od-h .qm-od-ic { width: 32px; height: 32px; border-radius: var(--r-md); background: var(--qm-primary-50); color: var(--qm-primary-600); display: grid; place-items: center; font-size: 14px; flex: 0 0 auto; }
.qm-od-h .qm-od-hx { margin-left: auto; font-weight: 600; color: var(--ink-400); font-size: 12.5px; }
/* items */
.qm-od-item { display: flex; align-items: center; gap: 12px; padding: 11px 0; }
.qm-od-item + .qm-od-item { border-top: 1px solid var(--line-2); }
.qm-od-item > img { width: 46px; height: 46px; border-radius: var(--r-md); object-fit: cover; background: var(--surface-2); flex: 0 0 auto; }
.qm-od-item .nm { font-weight: 600; font-size: 14px; line-height: 1.3; }
.qm-od-item .sub { font-size: 12px; color: var(--ink-400); }
.qm-od-item .tot { margin-left: auto; font-weight: 700; white-space: nowrap; }
/* summary */
.qm-od-sum { margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px dashed var(--line); }
.qm-od-sum-r { display: flex; justify-content: space-between; padding: 4px 0; font-size: 13.5px; }
.qm-od-sum-r > span:first-child { color: var(--ink-400); }
.qm-od-total { display: flex; justify-content: space-between; align-items: center; margin-top: 10px; padding: 12px 16px; border-radius: var(--r-md); background: var(--qm-primary-50); }
.qm-od-total .l { font-weight: 700; }
.qm-od-total .v { font-weight: 800; font-size: var(--fs-800); color: var(--qm-primary-600); }
/* customer */
.qm-od-cust { display: flex; align-items: center; gap: 14px; margin-bottom: var(--sp-3); }
.qm-od-cust .nm { font-weight: 700; font-size: var(--fs-600); }
.qm-od-cust .ml { font-size: 12.5px; color: var(--ink-400); word-break: break-word; }
.qm-od-addr { display: flex; gap: 9px; padding: 12px 14px; border-radius: var(--r-md); background: var(--surface-2); font-size: 13.5px; margin-bottom: var(--sp-3); }
.qm-od-addr i { color: var(--qm-primary-600); margin-top: 3px; }
.qm-od-meta-r { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; font-size: 13.5px; border-top: 1px solid var(--line-2); }
.qm-od-meta-r:first-child { border-top: 0; }
.qm-od-meta-r .k { color: var(--ink-400); flex: 0 0 auto; }
.qm-od-meta-r .v { font-weight: 600; text-align: right; }
/* journey — each checkpoint carries a face (avatar / restaurant brandmark), a channel
   glyph, or a tinted status icon as a genuine fallback; never an empty dot. */
.qm-od-journey { list-style: none; margin: 0; padding: 0; }
.qm-od-journey li { position: relative; padding: 0 0 var(--sp-4) 48px; min-height: 34px; }
.qm-od-journey li::before { content: ""; position: absolute; left: 16px; top: 38px; bottom: -2px; width: 2px; background: var(--line); }
.qm-od-journey li:last-child { padding-bottom: 0; }
.qm-od-journey li:last-child::before { display: none; }
/* marker slot — fixed circle that frames whatever it holds identically */
.qm-od-jic { position: absolute; left: 0; top: 0; width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center; font-size: 12px; overflow: hidden; }
.qm-od-jic.s { background: var(--qm-primary-50); color: var(--qm-primary-600); }
.qm-od-jic.pay { background: var(--ok-bg); color: var(--ok); }
.qm-od-jic.ref { background: var(--danger-bg); color: var(--danger); }
.qm-od-jic.rider { background: var(--info-bg); color: var(--info); }
.qm-od-jic.ph { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .07); }
.qm-od-jic > img { width: 100%; height: 100%; object-fit: cover; }
/* restaurant brandmark reused from the shared helper, sized to fill the slot */
.qm-od-jic .qm-brandmark { width: 100%; height: 100%; border-radius: 0; font-size: var(--fs-400); }
/* activity badge overlapping a face so payment / refund / hand-off stays legible */
.qm-od-jb { position: absolute; left: 21px; top: 20px; width: 16px; height: 16px; border-radius: 50%; display: grid; place-items: center; font-size: 8px; color: #fff; box-shadow: 0 0 0 2px var(--surface, #fff); }
.qm-od-jb.b-pay { background: var(--ok); }
.qm-od-jb.b-ref { background: var(--danger); }
.qm-od-jb.b-rider { background: var(--info); }
.qm-od-jbody { border-radius: var(--r-md); }
.qm-od-jt { font-weight: 600; font-size: 13.5px; padding-top: 1px; }
.qm-od-jm { font-size: 12px; color: var(--ink-400); margin-top: 1px; }
.qm-od-jm .d { color: var(--ink-500); font-weight: 600; }
/* longest-wait checkpoint — highlighted so a bottleneck stands out at a glance */
.qm-od-journey li.hot .qm-od-jbody { background: var(--warn-bg); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warn) 24%, transparent); padding: 8px 12px; margin: -6px -10px 0; }
.qm-od-journey li.hot .qm-od-jm .d { color: var(--warn); font-weight: 700; }
.qm-od-hot { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; font-size: 11px; font-weight: 700; color: var(--warn); background: color-mix(in srgb, var(--warn) 13%, transparent); padding: 1px 8px; border-radius: var(--r-pill); vertical-align: middle; }
.qm-od-hot i { font-size: 9px; }
/* journey roll-up — a compact 3-up strip tucked under the timeline (mirrors the
   order-summary footer), not a space-hungry band */
.qm-od-jsum { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: var(--sp-3); padding-top: var(--sp-3); border-top: 1px dashed var(--line); }
.qm-od-jsum > div { text-align: center; padding: 0 6px; }
.qm-od-jsum > div + div { border-left: 1px solid var(--line-2); }
.qm-od-jsum .v { display: block; font-size: var(--fs-700); font-weight: 800; color: var(--ink-800); line-height: 1.15; }
.qm-od-jsum .k { display: block; font-size: 11.5px; color: var(--ink-400); margin-top: 2px; }
/* delivery-route map + its pickup/drop-off caption */
.qm-od-map { margin-bottom: var(--sp-3); }
.qm-od-route { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.qm-od-route-pt { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--r-md); background: var(--surface-2); }
.qm-od-route-pt .qm-brandmark { flex: 0 0 auto; }
.qm-od-route-pt .t { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; color: var(--ink-400); }
.qm-od-route-pt .s { font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); margin-top: 1px; }
@media (max-width: 575.98px) { .qm-od-route { grid-template-columns: 1fr; } }

/* ============ Vendor · Get started (onboarding setup) ============ */
/* Hero: a subtle state-tinted gradient (green once the required steps are
done, amber while work remains) - the same radial-gradient + tinted-border
formula as the Dashboard's .qm-kpi-rich accent cards, keyed off --hc, plus a
large low-opacity watermark icon in the corner (same trick as .qm-kpi-bg). */
.qm-setup-hero { display: flex; align-items: center; gap: var(--sp-5); position: relative; overflow: hidden; isolation: isolate;
  background: radial-gradient(135% 160% at 100% 0, color-mix(in srgb, var(--hc, var(--qm-primary)) 10%, #fff) 0, var(--surface) 60%);
  border-color: color-mix(in srgb, var(--hc, var(--qm-primary)) 20%, var(--line)); }
.qm-setup-hero-bg { position: absolute; right: -14px; bottom: -22px; z-index: 0; font-size: 130px; line-height: 1; color: var(--hc, var(--qm-primary)); opacity: .07; transform: rotate(-14deg); pointer-events: none; }
.qm-setup-gauge, .qm-setup-hero-tx { position: relative; z-index: 1; }
.qm-setup-gauge { width: 108px; height: 108px; border-radius: 50%; flex: 0 0 auto; display: grid; place-items: center;
  background: conic-gradient(var(--gc) calc(var(--pct, 0) * 1%), var(--line-2) 0); }
.qm-setup-gauge::before { content: ""; position: absolute; inset: 10px; border-radius: 50%; background: var(--surface, #fff); }
.qm-setup-gauge.great, .qm-setup-gauge.good { --gc: var(--ok); }
.qm-setup-gauge.todo { --gc: var(--warn); }
.qm-setup-pct { position: relative; font-size: 27px; font-weight: 800; color: var(--ink-800); line-height: 1; }
.qm-setup-pct span { font-size: 14px; font-weight: 700; color: var(--ink-400); }
.qm-setup-hero-tx { flex: 1 1 auto; min-width: 0; }
.qm-setup-hero-tx :is(h1,h2,h3,h4,h5,h6) { margin: 0 0 4px; font-size: var(--fs-900); }
.qm-setup-hero-tx p { margin: 0 0 10px; color: var(--ink-500); font-size: 14px; }
.qm-setup-progress { height: 9px; border-radius: var(--r-pill); background: var(--line-2); overflow: hidden; margin: 0 0 12px; }
.qm-setup-progress > span { display: block; height: 100%; border-radius: inherit; background: var(--qm-grad); transition: width .4s ease; }
.qm-setup-progress.is-done > span { background: var(--ok); }
.qm-setup-legend { display: flex; flex-wrap: wrap; gap: 16px; font-size: 12.5px; font-weight: 600; }
.qm-setup-legend .done { color: var(--ok); }
.qm-setup-legend .required { color: var(--danger); }
.qm-setup-legend .skipped { color: var(--warn); }
/* Group card — the SAME accent-card formula as the Dashboard's .qm-kpi-rich
cards: a --kc-tinted radial gradient, tinted border, a rotated low-opacity
watermark of the group's own icon, and a lift + colour-glow on hover. */
/* Get-started groups pack into two masonry columns on wide screens, so the checklist reads at a
   glance instead of scrolling one tall column; each card stays whole (break-inside: avoid). */
.qm-setup-groups { column-gap: var(--sp-5); }
@media (min-width: 992px) { .qm-setup-groups { columns: 2; } }
.qm-setup-groups > .qm-setup-group { break-inside: avoid; }
.qm-setup-group { position: relative; overflow: hidden; isolation: isolate;
  background: radial-gradient(135% 130% at 100% 0, color-mix(in srgb, var(--kc, var(--qm-primary)) 13%, #fff) 0, var(--surface) 58%);
  border: 1px solid color-mix(in srgb, var(--kc, var(--qm-primary)) 20%, var(--line));
  border-top: 3px solid var(--kc, var(--qm-primary));
  transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.qm-setup-group:hover { transform: translateY(-3px); box-shadow: 0 16px 30px color-mix(in srgb, var(--kc, var(--qm-primary)) 22%, transparent); border-color: color-mix(in srgb, var(--kc, var(--qm-primary)) 40%, var(--line)); }
.qm-setup-group-bg { position: absolute; right: -10px; bottom: -16px; z-index: 0; font-size: 86px; line-height: 1; color: var(--kc, var(--qm-primary)); opacity: .08; transform: rotate(-10deg); pointer-events: none; transition: transform .5s cubic-bezier(.34,1.45,.5,1), opacity var(--t); }
.qm-setup-group:hover .qm-setup-group-bg { transform: rotate(2deg) scale(1.1); opacity: .13; }
.qm-setup-ghead, .qm-setup-list { position: relative; z-index: 1; }
.qm-setup-ghead { display: flex; align-items: center; gap: 12px; margin-bottom: var(--sp-3); }
.qm-setup-gic { width: 40px; height: 40px; border-radius: var(--r-md); background: color-mix(in srgb, var(--kc, var(--qm-primary)) 16%, #fff); color: var(--kc, var(--qm-primary-600)); display: grid; place-items: center; font-size: var(--fs-700); flex: 0 0 auto; }
.qm-setup-gt { flex: 1 1 auto; min-width: 0; }
.qm-setup-gt :is(h1,h2,h3,h4,h5,h6) { margin: 0; font-size: var(--fs-700); }
.qm-setup-gt p { margin: 0; font-size: 12.5px; color: var(--ink-400); }
.qm-setup-gcount { font-size: var(--fs-600); font-weight: 800; color: var(--ink-700); white-space: nowrap; }
.qm-setup-gcount span { color: var(--ink-400); font-weight: 600; }
/* checklist rows — each links to the page that completes it; the icon chip is
now colour-coded per status (not just 'done'), matching the .qm-setup-st pill
beside it, so a row's state reads at a glance instead of only via the pill. */
.qm-setup-list { display: flex; flex-direction: column; gap: 8px; }
.qm-setup-row { display: flex; align-items: center; gap: 13px; padding: 12px 14px; border-radius: var(--r-md); background: color-mix(in srgb, var(--surface) 60%, var(--surface-2)); border-left: 3px solid var(--line); text-decoration: none; color: inherit; transition: box-shadow .15s, transform .15s, background .15s; }
.qm-setup-row:hover { box-shadow: 0 2px 12px rgba(0, 0, 0, .07); transform: translateX(2px); background: var(--surface); }
.qm-setup-row.done { border-left-color: var(--ok); }
.qm-setup-row.required { border-left-color: var(--danger); }
.qm-setup-row.skipped { border-left-color: var(--warn); }
.qm-setup-ic { width: 38px; height: 38px; border-radius: var(--r-md); display: grid; place-items: center; font-size: var(--fs-600); flex: 0 0 auto; background: var(--surface); color: var(--ink-500); }
.qm-setup-row.done .qm-setup-ic { background: var(--ok-bg); color: var(--ok); }
.qm-setup-row.required .qm-setup-ic { background: var(--danger-bg); color: var(--danger); }
.qm-setup-row.skipped .qm-setup-ic { background: var(--warn-bg); color: var(--warn); }
.qm-setup-tx { flex: 1 1 auto; min-width: 0; }
.qm-setup-tx .nm { display: block; font-weight: 700; font-size: 14px; color: var(--ink-800); }
.qm-setup-tx .ds { display: block; font-size: 12.5px; color: var(--ink-400); }
.qm-setup-st { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; padding: 4px 10px; border-radius: var(--r-pill); white-space: nowrap; flex: 0 0 auto; }
.qm-setup-st.done { background: var(--ok-bg); color: var(--ok); }
.qm-setup-st.required { background: var(--danger-bg); color: var(--danger); }
.qm-setup-st.skipped { background: var(--warn-bg); color: var(--warn); }
.qm-setup-go { color: var(--ink-300); font-size: 12px; flex: 0 0 auto; }
@media (prefers-reduced-motion: reduce) { .qm-setup-group, .qm-setup-group-bg { transition: none; } }

/* Clear-database rows: same generic --rc-accent mechanism as .qm-setup-group
   above (icon + border + wash all keyed off one custom property, per row) so a
   list of many data-type toggles reads as a colourful family instead of a flat
   list of identical grey rows. Whole clickable <label> (not just the switch)
   for a larger, more forgiving tap/click target. */
.qm-clr-row { display: flex; align-items: center; gap: 14px; padding: 13px 16px; margin: 0; cursor: pointer;
  border-radius: var(--r-md); border-left: 3px solid var(--rc, var(--qm-primary));
  background: color-mix(in srgb, var(--rc, var(--qm-primary)) 5%, var(--surface));
  transition: background .15s, box-shadow .15s; }
.qm-clr-row:hover { background: color-mix(in srgb, var(--rc, var(--qm-primary)) 10%, var(--surface)); box-shadow: 0 2px 12px rgba(0,0,0,.06); }
.qm-clr-row + .qm-clr-row { margin-top: 8px; }
.qm-clr-row.is-disabled { cursor: default; opacity: .55; }
.qm-clr-row.is-disabled:hover { background: color-mix(in srgb, var(--rc, var(--qm-primary)) 5%, var(--surface)); box-shadow: none; }
.qm-clr-ic { width: 42px; height: 42px; border-radius: var(--r-md); display: grid; place-items: center; font-size: 17px; flex: 0 0 auto;
  background: color-mix(in srgb, var(--rc, var(--qm-primary)) 16%, #fff); color: var(--rc, var(--qm-primary)); }
.qm-clr-body { flex: 1 1 auto; min-width: 0; }
.qm-clr-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.qm-clr-head strong { font-size: 14.5px; color: var(--ink-800); }
.qm-clr-body small { display: block; font-size: 12.5px; color: var(--ink-500); margin-top: 2px; }
.qm-clr-toggle { flex: 0 0 auto; }
@media (max-width: 640px) { .qm-clr-row { flex-wrap: wrap; } .qm-clr-toggle { margin-inline-start: 56px; } }
@media (max-width: 640px) {
  .qm-setup-hero { flex-direction: column; text-align: center; }
  .qm-setup-tx .ds { display: none; }
  .qm-setup-st { padding: 4px 8px; }
}

/* ============================================================================
33. MOBILE RESPONSIVE REFINEMENTS
Phone / large-phone polish: comfortable 40–44px tap targets on primary controls,
touch-swipeable rails, sentences that wrap, and bands that breathe. Each override
sits after its base rule so the cascade applies it at the small breakpoints.
========================================================================== */

/* Restore native touch-swipe on the "What are you craving?" filter rail. The arrow
   buttons are a desktop-only enhancement: on mobile they indent the first chip out of
   line with the heading and clip the next chip, so hide them and let the strip swipe.
   Zeroing the strip's left padding lines the first chip up flush under the title. */
@media (max-width: 767.98px) {
  .qm-craving-track { overflow-x: auto; touch-action: pan-x; -webkit-overflow-scrolling: touch; }
  .qm-craving-arrow { display: none; }
  .qm-craving-strip.qm-carousel-track { padding: 12px 12px 14px 0; scroll-padding-inline: 0; }
}

/* Hero sliders: when the slide stacks to one column the centred prev/next arrows
   fall over the copy/CTA — hide them on mobile and navigate by the dots. */
@media (max-width: 860px) { .qm-heroslider-btn { display: none; } }
/* Give the 10px pagination dots a taller touch area: 44px down the page, and across it
   only as far as the row's 9px gap allows. A symmetric 44px pad reached 17px out of a
   10px dot whose neighbour's centre is 19px away, and since every pad is z-index:auto
   the later sibling won the point — a press on dot N selected dot N+1, and the slide
   before the last one could not be reached at all. 4px a side is the widest the row
   carries without two pads touching; widening it again needs a wider gap above. */
.qm-heroslider-dot { position: relative; }
.qm-heroslider-dot::before { content: ""; position: absolute; inset: -17px -4px; }

/* Food card: let a long dish name + "Popular" chip wrap instead of overflowing. */
.qm-food-head { flex-wrap: wrap; }
.qm-food-name { min-width: 0; overflow-wrap: anywhere; }

/* Tip chips share their row height and clear the 44px touch floor. */
.qm-tip-chip { display: flex; }
.qm-tip-chip span { flex: 1; justify-content: center; min-height: 44px; }

/* Password show/hide button: 40px hit area kept inside the input's padding. */
.qm-pw-wrap .form-control { padding-right: 48px; background-image: none; }
.qm-pw-toggle { width: 40px; height: 40px; right: 4px; }

/* Article body: long code lines / wide tables scroll instead of clipping. */
.qm-article-body pre { max-width: 100%; overflow-x: auto; }
.qm-article-body table { display: block; overflow-x: auto; }

/* Restaurant list: the filter panel is a normal always-visible sidebar on
   tablet/desktop (no dependency on any toggle state). Only phones collapse it
   behind a tap-to-open "Filters" bar, driven by a checkbox + :has() so the
   desktop panel can never be accidentally hidden. */
.qm-filter-toggle { display: none; }
@media (max-width: 767.98px) {
  .qm-filter-toggle { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 12px 16px; font-weight: 700; color: var(--ink-900); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; }
  .qm-filter-shell > .qm-filter-card { display: none; margin-top: var(--sp-3); }
  .qm-filter-shell:has(.qm-filter-open-cb:checked) > .qm-filter-card { display: block; }
}

/* Primary CTAs / toggles reach the standard hit area on small screens. */
@media (max-width: 767.98px) {
  .qm-food-add { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 9px 18px; }
  /* Three columns is tight on a phone: shrink the photo so the name and the Add
     button both keep usable width. */
  .qm-food { gap: 12px; padding: 12px; }
  .qm-food-media, .qm-food-media img { width: 96px; }
  .qm-food-media img { height: 96px; }
  .qm-pop-add { min-height: 40px; }
  .qm-view-btn { min-width: 40px; min-height: 40px; }
  .qm-partner-hero { padding: var(--sp-8) 0; }
  /* Stats band: 2-up across the whole large-phone range, with room to breathe.
  The desktop-only 78% inset (there to float the band off the drone photo
  behind it) is reset full-width here since the overlap itself is disabled
  on mobile - a narrowed band would just waste screen width for no reason. */
  .qm-stats { padding: var(--sp-6) var(--sp-5); max-width: 100%; }
  .qm-stats-grid { grid-template-columns: repeat(2, 1fr); gap: 30px 16px; }
  /* Cart: reserve a right gutter so the name never sits under the remove button. */
  .qm-cart-table tr { padding-right: calc(44px + var(--sp-2)); }
  /* Reserve form: stack the two action buttons full-width on small phones. */
  .qm-form-actions { flex-wrap: wrap; }
  .qm-form-actions .qm-btn { flex: 1 1 100%; }
}

/* Cart remove control matches the 44px stepper standard. */
@media (max-width: 991.98px) {
  .qm-cart-td-remove button { width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; }
}

/* Text row-actions + active-filter chips get a finger-sized target on touch,
   matching the .qm-icon-btn precedent. */
@media (pointer: coarse) {
  .qm-btn-sm, .qm-btn-xs { min-height: 44px; }
  .qm-filter-chip { min-height: 44px; }
  /* Small icon-only circles that were missed by the pass above - same
  precedent as .qm-icon-btn, growing the tap target to the 44px floor. */
  .qm-share-btn, .qm-view-btn, .qm-pop-add, .qm-fav-btn, .qm-ftr-social a, .qm-ob-food-add, .qm-ob-qty button { min-width: 44px; min-height: 44px; }
  .qm-img-remove-btn { min-width: 44px; min-height: 44px; top: -11px; right: -11px; }
  .qm-deal-code { min-height: 44px; }
}

/* ---- Impersonation bar -------------------------------------------------
   Shown on every page while a super admin is acting as another account. A muted
   warning red (#c0392b) — a standing caution about WHO you are — deliberately
   deeper than the site's bright primary crimson so it never reads as brand chrome. */
.qm-imp-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  background: #c0392b; color: #fff; border: 1px solid #a5281b; border-radius: var(--r-md);
  padding: 10px 14px; margin: var(--sp-4) 0; font-size: 14px; font-weight: 600;
  box-shadow: var(--e-1); }
.qm-imp-msg { min-width: 0; }
.qm-imp-form { margin: 0; flex-shrink: 0; }
.qm-imp-exit { display: inline-flex; align-items: center; gap: 6px; background: #fff; color: #c0392b;
  border: 0; border-radius: var(--r-pill); padding: 7px 14px; font-size: var(--fs-400); font-weight: 700;
  cursor: pointer; transition: background var(--t-fast), color var(--t-fast); }
.qm-imp-exit:hover { background: #fbe3df; color: #a5281b; }
.qm-imp-exit:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
/* On a phone the message and the way out stack, and the button spans the row so it stays a
   comfortable tap target instead of a chip squeezed against the edge. */
@media (max-width: 575.98px) {
  .qm-imp-bar { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; font-size: var(--fs-400); }
  .qm-imp-form { width: 100%; }
  .qm-imp-exit { width: 100%; justify-content: center; padding: 9px 14px; }
}

/* ============================================================
   Circular avatar / logo uploader — FilePond `data-pond-circle`
   One uploader look for every role: a dashed circle the chosen
   image fills, with the remove (X) centred at the bottom. Paired
   with the data-pond-circle branch in qm-pickers.js (which also
   sets stylePanelLayout: 'compact circle').
   ============================================================ */
.qm-pond-circle.filepond--root {
  width: 150px;
  height: 150px;
  margin: 0;
}
.qm-pond-circle .filepond--panel-root {
  border-radius: 50%;
  background-color: transparent;
  border: 2px dashed var(--qm-border, #d7d2c8);
}
.qm-pond-circle .filepond--drop-label {
  min-height: 150px;
  border-radius: 50%;
  color: var(--qm-muted, #6b7280);
  font-size: var(--fs-400);
  line-height: 1.4;
}
.qm-pond-circle .filepond--label-action { text-decoration-color: currentColor; }
.qm-pond-circle .filepond--image-preview-wrapper,
.qm-pond-circle .filepond--image-preview,
.qm-pond-circle .filepond--image-clip { border-radius: 50%; }
.qm-pond-circle .filepond--file-info,
.qm-pond-circle .filepond--file-status { display: none; }

/* Interactive crop modal (Cropper.js) shared by every circular uploader — avatars, restaurant logo,
   brand avatar. Built in qm-pickers.js; the stage hosts the Cropper canvas, actions align right. */
.qm-cropper-ov { position: fixed; inset: 0; z-index: 3000; background: rgba(0, 0, 0, .62); display: flex; align-items: center; justify-content: center; padding: 20px; }
.qm-cropper-box { background: var(--surface, #fff); border-radius: var(--r-md, 12px); padding: 16px; width: min(560px, 96vw); display: flex; flex-direction: column; gap: 14px; }
.qm-cropper-stage { max-height: 70vh; overflow: hidden; }
.qm-cropper-stage img { max-width: 100%; display: block; }
.qm-cropper-actions { display: flex; justify-content: flex-end; gap: 10px; }

/* ============================================================================
   Media library — the shared pick-or-upload field + picker modal
   (core/Helpers.php media_field_html(), partials/media-picker.php,
   assets/js/qm-media-picker.js). Reuses the design tokens + qm-btn styling.
   ========================================================================== */
/* Media-picker component palette → mapped onto the design system so the field preview,
   the library modal, its tiles and tabs all read on-brand instead of generic grey.
   These three names were used across the picker but never defined (they fell back to
   hardcoded #e5e7eb/#f5f6f8/#8a8f98); aliasing them here fixes every usage at once. */
:root { --qm-border: var(--line); --qm-fill: var(--surface-2); --qm-muted: var(--ink-400); }
/* The field is a self-contained card: header (title + a slot for a future AI action),
   body (live preview + optional hint), footer (Remove / Change, right-aligned). A
   title-less caller (vendor settings, campaigns) renders no header and the card is just
   the preview + the action row it always was, only now boxed for consistency. */
.qm-media-field { display: flex; flex-direction: column; height: 100%; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--e-1); }
.qm-media-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.qm-media-card-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: .95rem; color: var(--ink-900); }
.qm-media-card-title .qm-tip { font-size: .85rem; }
.qm-media-card-head-actions { display: inline-flex; align-items: center; gap: 8px; }
.qm-media-card-body { padding: 14px; display: flex; flex-direction: column; gap: 10px; flex: 1 1 auto; }
.qm-media-card-foot { display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap; padding: 12px 14px; border-top: 1px solid var(--line); background: transparent; }
.qm-media-preview { position: relative; width: 100%; height: 160px; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface-2); display: flex; align-items: center; justify-content: center; }
.qm-media-preview [data-media-thumb] { width: 100%; height: 100%; object-fit: contain; display: block; padding: 8px; }
.qm-media-preview.is-empty { cursor: pointer; border-style: dashed; }
.qm-media-empty { display: inline-flex; align-items: center; gap: 8px; color: var(--ink-400); font-size: .9rem; padding: 12px; text-align: center; }
.qm-media-empty i { font-size: 1.4rem; opacity: .7; }
/* ---- Storefront-section editors on Settings → Business model (admin/settings.php) ----
   A row of section cards. `minmax(400px, …)`, not `1fr`: a `1fr` track is `minmax(auto, 1fr)`, so a
   media field's min-content width pushed the card wider than its column and the picker hung out
   past the card's edge.

   `auto-fit` rather than a fixed two: switching delivery off removes both of its cards, and the
   steps card left behind should have the row, not sit in a half-width column beside nothing. The
   empty track collapses on its own, so no class and no script has to know it happened. */
/* Two tracks, not auto-fit. A .qm-pc-card-wide spans grid-column: 1 / -1, which OCCUPIES every
   track, so auto-fit could never collapse a spare one: at 1278px it built three tracks and the
   two normal cards took one each, leaving the third reserved and empty on the right. */
.qm-pc-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
.qm-pc-cards > * { min-width: 0; }
/* The delivery-gated pair must collapse together under ONE switch, but they do not sit together:
   the "why us" card shares a row with the steps while the delivery band runs the full width below.
   `display: contents` lets the wrapper group them for the toggle without becoming a grid cell —
   .d-none's `!important` still beats it, so switching delivery off takes both away. */
.qm-pc-contents { display: contents; }
/* Full width, and ordered after the two half cards it is declared alongside. Across that width its
   fields pair up two per line, which is the shape they had before the columns existed. */
.qm-pc-card-wide { grid-column: 1 / -1; order: 1; }
.qm-pc-card-wide > .qm-card-body { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 16px; align-items: start; }
@media (max-width: 600px) { .qm-pc-card-wide > .qm-card-body { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 1200px) { .qm-pc-cards { grid-template-columns: minmax(0, 1fr); } }
/* One field per line: an eyebrow, a heading and a subheading are a short line each, and a
   two-column grid only made the card wider than its content. */
.qm-pc-row { min-width: 0; margin-bottom: 14px; }
.qm-pc-row:last-child { margin-bottom: 0; }
/* A repeating block (a step, a feature card), folded. Mirrors the account-nav <details>: the
   native marker goes, and the caret we draw turns instead. */
.qm-pc-fold { border: 1px solid var(--line); border-radius: var(--r-md); margin-top: 10px; background: var(--surface); }
.qm-pc-fold > summary { display: flex; align-items: center; gap: 10px; padding: 10px 14px; font-weight: 600; font-size: .95rem; color: var(--ink-900); cursor: pointer; list-style: none; }
.qm-pc-fold > summary::-webkit-details-marker { display: none; }
/* The name takes the slack and stays hard left, whatever sits beside it — adding the grip in front
   of it must not push the title toward the middle of the row. */
.qm-pc-fold > summary > span { flex: 1 1 auto; min-width: 0; text-align: left; }
.qm-pc-fold-icon { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-400); color: var(--qm-primary); background: var(--qm-primary-50); border-radius: 50%; flex: 0 0 auto; }
.qm-pc-fold-caret { font-size: 11px; color: var(--ink-400); transition: transform var(--t-fast); }
.qm-pc-fold[open] .qm-pc-fold-caret { transform: rotate(180deg); }
/* A run of fields. One per line, EXCEPT a glyph picker, which sits on the same line as the heading
   it belongs to — an icon is a property of its card, not a field in its own right, and given a full
   row it reads as one. Shared by the folded blocks here and the built-in page editor. */
.qm-pc-fields { display: flex; flex-wrap: wrap; gap: 0 12px; }
.qm-pc-fields > .qm-pc-row { flex: 1 1 100%; min-width: 0; }
.qm-pc-fields > .qm-pc-row-icon { flex: 0 0 76px; }
/* A declared pair (eyebrow + heading, button text + button link) shares one row: half each,
   minus half the 12px gap, so the two exactly fill the line and the next field wraps. */
.qm-pc-fields > .qm-pc-row-half { flex: 1 1 calc(50% - 6px); }
/* The field beside a picker takes ALL the rest of the line — 76px picker + 12px gap + the
   remainder — so the pair fills the row and the next pair wraps under it. `flex: 1 1 0` let five
   pairs share one line: the inputs collapsed to a few characters and the labels overlapped. */
.qm-pc-fields > .qm-pc-row-icon + .qm-pc-row { flex: 1 1 calc(100% - 88px); }
/* A media field carries `height: 100%` so several of them line up in a Bootstrap grid row. In here
   its row is a flex item whose height that same field decides, so the two chase each other: the
   field took the row's height, ignored its own label, and hung 30px out of the panel. Content-sized
   here, and the row grows to hold the label AND the picker. */
.qm-pc-row > .qm-media-field { height: auto; }
.qm-pc-fold-body { padding: 14px 14px 14px; border-top: 1px solid var(--line); }
/* A section fold reads as the page's own heading, so it carries the card's weight; the panel folds
   nested inside it stay quieter. */
.qm-pc-sec { background: var(--surface); box-shadow: var(--e-1); margin-top: 16px; }
.qm-pc-sec:first-child { margin-top: 0; }
.qm-pc-sec > summary { padding: 14px 18px; font-size: 1.05rem; }
.qm-pc-sec-body { padding: 0 18px 18px; }
.qm-pc-sec-body > .qm-card { box-shadow: none; border: 1px solid var(--line); }
/* The section's own card: name on the left, its visibility switch inline on the right, so the
   control sits with what it governs instead of in a column of checkboxes down the page. */
.qm-pc-sechead > .qm-card-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.qm-pc-secswitch { flex: 0 0 auto; border: 0; padding: 0; background: none; gap: 10px; }
.qm-pc-secswitch > span { font-size: .9rem; font-weight: 600; color: var(--ink-600); }
/* The rail is a list of names now, not a control panel. */
.qm-pc-jump { padding: 7px 0; border-bottom: 1px solid var(--line); }
.qm-pc-jump:last-child, .qm-pc-jump.mb-0 { border-bottom: 0; }
.qm-pc-jump a { color: var(--ink-700); text-decoration: none; font-weight: 500; font-size: .95rem; }
.qm-pc-jump a:hover, .qm-pc-jump a.fw-bold { color: var(--qm-primary); }
/* Circular variant (avatars, restaurant logo) — mirrors the FilePond circle. */
.qm-media-field.is-circle .qm-media-card-body { align-items: center; }
.qm-media-field.is-circle .qm-media-preview { width: 150px; height: 150px; max-width: 150px; aspect-ratio: 1; border-radius: 50%; }
.qm-media-field.is-circle .qm-media-preview [data-media-thumb] { object-fit: cover; padding: 0; }

/* The modal (one per page). Layered below the crop modal (z 3000) so a crop on
   the Upload tab opens on top of it. */
.qm-media-modal,
.qm-pm-modal { position: fixed; inset: 0; z-index: 2600; display: flex; align-items: center; justify-content: center; padding: 20px; }
.qm-media-modal[hidden],
.qm-pm-modal[hidden] { display: none; }
.qm-media-modal-backdrop,
.qm-pm-modal-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, .55); }
/* A wide, tall panel (WordPress "Select media" scale) so the library reads across the
   page: the head/tabs stay fixed and the active pane scrolls its own grid. */
.qm-media-modal-box { position: relative; background: var(--surface, #fff); border-radius: var(--r-lg, 16px); width: min(1180px, 95vw); height: min(88vh, 920px); max-height: 92vh; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 24px 60px rgba(0, 0, 0, .28); }
.qm-media-modal-head,
.qm-pm-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--qm-border, #e5e7eb); }
.qm-media-modal-title,
.qm-pm-modal-title { font-size: 1.05rem; margin: 0; display: inline-flex; align-items: center; gap: 8px; }
.qm-media-modal-close,
.qm-pm-modal-close { border: 0; background: transparent; font-size: 1.1rem; line-height: 1; color: var(--qm-muted, #8a8f98); cursor: pointer; padding: 6px; border-radius: 8px; }
.qm-media-modal-close:hover,
.qm-pm-modal-close:hover { background: var(--qm-fill, #f1f2f4); color: inherit; }
/* Adding a payment method is a short errand, so its dialog is a small panel rather than the
   library's wide one — but the head, the close control and the backdrop are the media picker's
   own rules above, shared rather than restated, so the two dialogs cannot drift apart. */
.qm-pm-modal { z-index: 2600; padding: 20px; }
.qm-pm-modal-box { position: relative; background: var(--surface, #fff); border-radius: var(--r-lg, 16px);
  width: min(480px, 95vw); max-height: 92vh; display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .28); }
.qm-pm-modal-body { padding: 16px 20px 20px; overflow: auto; flex: 1 1 auto; min-height: 0; }
/* A gateway field wearing our input's clothes: same height, border and radius as every other
   .form-control on the page, so a card asks for itself the way the rest of the product does. */
/* A gateway element still has to be in the document to be confirmed against, even when this page
   shows nothing of its own. display:none would stop it mounting, so it is placed out of view with a
   real width instead. */
.qm-el-offscreen { position: fixed; inset-block-start: 0; inset-inline-start: -10000px; width: 420px; height: 1px; overflow: hidden; }
/* The row that was pressed says it is working, since no dialog opens to say it. */
.qm-addpay-choice.is-busy { opacity: .6; pointer-events: none; }
.qm-el-field { display: flex; flex-direction: column; justify-content: center; min-height: 44px; }
.qm-el-field .StripeElement { width: 100%; }
.qm-pm-modal-foot { margin-top: 16px; }

.qm-media-tabs { display: flex; gap: 4px; padding: 10px 20px 0; border-bottom: 1px solid var(--qm-border, #e5e7eb); }
.qm-media-tab { border: 0; background: transparent; padding: 8px 14px; border-radius: 8px 8px 0 0; cursor: pointer; color: var(--qm-muted, #8a8f98); font-weight: 600; display: inline-flex; align-items: center; gap: 6px; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.qm-media-tab.is-active { color: var(--qm-primary, #ff003d); border-bottom-color: var(--qm-primary, #ff003d); }
.qm-media-pane { display: none; padding: 16px 20px 20px; overflow: auto; flex: 1 1 auto; min-height: 0; overscroll-behavior: contain; }
.qm-media-pane.is-active { display: block; }
.qm-media-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
.qm-media-search-wrap { position: relative; flex: 1 1 200px; }
.qm-media-search-wrap i { position: absolute; top: 50%; inset-inline-start: 12px; transform: translateY(-50%); color: var(--qm-muted, #8a8f98); pointer-events: none; }
.qm-media-search { padding-inline-start: 34px; }
.qm-media-filters { display: inline-flex; gap: 4px; background: var(--qm-fill, #f1f2f4); border-radius: 999px; padding: 3px; }
.qm-media-chip { border: 0; background: transparent; padding: 6px 14px; border-radius: 999px; cursor: pointer; font-size: .85rem; font-weight: 600; color: var(--qm-muted, #8a8f98); }
.qm-media-chip.is-active { background: var(--surface, #fff); color: var(--qm-primary, #ff003d); box-shadow: 0 1px 3px rgba(0, 0, 0, .12); }
.qm-media-restaurant { max-width: 220px; }
.qm-media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; min-height: 120px; }
.qm-media-tile { position: relative; aspect-ratio: 1; border: 2px solid transparent; border-radius: var(--r-md, 12px); overflow: hidden; cursor: pointer; background: var(--qm-fill, #f1f2f4); }
.qm-media-tile img, .qm-media-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
.qm-media-tile:hover, .qm-media-tile:focus-visible { border-color: var(--qm-primary, #ff003d); outline: none; }
.qm-media-tile.is-selected { border-color: var(--qm-primary, #ff003d); }
.qm-media-tile-badge { position: absolute; inset-block-start: 6px; inset-inline-start: 6px; background: var(--qm-primary, #ff003d); color: #fff; font-size: .68rem; font-weight: 700; padding: 2px 7px; border-radius: 999px; }
.qm-media-tile-del { position: absolute; inset-block-start: 6px; inset-inline-end: 6px; width: 26px; height: 26px; border: 0; border-radius: 8px; background: rgba(0, 0, 0, .55); color: #fff; cursor: pointer; opacity: 0; transition: opacity .15s; display: inline-flex; align-items: center; justify-content: center; }
.qm-media-tile:hover .qm-media-tile-del, .qm-media-tile:focus-within .qm-media-tile-del { opacity: 1; }
.qm-media-tile-name { position: absolute; inset-inline: 0; inset-block-end: 0; padding: 3px 6px; font-size: .68rem; color: #fff; background: linear-gradient(transparent, rgba(0, 0, 0, .6)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Multi-select: a ticked tile + the footer bar that commits them all at once. */
.qm-media-tile.is-picked { border-color: var(--qm-primary, #ff003d); }
.qm-media-tile.is-picked::after { content: "\f00c"; font-family: "Font Awesome 6 Free"; font-weight: 900; position: absolute; inset-block-start: 6px; inset-inline-end: 6px; width: 22px; height: 22px; display: grid; place-items: center; border-radius: 50%; background: var(--qm-primary, #ff003d); color: #fff; font-size: 11px; }
.qm-media-addbar { display: flex; justify-content: flex-end; gap: 10px; margin-block-start: 16px; padding-block-start: 14px; border-block-start: 1px solid var(--line); }
/* A multi FIELD shows its chosen images as a grid of thumbs instead of one preview. */
.qm-media-field.is-multi .qm-media-chosen { display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 10px; }
.qm-media-chosen-item { position: relative; aspect-ratio: 1; border-radius: var(--r-md, 12px); overflow: hidden; background: var(--qm-fill, #f1f2f4); }
.qm-media-chosen-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qm-media-field.is-multi .qm-media-chosen:empty { display: none; }
.qm-media-more { display: flex; justify-content: center; margin-block-start: 18px; }
.qm-media-more[hidden] { display: none; }
.qm-media-state { text-align: center; color: var(--qm-muted, #8a8f98); padding: 32px 12px; }
.qm-media-drop { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; min-height: 220px; border: 2px dashed var(--qm-border, #d5d7dd); border-radius: var(--r-md, 12px); background: var(--qm-fill, #f7f8fa); color: var(--qm-muted, #8a8f98); cursor: pointer; text-align: center; padding: 24px; }
.qm-media-drop.is-dragover { border-color: var(--qm-primary, #ff003d); background: var(--qm-primary-50, #fff0f3); color: var(--qm-primary, #ff003d); }
.qm-media-drop-icon { font-size: 2.2rem; }
.qm-media-drop-browse { color: var(--qm-primary, #ff003d); font-weight: 700; text-decoration: underline; }
.qm-media-progress { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 16px; color: var(--qm-muted, #8a8f98); }
.qm-media-spinner { width: 18px; height: 18px; border: 2px solid var(--qm-border, #e5e7eb); border-top-color: var(--qm-primary, #ff003d); border-radius: 50%; animation: qm-media-spin .7s linear infinite; }
@keyframes qm-media-spin { to { transform: rotate(360deg); } }
/* Lock BOTH the root and body while the picker is open. The dashboard's scroller is the
   <html> element (overflow-y:auto), so locking only <body> left the page scrolling behind
   the modal — which also dragged the sticky sidebar out from under the backdrop. */
html.qm-media-open { overflow: hidden; }
@media (max-width: 560px) { .qm-media-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); } }

/* Admin all-tenants media browser: a filter bar above the shared grid, with a
   per-tile owner label and an always-visible delete. */
.qm-media-adminbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 18px; }
.qm-media-adminbar .qm-media-search-wrap { flex: 1 1 220px; }
.qm-media-admingrid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.qm-media-admingrid .qm-media-tile { cursor: default; }
/* The image itself opens the preview, so it — and only it — reads as clickable. The corner
   crop/delete buttons keep their own affordance. */
.qm-media-grid .qm-media-tile > img[data-lightbox-src],
.qm-media-grid .qm-media-tile > video[data-lightbox-src] { cursor: zoom-in; }
/* Lightbox caption — only rendered when a trigger supplies a title or a URL. */
.qm-lightbox.has-meta { flex-direction: column; gap: var(--sp-4, 16px); }
.qm-lightbox.has-meta .qm-lightbox-img, .qm-lightbox.has-meta .qm-lightbox-video { max-height: 70vh; }
.qm-lightbox-meta { width: min(92vw, 1000px); background: #fff; border-radius: var(--r-md, 12px); padding: 14px 16px; box-shadow: var(--e-2); }
.qm-lightbox-title { margin: 0 0 10px; font-weight: 700; font-size: .95rem; color: var(--ink-900, #1f0b12); word-break: break-all; }
.qm-lightbox-urlrow { display: flex; gap: 8px; align-items: center; }
.qm-lightbox-url { flex: 1 1 auto; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .8rem; padding: 8px 10px; border: 1px solid var(--line, #e3e6ea); border-radius: var(--r-sm, 8px); background: var(--surface-2, #f7f8fa); color: var(--ink-700, #4a5058); }
.qm-lightbox-copy { flex: 0 0 auto; border: 0; border-radius: var(--r-sm, 8px); padding: 8px 14px; font-size: .85rem; font-weight: 600; cursor: pointer; background: var(--qm-primary, #ff003d); color: #fff; }
.qm-lightbox-copy:hover { filter: brightness(1.08); }
.qm-media-admingrid .qm-media-tile-del { opacity: 1; background: rgba(0, 0, 0, .5); }
.qm-media-tile-owner { position: absolute; inset-inline: 0; inset-block-end: 0; padding: 4px 7px; font-size: .68rem; font-weight: 600; color: #fff; background: linear-gradient(transparent, rgba(0, 0, 0, .68)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ============================================================================
   34. THREE-STEP CHECKOUT

   Chrome for the grouped checkout (app/views/public/checkout.php): a numbered step
   rail above the form and a Back/Next row below it. Both ship hidden in the markup
   and are revealed by assets/js/checkout.js, so none of this renders for a browser
   that does not run it — the page stays the single stacked form it has always been.

   The numbered badge is the order builder's own .qm-ob-step rather than a second
   numbered-step look; only its colour changes per state, which is the entire reason
   it was reused. Everything else here is layout.
   ========================================================================== */
.qm-cowiz-pane[hidden] { display: none; }

.qm-cowiz-rail { margin-bottom: var(--sp-4); }
.qm-cowiz-rail ol { display: flex; align-items: flex-start; margin: 0; padding: 0; list-style: none; }
.qm-cowiz-railitem { flex: 1 1 0; min-width: 0; position: relative; }
/* The connector spans from the previous badge's centre to this one's, so it keeps its
   line when a step name wraps and the two badges no longer sit on one text baseline. */
.qm-cowiz-railitem + .qm-cowiz-railitem::before {
  content: ""; position: absolute; top: 15px; left: -50%; width: 100%;
  border-top: 2px solid var(--line);
}
.qm-cowiz-railitem.is-active::before,
.qm-cowiz-railitem.is-done::before { border-top-color: var(--qm-primary); }

.qm-cowiz-railbtn {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 100%; padding: 0; background: none; border: 0; cursor: pointer;
  font: inherit; color: var(--ink-500); text-align: center;
}
/* Above the connector, and filled only once the step is reached — an upcoming step
   reads as an outline, so the rail shows progress rather than three identical badges. */
.qm-cowiz-railbtn .qm-ob-step {
  position: relative; z-index: 1;
  background: var(--surface); border: 2px solid var(--line); color: var(--ink-500);
}
.qm-cowiz-railitem.is-done .qm-cowiz-railbtn .qm-ob-step { background: var(--ok); border-color: var(--ok); color: #fff; }
.qm-cowiz-railitem.is-active .qm-cowiz-railbtn .qm-ob-step { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-cowiz-railtext { font-size: .8rem; font-weight: 600; line-height: 1.25; }
.qm-cowiz-railitem.is-done .qm-cowiz-railtext { color: var(--ink-700); }
.qm-cowiz-railitem.is-active .qm-cowiz-railtext { color: var(--ink-900); }
.qm-cowiz-railbtn:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: 3px; border-radius: var(--r-sm); }
@media (max-width: 575.98px) { .qm-cowiz-railtext { font-size: .72rem; } }

/* Forward action right, the way every action row on this build reads. Back is hidden on
   the first step, so Next is pushed across rather than left sitting where Back was. */
.qm-cowiz-nav { display: flex; justify-content: space-between; gap: var(--sp-3); margin-top: var(--sp-4); }
.qm-cowiz-nav [data-cowiz-next] { margin-left: auto; }

/* ============================================================================
   Touch-device tap targets (WCAG 2.5.5, 44px). Kept at the very end of the file so
   these win on source order over the component base rules defined earlier — the
   .qm-icon-btn touch rule sits with its component, but these controls are defined
   further down, so their coarse-pointer sizing must come last. The deliberately
   compact .qm-btn-sm / .qm-btn-xs stay small by design (dense secondary actions).
   ========================================================================== */
@media (pointer: coarse) {
  .qm-dash-navtoggle { width: 44px; height: 44px; }
  .qm-pw-toggle { width: 44px; height: 44px; }
  .qm-media-tile-del { width: 44px; height: 44px; }
  .qm-dash-top .qm-icon-pill,
  .qm-dash-top .qm-account-btn { min-width: 44px; min-height: 44px; }
  /* Bare icon "hide/dismiss" controls (e.g. the dashboard card dismiss) carry only the
     icon — give them a full 44px tap area without changing the icon itself. */
  [data-qm-dismiss-btn] { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; }
}

/* ---- Typeahead panel (qm-suggest.js) ----
   A results panel, not a tooltip: it wears the same light card surface as every
   other list the product shows — card ground, hairline rules, ink-900 label over an
   ink-500 detail. Built from existing tokens only. Logical properties throughout, so
   the RTL build mirrors it without a hand-written override. */
.qm-sug-wrap { position: relative; display: block; }
.qm-sug {
  position: absolute; inset-block-start: calc(100% + 4px); inset-inline: 0; z-index: 40;
  max-height: 320px; overflow-y: auto;
  background: var(--card-bg); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: 0 10px 26px rgba(31, 11, 18, .16);
}
.qm-sug-item {
  display: flex; align-items: baseline; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); cursor: pointer;
  border-block-end: 1px solid var(--line-2);
  font-size: var(--fs-400); line-height: 1.45;
}
.qm-sug-item:last-child { border-block-end: 0; }
.qm-sug-item:hover,
.qm-sug-item.is-active { background: var(--line-2); }
.qm-sug-label { font-weight: 600; color: var(--ink-900); min-width: 0; }
.qm-sug-sub { font-size: .85em; color: var(--ink-500); margin-inline-start: auto; white-space: nowrap; }

/* ---- Map cluster bubble (QMMap.cluster) ----
   One bubble per occupied grid cell when a map carries more points than it can show
   as pins. Wears the brand pin's own colour so a cluster and a marker read as the
   same family; sized inline by the engine because the count decides it. */
.qm-map-cluster-wrap { background: none; border: 0; }
.qm-map-cluster {
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%; cursor: pointer;
    background: var(--qm-primary); color: #fff;
    font-weight: 700; font-size: .82rem; line-height: 1;
    border: 3px solid #fff; box-shadow: 0 4px 12px rgba(31, 11, 18, .32);
}
/* Drag handle on a section fold's summary. Only the handle drags — the rest of the row opens the
   fold — so the two gestures never compete.

   Sized to the field height, the same recipe .qm-order-filters uses below: a control standing among
   fields takes the field's height rather than the 32px .qm-icon-btn default. The number is this
   screen's, not that one's — .form-select is 44px here, where the filter row's .qm-filter-reset is
   48px — so it is read off the fields it sits with instead of copied across. */
.qm-icon-btn.qm-pc-drag {
  flex: 0 0 auto; cursor: grab; color: var(--ink-400);
  font-size: 14px;
  width: 44px; min-width: 44px;
  height: 44px; min-height: 44px;
}
.qm-icon-btn.qm-pc-drag:active { cursor: grabbing; }
.qm-pc-sec.is-dragging { opacity: .45; }
/* A grip or a remove button standing in a filter row is a peer of the fields beside it, so it takes
   the field's own height instead of the 32px default — same recipe as .form-control (1.5em line box
   48px is what .qm-filter-reset already uses in this same row, so the controls in it are one height. */
.qm-order-filters .qm-icon-btn {
  font-size: 14px;
  width: 48px; min-width: 48px;
  height: 48px; min-height: 48px;
}

/* ------------------------------------------------------------------ 35. UPSELL BAND
   "Goes well with your order". Two placements, one partial.

   In the restaurant page's rail the band is a SECTION OF the sticky "Your Order" panel, not a
   card beside it: as a sibling of .qm-cart-side (position:sticky, z-index 20) it was scrolled
   over — the panel pinned and the band slid up underneath it. Inside, the two move together.
   It borrows the panel's own separator rhythm rather than introducing one. */
.qm-cart-side-upsell { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line-2); }
.qm-cart-side-upsell .qm-cart-side-title { font-size: var(--fs-600); margin-bottom: 0; }

/* A LONE recommendation on a wide surface lays out sideways. Left as the vertical tile it is at
   three-across, one card stretched to full width turns its square photo into a banner and pushes
   the price off the fold. Only below the grid's own breakpoint, where full width is narrow enough
   that the upright card is right again. */
@media (min-width: 576px) {
  .qm-pop-card-wide { flex-direction: row; align-items: stretch; }
  .qm-pop-card-wide .qm-pop-media { aspect-ratio: auto; width: 34%; max-width: 220px; flex: 0 0 auto; }
  .qm-pop-card-wide > :not(.qm-pop-media) { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; }
}

/* ------------------------------------------------------------------ 35. UPSELL BAND (rail)
   "Goes well with your order" as it appears on a restaurant page, above the sticky order panel.

   The shared tile is built for a three-across grid, so at the rail's full width its square photo
   became a banner and pushed the price below the fold. Here the card lies on its side with a small
   fixed thumbnail, which is what keeps the whole block short enough to sit above "Your Order"
   without pushing it off the screen. The cart page keeps the upright grid untouched. */
.qm-upsell-rail .qm-pop-card { flex-direction: row; align-items: center; height: auto; gap: 12px; padding: 10px; border-radius: var(--r-md); }
.qm-upsell-rail .qm-pop-card:hover { transform: none; }
/* A fixed square thumbnail, inset from the card edge. The shared tile bleeds its photo to the
   border because it sits above the text; lying on its side it needs the same breathing room the
   words beside it have, or the image reads as a stray sticker on the card. */
.qm-upsell-rail .qm-pop-media { aspect-ratio: 1/1; width: 92px; flex: 0 0 92px; border-radius: var(--r-md); overflow: hidden; }
.qm-upsell-rail .qm-pop-media img { width: 100%; height: 100%; object-fit: cover; }
.qm-upsell-rail .qm-pop-body { flex: 1 1 auto; min-width: 0; padding: 0; }

/* ------------------------------------------------------------------ 36. BROWSE RESTAURANTS · MAP VIEW
   The map that replaces the results grid on /restaurants?view=map.

   It is the shared .qm-map box — same border, radius, and zero-DNS local grid as the
   tracking map and the zone editor — with two differences. It IS the content here rather
   than a panel beside it, so it is taller; and it is the only map in the product that
   draws the distance the customer searched. */
.qm-map-results { height: clamp(420px, 68vh, 720px); }
/* The searched circle. Leaflet renders it as an SVG <path> and author CSS beats its
   presentation attributes, so the stroke and fill are decided here — the same arrangement
   .qm-map-route-line already uses, rather than a second colour hardcoded in JavaScript. */
.qm-map-radius { stroke: var(--qm-primary); stroke-width: 2; stroke-dasharray: 6 8; fill: var(--qm-primary); fill-opacity: .07; }
/* The distance line under a restaurant's name inside a map popup. */
.qm-map-popup-sub { display: block; margin-top: 2px; color: var(--ink-600); font-size: var(--fs-400); }

/* ---- Till deck strip (vendor → quick keys) ------------------------------------------------
   The pinned dishes, drawn the way the till draws them: one wrapping row of chips in cashier
   order, directly above the menu they are chosen from. This replaced a full dashboard table
   whose thumbnail, price and actions columns cost a screen's height to show at most ten ids.
   Everything here is symmetric (no left/right property), so the RTL build is a no-op on it. */
.qm-qk-deck { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; min-height: 56px;
  padding: 10px; margin-bottom: 16px; border: 1px dashed var(--line); border-radius: var(--r-md);
  background: var(--surface-2); }
.qm-qk-chip { display: inline-flex; align-items: center; gap: 8px; padding: 6px 8px;
  border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--surface); }
/* Dragging is the shared arranger's job; it sets draggable on this chip. */
.qm-qk-chip[draggable="true"] { cursor: grab; }
.qm-qk-chip img { width: 32px; height: 32px; border-radius: 6px; object-fit: cover; flex: 0 0 auto; }
.qm-qk-name { font-weight: 600; font-size: var(--fs-400); }
/* Bare buttons, not .qm-icon-btn: a chip is small and two pill buttons inside it would read as
   two more chips. Compounded selectors so Bootstrap's [type=button] rule does not win the cursor. */
.qm-qk-chip .qm-qk-grip, .qm-qk-chip .qm-qk-x { border: 0; background: none; padding: 2px;
  line-height: 1; color: var(--ink-400); font-size: var(--fs-400); }
.qm-qk-chip .qm-qk-grip { cursor: grab; }
.qm-qk-chip .qm-qk-x { cursor: pointer; }
.qm-qk-chip .qm-qk-x:hover, .qm-qk-chip .qm-qk-x:focus-visible { color: var(--danger, #d92d20); }
.qm-qk-empty { margin: 0; padding: 0 4px; color: var(--ink-400); font-size: var(--fs-400); }

/* ---- Shared output controls (partials/print-actions.php) -----------------------------------
   One print path for every designed artefact. The clone is off-screen until the run starts, so
   the page never flashes it; during a run everything except the clone is hidden, so dashboard
   chrome cannot follow the artefact onto the paper. */
.qm-pa-area { position: absolute; left: -10000px; top: 0; }
@media print {
  body.qm-pa-printing > *:not(.qm-pa-area) { display: none !important; }
  body.qm-pa-printing .qm-pa-area { position: static; display: block; }
  /* Multiple per sheet: a grid the artefact's own size flows into, rather than a second layout. */
  body.qm-pa-printing .qm-pa-area.qm-pa-nup > * { display: grid;
    grid-template-columns: repeat(3, 1fr); gap: 4mm; }
}

/* ---- Export dropdown, shared -----------------------------------------------------------------
   These rules lived INLINE in print-menu-form.php. The moment the shared output component reused
   .qm-se-export on the QR and receipt pages, the dropdown there had no styling at all — an
   unstyled <details> with its menu in the page flow. Page-local CSS for a component used on more
   than one page is the same trap the TV editor's chrome sets. The menu form's copy is removed, so
   there is one definition. */
.qm-se-export { position: relative; }
.qm-se-export > summary { list-style: none; cursor: pointer; }
.qm-se-export > summary::-webkit-details-marker { display: none; }
.qm-se-export-menu { position: absolute; z-index: 20; inset-block-start: calc(100% + 6px);
  inset-inline-end: 0; min-width: 190px; background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb); border-radius: 12px;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .16); padding: 6px; display: flex;
  flex-direction: column; gap: 2px; }
.qm-se-export-menu a { display: flex; align-items: center; gap: 9px; padding: 9px 12px;
  border-radius: 8px; color: var(--ink-900, #1f2430); text-decoration: none; font-size: .9rem;
  cursor: pointer; }
.qm-se-export-menu a:hover { background: var(--line-2, #f1f2f4); }
.qm-se-export-menu a i { width: 16px; text-align: center; color: var(--ink-500, #6b7280); }
/* Print sits with the formats it belongs beside, so the separator marks "device" from "file". */
.qm-se-export-menu hr { border: 0; border-top: 1px solid var(--line, #e5e7eb); margin: 4px 6px; }

/* The menu flips above its trigger when print-actions.js finds no room below — a bar at the end of
   a form would otherwise drop its menu past the end of the document and lengthen the page. */
.qm-se-export.is-up .qm-se-export-menu { inset-block-start: auto; inset-block-end: calc(100% + 6px); }





/* ---- Email preview: the email fills its panel ----------------------------------------------
   The document renders at its true width (600 desktop / 375 mobile) and is scaled to the column,
   so what is on screen is the layout an inbox produces, shown at the size of the space rather
   than stranded in the middle of it. The stage carries the scaled height, because a transform
   does not affect layout and the card would otherwise collapse around the unscaled box. */
.qm-email-stage { position: relative; overflow: hidden; }
.qm-email-preview { display: block; width: 100%; min-height: 320px; border: 1px solid var(--field-border); border-radius: 10px; background: #fff; }

/* ---- Email editor: the button, inside the visual editor -------------------------------------
   Quill keeps the class its blot declares but not the inline style, so the editor shows the button
   as a button while emailize() restores the email-safe inline style on the way out. The toolbar
   control reuses the same icon the HTML tab's button snippet uses. */
.ql-editor a.qm-em-btn { display: inline-block; background: var(--qm-primary); color: #fff;
  text-decoration: none; font-weight: 700; padding: 10px 20px; border-radius: 8px; }
.ql-snow .ql-toolbar button.ql-embutton::after,
.ql-snow.ql-toolbar button.ql-embutton::after { content: "\f0a6"; font-family: "Font Awesome 5 Free";
  font-weight: 900; font-size: var(--fs-400); }

/* Delivery-zone row: the four fields and Remove on ONE line.
   .qm-form-grid is 2-up, so four fields wrapped onto two lines and Remove sat on a third.

   NO viewport media query for the 5-across layout. This grid lives inside the Charges panel
   and is ~780px wide while the window is ~1150px, so a max-width rule fired on the WINDOW and
   collapsed a row that had plenty of room. The columns shrink themselves via minmax(0,1fr);
   only the genuine phone stack below is worth a breakpoint. */
.qm-zone-grid { grid-template-columns: repeat(4, minmax(0, 1fr)) auto; align-items: end; gap: 12px; }
.qm-zone-grid .qm-zone-remove { display: flex; flex-direction: column; }
.qm-zone-grid .qm-zone-remove .form-label { display: block; }
/* Match the input box, not the small-button default: 31px against 43px inputs read as a
   misalignment however the row is aligned. */
.qm-zone-grid .qm-zone-remove .qm-btn { min-height: 43px; display: inline-flex; align-items: center;
                                        justify-content: center; white-space: nowrap; padding: 0 16px; }
@media (max-width: 760px) { .qm-zone-grid { grid-template-columns: minmax(0, 1fr); }
                            .qm-zone-grid .qm-zone-remove .form-label { display: none; } }

/* Account identity: photo beside the details.
   A circular avatar in a full-width block is mostly empty space and pushed first name,
   email and phone below the fold. Auto track for the picker, the rest for the fields.
   The second cell must NOT be a Bootstrap .row — its negative margins hang past the cell. */
/* align-items: stretch, not start — the picker card was shorter than the field block beside
   it, so the two columns ended at different heights and the card looked truncated. It now
   fills the row and the preview grows with it. */
.qm-acct-identity { display: grid; grid-template-columns: minmax(0, 280px) minmax(0, 1fr); gap: 20px; align-items: stretch; }
.qm-acct-identity > div:first-child { display: flex; flex-direction: column; }
.qm-acct-identity .qm-media-field { flex: 1 1 auto; display: flex; flex-direction: column; }
.qm-acct-identity .qm-media-card-body { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; }
/* Remove + Change sit side by side. The picker foot already flexes, but at the old 220px
   the two labels did not fit and wrapped onto separate lines, so the column is wider and
   wrapping is off here — the buttons stay on one row and keep the foot to a single line. */
.qm-acct-identity .qm-media-card-foot { flex-wrap: nowrap; justify-content: flex-end; }
.qm-acct-identity .qm-media-card-foot .qm-btn { white-space: nowrap; }
@media (max-width: 992px) { .qm-acct-identity { grid-template-columns: minmax(0, 1fr); } }

/* Branding grid: label above a media card.
   .qm-media-field is height:100% so two cards in a row match, but that 100% measured against
   the whole CELL while a label sat above it — so the card overflowed its cell by the label's
   height and its Remove/Change foot rendered on top of the field below. Making the cell a flex
   column lets the card take the space that is actually left. */
.qm-form-grid > div:has(> .qm-media-field) { display: flex; flex-direction: column; min-width: 0; }
.qm-form-grid > div:has(> .qm-media-field) > .qm-media-field { height: auto; flex: 1 1 auto; }
/* Brand colour: a clear break from the cards above, and the swatch matched to the monogram it
   previews — 27px against 44px read as two unrelated objects. */
.qm-brand-color { margin-top: 4px; }
.qm-brand-color .qm-color-swatch { height: 44px; }

/* ============================================================================
   INVOICE DOCUMENT SPACING
   An invoice is read as a document, not scanned as a dashboard panel: the payer
   looks up one figure, one date, one reference. It was laid out on the ordinary
   card rhythm, so the two address blocks, the line table and the payment meta
   ran together as one dense column with 16px between bands and address lines
   set on the default body leading.

   Scoped to .qm-print-area, which only the invoice pages use, so no other card
   in the product moves. Nothing here changes what is printed — only how much
   room it is given.
   ============================================================================ */
.qm-print-area .qm-ops-head { padding-bottom: var(--sp-4); border-bottom: 1px solid var(--line); margin-bottom: var(--sp-5); }
.qm-print-area .qm-ops-head h2 { font-size: 1.5rem; letter-spacing: -.01em; }

/* The two parties sit further apart than form fields would, and their lines are
   addresses — they need postal leading, not paragraph leading. */
.qm-print-area .qm-form-grid { gap: var(--sp-6); margin-bottom: var(--sp-5); }
.qm-print-area .qm-form-grid p { line-height: 1.75; color: var(--ink-700); }
.qm-print-area .qm-form-grid strong { display: inline-block; margin-bottom: 2px; color: var(--ink-900); }

/* Each band announces itself before its content. */
.qm-print-area .qm-report-sub { margin-bottom: var(--sp-3); }
/* Invoice section heads: a hairline rule under the label and no icon — it is a document,
   not a dashboard card. The shift reports keep the icon variant of .qm-report-sub. */
.qm-print-area .qm-inv-sub { display: block; padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line); }
.qm-print-area .qm-report-sub.mt-3 { margin-top: var(--sp-5) !important; }

/* Money wants air around it: taller rows, and the totals set apart from the line
   they are summing. */
.qm-print-area .qm-table td,
.qm-print-area .qm-table th { padding-top: var(--sp-4); padding-bottom: var(--sp-4); }
.qm-print-area .qm-table tfoot td { border-top: 1px solid var(--line); }
.qm-print-area .qm-table tfoot tr:last-child td { border-top-width: 2px; font-size: 1.05rem; }

/* The payment facts are a footer to the document, not another row of it. */
.qm-print-area .qm-order-totals { margin-top: var(--sp-5) !important; }
.qm-print-area .qm-hint.mt-3 { margin-top: var(--sp-5) !important; }


/* ============================================================================
   35. DASHBOARD DENSITY STANDARD — .qm-d2
   The panel-wide design contract: one spacing scale, one radius, one card
   shadow, one accent (the brand primary), hierarchy from weight and ink.
   Scoped to the .qm-d2 wrapper a page view opts into, so bespoke surfaces
   (POS, KDS, print documents, editors) stay untouched. Same partials, same
   language keys everywhere — this section only restyles them.
   ========================================================================== */

.qm-d2 {
    --d2-gap: 14px;
    --d2-pad: 16px;
    --d2-radius: 14px;
    --d2-border: color-mix(in srgb, var(--ink-900, #1c1917) 8%, transparent);
    --d2-shadow: 0 1px 2px rgba(28, 25, 23, .04), 0 8px 24px -18px rgba(28, 25, 23, .18);
    --d2-shadow-up: 0 2px 4px rgba(28, 25, 23, .05), 0 14px 32px -18px rgba(28, 25, 23, .24);
    --d2-accent: var(--qm-primary, #ff003d);
    --d2-accent-soft: color-mix(in srgb, var(--d2-accent) 7%, #fff);
    --d2-t: 160ms cubic-bezier(.4, 0, .2, 1);
}

/* ---------------------------------------------------------------- cards --- */
.qm-d2 .qm-card,
.qm-d2 .qm-kpi-panel,
.qm-d2 .qm-chart-card > .card,
.qm-d2 .qm-chart-card .qm-card {
    border-radius: var(--d2-radius);
    border: 1px solid var(--d2-border);
    box-shadow: var(--d2-shadow);
    transition: box-shadow var(--d2-t), transform var(--d2-t), border-color var(--d2-t);
}
.qm-d2 .qm-card { padding: var(--d2-pad); margin-bottom: var(--d2-gap); }
.qm-d2 .qm-card:hover,
.qm-d2 .qm-kpi-panel:hover { box-shadow: var(--d2-shadow-up); }

/* Section titles: quieter, tighter — a label, not a headline. (2/8 was 4/10:
   the three labels above the vendor dashboard's fold each gave 4px back so the
   Breakdown lands inside one screen.) */
.qm-d2 .qm-snap-section-title {
    font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--ink-500, #78716c);
    margin: 2px 0 8px;
    display: flex; align-items: center; gap: 8px;
}
.qm-d2 .qm-snap-title-bar { width: 3px; height: 14px; border-radius: 2px; background: var(--d2-accent); }

/* ------------------------------------------------- "Right now" pulse bar --- */
/* One slim strip: heading as the left cap, the six queue tiles filling the
   row. The six pastel tints are retired — one accent system, neutral tiles. */
.qm-d2 .qm-rightnow { display: flex; align-items: center; gap: 6px; padding: 6px 14px; }
.qm-d2 .qm-rightnow .qm-ops-head { margin: 0; padding-inline-end: 12px; border-inline-end: 1px solid var(--d2-border); flex: 0 0 auto; }
.qm-d2 .qm-rightnow .qm-ops-head h2 { font-size: 13px; font-weight: 800; }
.qm-d2 .qm-rightnow .qm-order-totals {
    display: flex; flex-wrap: wrap; gap: 2px; margin: 0 !important; flex: 1 1 auto; min-width: 0;
}
/* Scoped to the Right-Now strip ONLY: .qm-ot-item is also the totals chip under
   the Orders/Reservations filters, and an unscoped override here stripped those
   chips to bare text spread across the row. Off the dashboard they keep their
   base bordered-chip look. */
.qm-d2 .qm-rightnow .qm-ot-item {
    display: flex; align-items: center; gap: 10px;
    flex: 1 1 0; min-width: 132px;
    background: transparent; border: 0; border-radius: 10px;
    padding: 7px 10px;
    transition: background var(--d2-t);
}
.qm-d2 .qm-rightnow .qm-ot-item:hover { background: var(--d2-accent-soft); }
.qm-d2 .qm-rightnow .qm-ot-ico {
    width: 30px; height: 30px; border-radius: 9px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 12px;
    background: var(--d2-accent-soft); color: var(--d2-accent);
    transition: transform var(--d2-t);
}
.qm-d2 .qm-rightnow .qm-ot-item:hover .qm-ot-ico { transform: scale(1.06); }
.qm-d2 .qm-rightnow .qm-ot-body { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.qm-d2 .qm-rightnow .qm-ot-v { font-size: 17px; font-weight: 800; line-height: 1.1; color: var(--ink-900, #1c1917); }
.qm-d2 .qm-rightnow .qm-ot-k { font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--ink-500, #78716c); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-d2 .qm-rightnow .qm-ot-go { margin-inline-start: auto; font-size: 10px; color: var(--ink-400, #a8a29e); opacity: 0; transform: translateX(-4px); transition: opacity var(--d2-t), transform var(--d2-t); }
.qm-d2 .qm-rightnow .qm-ot-item:hover .qm-ot-go { opacity: 1; transform: none; }

/* The strip is a TRIAGE bar — an attention rail, not a stat row. Structure:
   hairline-segmented tiles (no floating gaps). State does the talking: a tile
   whose queue has work is `is-hot` — accent-filled chip, full-ink numeral and
   a live dot pulsing in the same qm-kpi-pulse language as the Live badge — and
   a tile at zero is `is-calm`, receding to gray so the eye lands on the work.
   Status tiles (drawer, venue count) carry neither class and stay neutral. */
.qm-d2 .qm-rightnow .qm-order-totals { gap: 0; }
.qm-d2 .qm-rightnow .qm-ot-item { border-radius: 0; padding: 8px 16px; }
/* The base layer's 3px per-tile accent bar would stack beside the hairline
   dividers as six competing colour stripes — the chip carries the accent now. */
.qm-d2 .qm-rightnow .qm-ot-item::before { display: none; }
/* Labels are the content of a triage bar: wrap to two lines, never ellipsize. */
.qm-d2 .qm-rightnow .qm-ot-k { white-space: normal; line-height: 1.2; }
.qm-d2 .qm-rightnow .qm-ot-item + .qm-ot-item { border-inline-start: 1px solid var(--d2-border); }
.qm-d2 .qm-rightnow .qm-ot-item:first-child { border-start-start-radius: 10px; border-end-start-radius: 10px; }
.qm-d2 .qm-rightnow .qm-ot-item:last-child { border-start-end-radius: 10px; border-end-end-radius: 10px; }
.qm-d2 .qm-rightnow .qm-ot-ico { position: relative; width: 34px; height: 34px; border-radius: 10px; font-size: 13px; }
.qm-d2 .qm-rightnow .qm-ot-item.is-hot .qm-ot-ico { background: var(--kc, var(--d2-accent)); color: #fff; }
.qm-d2 .qm-rightnow .qm-ot-item.is-hot .qm-ot-ico::after {
    content: ''; position: absolute; top: -3px; inset-inline-end: -3px;
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--kc, var(--d2-accent));
    border: 2px solid var(--surface, #fff);
    animation: qm-kpi-pulse 1.8s ease-out infinite;
}
.qm-d2 .qm-rightnow .qm-ot-item.is-hot .qm-ot-v { color: var(--ink-900, #1c1917); }
.qm-d2 .qm-rightnow .qm-ot-item.is-calm .qm-ot-ico { background: color-mix(in srgb, var(--ink-900, #1c1917) 6%, transparent); color: var(--ink-400, #a8a29e); }
.qm-d2 .qm-rightnow .qm-ot-item.is-calm .qm-ot-v { color: var(--ink-500, #78716c); }
.qm-d2 .qm-rightnow .qm-ot-item.is-calm .qm-ot-k { color: var(--ink-400, #a8a29e); }
@media (prefers-reduced-motion: reduce) { .qm-d2 .qm-rightnow .qm-ot-item.is-hot .qm-ot-ico::after { animation: none; } }
/* Below wide desktop the single-row rail cannot hold the head plus six tiles
   without crushing labels, so the strip restacks: the head takes its own row
   and the tiles form an auto-fitting grid — two-up on phones, three/four-up on
   tablets. Dividers give way to gaps; hot/calm chips and dots carry through
   unchanged. The inline rail is reserved for ≥1400px, where it genuinely fits. */
@media (max-width: 1399.98px) {
    .qm-d2 .qm-rightnow { flex-direction: column; align-items: stretch; gap: 10px; }
    .qm-d2 .qm-rightnow .qm-ops-head { border-inline-end: 0; padding-inline-end: 0; }
    .qm-d2 .qm-rightnow .qm-order-totals { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; }
    .qm-d2 .qm-rightnow .qm-ot-item, .qm-d2 .qm-rightnow .qm-ot-item + .qm-ot-item { border: 0; border-radius: 10px; min-width: 0; padding: 8px 10px; }
}

/* The live badge needs nothing from this layer: app.css already draws its 7px
   round dot and pulses it (qm-kpi-pulse), reduced-motion handled. A previous
   revision redefined that ::before here and turned the dot into a full-badge
   square overlay — the layer must never restyle the badge's dot again. */

/* ----------------------------------------------------- snapshot KPI row --- */
.qm-d2 .qm-snap-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--d2-gap); margin-bottom: var(--d2-gap); }
.qm-d2 .qm-kpi-panel--rich { padding: 13px 15px 12px; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-bg { opacity: .05; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-label { font-size: 11px; letter-spacing: .07em; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-value { font-size: 23px; letter-spacing: -.01em; margin-top: 0; font-variant-numeric: tabular-nums; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-meta { margin-top: 3px; font-size: 11px; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-rows { margin-top: 9px; gap: 5px; padding-top: 8px; border-top: 1px dashed var(--d2-border); }
.qm-d2 .qm-kpi-row { font-size: 12px; }
.qm-d2 .qm-kpi-row-val { font-weight: 700; font-variant-numeric: tabular-nums; }
.qm-d2 .qm-kpi-panel--rich .qm-kpi-pillrow { margin-top: 9px; gap: 5px; }
.qm-d2 .qm-kpi-pillrow .qm-pill { font-size: 10px; padding: 3px 8px; }

/* The four KPI accents stay per-card (they key the sparkline hue the partial
   already draws) — but the watermark and dot quiet down so the accent reads
   as a signature, not a wash. */
.qm-d2 .qm-kpi-dot { width: 6px; height: 6px; }

/* ------------------------------------------------------------ chart row --- */
.qm-d2 .qm-charts-grid { gap: var(--d2-gap); margin-bottom: var(--d2-gap); }
.qm-d2 .qm-chart-card .qm-card,
.qm-d2 .qm-chart-card > div { margin-bottom: 0; }
/* …but the HEAD keeps its breathing room: the wrapper reset above swallowed its
   margin too, which stacked the title, the Apex caption row (subtitle + export
   menu) and the plot into one cramped seam. */
.qm-d2 .qm-chart-card .qm-chart-card-head { margin-bottom: 14px; }
/* The chart FILLS the card: in an equal-height row the shorter chart used to
   float in the stretched card's top, leaving dead space under its legend (the
   donuts, most visibly). The figure absorbs the slack, and a chart built with
   height '100%' (the dashboard donuts) grows its circle into it while its
   bottom legend lands on the card's foot — exactly where the bar charts' do.
   The 264px floor keeps percent-height charts alive in single-column stacks. */
.qm-d2 .qm-chart-card { display: flex; flex-direction: column; }
.qm-d2 .qm-chart-card .qm-chart-figure { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; margin: 0; }
/* 244 (was 264): the vendor Breakdown's fill-height cards settle on this floor,
   and trimming it per grid row is what lets the section fit one screen. Fixed-
   height charts ignore the floor entirely. */
.qm-d2 .qm-chart-card .qm-chart-figure .qm-chart { flex: 1 1 auto; min-height: 244px; }

/* ---------------------------------------------------------------- tables --- */
.qm-d2 .qm-table thead th {
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--ink-500, #78716c);
    padding: 8px 12px;
    border-bottom: 1px solid var(--d2-border);
}
.qm-d2 .qm-table tbody td { padding: 9px 12px; font-size: 12.5px; vertical-align: middle; }
.qm-d2 .qm-table tbody tr { transition: background var(--d2-t), box-shadow var(--d2-t); }
.qm-d2 .qm-table tbody tr:hover { background: var(--d2-accent-soft); box-shadow: inset 3px 0 0 var(--d2-accent); }
[dir="rtl"] .qm-d2 .qm-table tbody tr:hover { box-shadow: inset -3px 0 0 var(--d2-accent); }
.qm-d2 .qm-tbl-thumb { width: 36px; height: 36px; border-radius: 9px; }
.qm-d2 .qm-tbl-id { gap: 9px; }
.qm-d2 .qm-tbl-id-text small { font-size: 11px; }
.qm-d2 .qm-table .qm-pill { font-size: 11px; padding: 3px 9px; }

/* Card headers above tables: one line, tighter. */
.qm-d2 .qm-card > .d-flex.align-items-center { margin-bottom: 8px !important; }
.qm-d2 .qm-card-title-ico { font-size: 14.5px; }
.qm-d2 .qm-head-chip { width: 26px; height: 26px; font-size: 11px; border-radius: 8px; }

/* ------------------------------------------------------------- buttons --- */
.qm-d2 .qm-btn-sm { transition: transform var(--d2-t), box-shadow var(--d2-t), background var(--d2-t), color var(--d2-t); }
.qm-d2 .qm-btn-sm:active { transform: scale(.97); }

/* ------------------------------------------------------- page chrome --- */
/* The breadcrumb stays on every page: on deep pages it is real navigation
   (Dashboard / Orders / Order #…). An earlier revision hid it via a body
   class added by end-of-body script, which painted the crumb and then
   yanked it — a flicker on every navigation. Nothing here touches it. */

/* Dark scheme: the layer inherits the app's dark tokens; only the shadows and
   the soft accent need restating against a dark surface. */
@media (prefers-color-scheme: dark) {
    body[data-scheme="dark"] .qm-d2,
    .qm-dash-dark .qm-d2 {
        --d2-border: color-mix(in srgb, #fff 9%, transparent);
        --d2-shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 26px -18px rgba(0, 0, 0, .6);
        --d2-shadow-up: 0 2px 5px rgba(0, 0, 0, .35), 0 16px 34px -18px rgba(0, 0, 0, .7);
        --d2-accent-soft: color-mix(in srgb, var(--d2-accent) 14%, transparent);
    }
}

/* ------------------------------------------- vendor snapshot (plain panels) --- */
/* The vendor KPI row uses the plain panel variant (no rows/pills) in a 5-up
   grid — same compression as the rich cards, same numerals. The card widths
   must be recomputed from the SAME gap token: the base slider sizes cards for
   its own 24px gap, so tightening the gap alone left the difference as dead
   space at the row's trailing edge. */
.qm-d2 .qm-kpi-grid { gap: var(--d2-gap); margin-bottom: var(--d2-gap); }
.qm-d2 .qm-kpi-grid > * { flex-basis: calc((100% - 4 * var(--d2-gap)) / 5); }
@media (max-width: 1199.98px) { .qm-d2 .qm-kpi-grid > * { flex-basis: calc((100% - 3 * var(--d2-gap)) / 4); } }
@media (max-width: 991.98px) { .qm-d2 .qm-kpi-grid > * { flex-basis: calc((100% - 2 * var(--d2-gap)) / 3); } }
/* Phones keep the base slider's one-card-per-view — without this the 3-up rule
   above bleeds below 576px and crushes the cards to a third of the screen. */
@media (max-width: 575.98px) { .qm-d2 .qm-kpi-grid > * { flex-basis: 100%; } }
.qm-d2 .qm-kpi-panel:not(.qm-kpi-panel--rich) { padding: 13px 15px 0; }
.qm-d2 .qm-kpi-panel:not(.qm-kpi-panel--rich) .qm-kpi-label { font-size: 11px; letter-spacing: .07em; }
.qm-d2 .qm-kpi-panel:not(.qm-kpi-panel--rich) .qm-kpi-value { font-size: 23px; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.qm-d2 .qm-kpi-panel:not(.qm-kpi-panel--rich) .qm-kpi-meta { font-size: 11px; }
.qm-d2 .qm-kpi-panel:not(.qm-kpi-panel--rich) .qm-kpi-bg { opacity: .05; }

/* Table-card headers built on qm-ops-head (vendor tables): same tight rhythm
   as the admin's flex headers. */
.qm-d2 .qm-card > .qm-ops-head { margin-bottom: 8px; }
.qm-d2 .qm-card > .qm-ops-head h2 { font-size: 14.5px; }
.qm-d2 .qm-card > .qm-ops-head .qm-ops-sub { font-size: 11px; }
/* A tally placed directly in the head row (not wrapped with the title) sits on
   the title's own line, trailing side — no stacked-layout top margin. */
.qm-d2 .qm-ops-head > .qm-ops-sub { margin-top: 0; }

/* --- 35c. Floor plan: furniture rendering --------------------------------
   The floor view draws a ROOM: a dot-grid canvas, and each table as top-down
   furniture — the tabletop in its real shape with chairs placed around it by
   seat count. Status is the tabletop's surface (vacant outline, occupied
   filled, reserved amber, cleaning slate-dashed). All interaction hooks
   (.qm-table-card data-*, .qm-tc-actions, .qm-tc-status, cues, drag, a11y)
   are the same elements as before — only the face changed. */
.qm-d2 .qm-floor-grid {
    background:
        radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--ink-900, #1c1917) 9%, transparent) 1px, transparent 1.6px) 0 0 / 18px 18px,
        color-mix(in srgb, var(--surface, #fff) 88%, #f3efe9);
    border: 1px solid var(--d2-border);
    border-radius: 12px;
}
.qm-d2 .qm-table-card {
    --tt-w: 78px; --tt-h: 78px; --tt-r: 14px; --tt-orbit: 52px;
    --tt-face: var(--surface, #fff);
    --tt-ink: var(--ink-800, #292524);
    --tt-edge: color-mix(in srgb, var(--ink-900, #1c1917) 22%, transparent);
    --tt-chair: color-mix(in srgb, var(--ink-900, #1c1917) 26%, #fff);
    width: 128px; min-height: 128px;
    background: transparent; border: 0; box-shadow: none; padding: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
}
.qm-d2 .qm-table-card.is-shape-rectangle { --tt-w: 112px; --tt-h: 60px; width: 156px; }
.qm-d2 .qm-table-card.is-shape-round { --tt-r: 50%; }

.qm-d2 .qm-tbl-unit { position: relative; width: var(--tt-w); height: var(--tt-h); margin: 22px auto 2px; }
.qm-d2 .qm-tbl-top {
    position: absolute; inset: 0; z-index: 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
    background: var(--tt-face);
    border: 1.5px solid var(--tt-edge);
    border-radius: var(--tt-r);
    box-shadow: 0 2px 5px rgba(28, 25, 23, .10), inset 0 0 0 4px color-mix(in srgb, var(--tt-edge) 24%, transparent);
    transition: box-shadow var(--d2-t), border-color var(--d2-t), background var(--d2-t);
}
.qm-d2 .qm-tc-label { font-size: 14px; font-weight: 800; line-height: 1.05; color: var(--tt-ink); }
.qm-d2 .qm-tc-seats { font-size: 10px; font-weight: 600; color: color-mix(in srgb, var(--tt-ink) 62%, transparent); }
.qm-d2 .qm-tc-seats i { font-size: 9px; }

/* Chairs: small rounded bars hugging the tabletop. */
.qm-d2 .qm-chair { position: absolute; z-index: 0; background: var(--tt-chair); border: 1px solid color-mix(in srgb, var(--tt-chair) 60%, #786c60); transition: background var(--d2-t); }
.qm-d2 .qm-chair.is-top    { top: -12px;  left: 50%; width: 24px; height: 8px; border-radius: 5px 5px 2px 2px; transform: translateX(-50%); }
.qm-d2 .qm-chair.is-bottom { bottom: -12px; left: 50%; width: 24px; height: 8px; border-radius: 2px 2px 5px 5px; transform: translateX(-50%); }
.qm-d2 .qm-chair.is-left  { left: -12px;  top: 50%; width: 8px; height: 24px; margin-top: -12px; border-radius: 5px 2px 2px 5px; }
.qm-d2 .qm-chair.is-right { right: -12px; top: 50%; width: 8px; height: 24px; margin-top: -12px; border-radius: 2px 5px 5px 2px; }
.qm-d2 .qm-chair.is-radial {
    left: 50%; top: 50%; width: 22px; height: 8px; margin: -4px 0 0 -11px;
    border-radius: 5px 5px 2px 2px; transform-origin: center;
}

/* Status surfaces (semantic, matching the legend + status pill colors). */
.qm-d2 .qm-table-card.is-occupied { --tt-face: #40343a; --tt-ink: #fff; --tt-edge: #40343a; --tt-chair: #6b5a62; }
.qm-d2 .qm-table-card.is-occupied .qm-tc-seats { color: rgba(255, 255, 255, .72); }
.qm-d2 .qm-table-card.is-reserved { --tt-face: color-mix(in srgb, #d98c1c 14%, #fff); --tt-edge: #d98c1c; --tt-chair: color-mix(in srgb, #d98c1c 38%, #fff); }
.qm-d2 .qm-table-card.is-cleaning { --tt-face: color-mix(in srgb, #5b6675 9%, #fff); --tt-edge: #8b939e; }
.qm-d2 .qm-table-card.is-cleaning .qm-tbl-top { border-style: dashed; }
.qm-d2 .qm-table-card.is-vacant .qm-tbl-top { box-shadow: 0 2px 5px rgba(28, 25, 23, .10), inset 0 0 0 4px color-mix(in srgb, #1f9d4d 16%, transparent); }
.qm-d2 .qm-table-card.is-hidden-table .qm-tbl-unit { opacity: .45; filter: grayscale(.6); }
.qm-d2 .qm-table-card.is-reserved-soon .qm-tbl-top { box-shadow: 0 0 0 3px color-mix(in srgb, #d98c1c 55%, transparent), 0 2px 5px rgba(28, 25, 23, .12); }
.qm-d2 .qm-table-card.has-event .qm-tbl-top { border-color: var(--ev, var(--tt-edge)); }

/* Selection / drag / merge-highlight states ride the tabletop. */
.qm-d2 .qm-table-card.is-selected .qm-tbl-top,
.qm-d2 .qm-table-card:focus-visible .qm-tbl-top { box-shadow: 0 0 0 3px color-mix(in srgb, var(--d2-accent) 45%, transparent), 0 4px 10px rgba(28, 25, 23, .16); }
.qm-d2 .qm-table-card.is-dragging { z-index: 30; }
.qm-d2 .qm-table-card.is-dragging .qm-tbl-top { box-shadow: 0 10px 24px rgba(28, 25, 23, .28); transform: scale(1.03); }
.qm-d2 .qm-table-card.is-merge-hi .qm-tbl-top { box-shadow: 0 0 0 3px color-mix(in srgb, #2484c6 45%, transparent); }

/* The status pill sits under the furniture; the merge tag over its top edge. */
.qm-d2 .qm-tc-status {
    position: static; margin: 0 auto; display: inline-flex; align-items: center; gap: 5px;
    font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 999px;
    background: var(--surface, #fff); border: 1px solid var(--d2-border);
    box-shadow: 0 1px 2px rgba(28, 25, 23, .08);
}
.qm-d2 .qm-tc-merge {
    position: absolute; top: 8px; left: 50%; transform: translateX(-50%); z-index: 3;
    font-size: 10px; font-weight: 700; padding: 1.5px 7px; border-radius: 999px;
    background: color-mix(in srgb, #2484c6 12%, #fff); color: #1c5f95; border: 1px solid color-mix(in srgb, #2484c6 35%, #fff);
    white-space: nowrap;
}
.qm-d2 .qm-tc-actions { top: 2px; inset-inline-end: 2px; z-index: 4; }
.qm-d2 .qm-tc-cues { top: 2px; inset-inline-start: 2px; z-index: 4; }

/* Shape picker: each option previews the furniture at small scale. */
.qm-d2 .qm-shape-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.qm-d2 .qm-shape-opt {
    display: flex; flex-direction: column; align-items: center; gap: 7px;
    padding: 12px 6px 9px; border-radius: 12px;
    background: var(--surface, #fff); border: 1.5px solid var(--d2-border);
    transition: border-color var(--d2-t), box-shadow var(--d2-t), background var(--d2-t);
}
.qm-d2 .qm-shape-opt:hover { border-color: color-mix(in srgb, var(--d2-accent) 40%, var(--d2-border)); }
.qm-d2 .qm-shape-opt.is-active { border-color: var(--d2-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--d2-accent) 14%, transparent); background: color-mix(in srgb, var(--d2-accent) 4%, #fff); }
.qm-d2 .qm-shape-name { font-size: 11px; font-weight: 700; }
/* ONE TABLE GLYPH, DRAWN ONCE - used by the rail stamps AND the inspector's shape picker.
   The base .qm-shape-ico paints the whole shape ON THE ELEMENT (a bordered, rounded box). This
   design paints the tabletop on ::before and the chairs on ::after instead, so the element is
   only a frame - but its border and radius were never cleared. That left a SECOND outline drawn
   around every glyph, and on `round`, whose frame is wider than it is tall, the ghost was an
   ELLIPSE around a circle. That is the "broken" look; it is cleared here.
   Geometry now lives in ONE place and is proportional: a context changes --ico-w/--ico-h and the
   colours, never the shape, so the rail and the inspector cannot drift apart again. */
.qm-d2 .qm-shape-ico {
    --ico-w: 46px; --ico-h: 40px;
    position: relative; display: block; width: var(--ico-w); height: var(--ico-h);
    border: 0; border-radius: 0; background: none; box-shadow: none;
}
/* The tabletop, centred in the frame. */
.qm-d2 .qm-shape-ico::before {
    content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    border: 1.5px solid var(--ink-500, #78716c); border-radius: 4px;
    background: color-mix(in srgb, var(--ink-500, #78716c) 8%, #fff);
}
/* The chair pair: one nub above the tabletop, its twin below it via the shadow. The offset is a
   fraction of the frame, so both chairs stay put at any size. */
.qm-d2 .qm-shape-ico::after {
    content: ""; position: absolute; left: 50%; top: 6%; transform: translateX(-50%);
    width: 26%; height: 10%; min-height: 3px; border-radius: 3px;
    background: var(--ink-400, #a8a29e);
    box-shadow: 0 calc(var(--ico-h) * .84) 0 var(--ink-400, #a8a29e);
}
/* aspect-ratio keeps a square SQUARE and a round table CIRCULAR whatever the frame's proportions
   are - which is what the old pixel insets could not do. */
.qm-d2 .qm-shape-ico-square::before { height: 52%; width: auto; aspect-ratio: 1 / 1; }
.qm-d2 .qm-shape-ico-rect::before { height: 38%; width: 76%; }
.qm-d2 .qm-shape-ico-round::before { height: 58%; width: auto; aspect-ratio: 1 / 1; border-radius: 50%; }

/* --- 35d. Floor plan studio ----------------------------------------------
   The page is ONE editor window, not stacked cards: a dark tool rail on the
   left (shape stamps, layout toggle, zoom column), a context strip across the
   top (zones, live legend, view switch, QR), the room as a full-bleed pannable
   canvas, and the inspector sliding over the canvas edge. The rail is always
   there — picking a shape arms a ghost table and turns edit mode on itself. */
.qm-d2 .qm-fp {
    display: grid; grid-template-columns: 84px minmax(0, 1fr);
    height: clamp(560px, calc(100dvh - 230px), 940px);
    border: 1px solid var(--d2-border); border-radius: 16px; overflow: hidden;
    background: var(--surface, #fff);
    box-shadow: var(--d2-shadow);
}

/* ------------------------------------------------------------ tool rail --- */
.qm-d2 .qm-fp-rail {
    display: flex; flex-direction: column; align-items: stretch; gap: 14px;
    padding: 14px 10px;
    background: linear-gradient(180deg, #262024 0%, #1d181b 100%);
    overflow-y: auto; overflow-x: hidden;
}
.qm-d2 .qm-fp-rail-grp { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
.qm-d2 .qm-fp-rail-cap {
    font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: rgba(255, 255, 255, .38); text-align: center; padding-bottom: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.qm-d2 .qm-fp-rail-spring { flex: 1 1 auto; }
.qm-d2 .qm-fp-rail .qm-fp-stamp,
.qm-d2 .qm-fp-rail .qm-fp-mode,
.qm-d2 .qm-fp-rail .qm-fp-lock {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
    padding: 9px 4px 7px; border-radius: 12px;
    border: 1px solid transparent; background: rgba(255, 255, 255, .05);
    color: rgba(255, 255, 255, .78); font-size: 10.5px; font-weight: 700;
    transition: background var(--d2-t), border-color var(--d2-t), color var(--d2-t), box-shadow var(--d2-t);
}
.qm-d2 .qm-fp-rail .qm-fp-stamp:hover,
.qm-d2 .qm-fp-rail .qm-fp-mode:hover,
.qm-d2 .qm-fp-rail .qm-fp-lock:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.qm-d2 .qm-fp-rail .qm-fp-stamp.is-active,
.qm-d2 .qm-fp-rail .qm-fp-mode.is-on,
.qm-d2 .qm-fp-rail .qm-fp-lock.is-on {
    background: color-mix(in srgb, var(--d2-accent) 26%, transparent);
    border-color: color-mix(in srgb, var(--d2-accent) 65%, transparent);
    color: #fff;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--d2-accent) 16%, transparent);
}
.qm-d2 .qm-fp-rail .qm-fp-mode i,
.qm-d2 .qm-fp-rail .qm-fp-lock i { font-size: 15px; padding: 3px 0 2px; }
.qm-d2 .qm-fp-stamp-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The shape glyphs on the dark rail: light tabletop, light chairs. */
/* The SAME glyph on the dark rail: smaller, and light-on-dark. Size and colour only - the shape
   itself is never redefined here, which is what keeps the two sets identical. */
.qm-d2 .qm-fp-rail .qm-shape-ico { --ico-w: 34px; --ico-h: 28px; }
.qm-d2 .qm-fp-rail .qm-shape-ico::before { border-color: rgba(255, 255, 255, .82); background: rgba(255, 255, 255, .10); }
.qm-d2 .qm-fp-rail .qm-shape-ico::after { background: rgba(255, 255, 255, .6); box-shadow: 0 calc(var(--ico-h) * .84) 0 rgba(255, 255, 255, .6); }
.qm-d2 .qm-fp-rail .qm-fp-stamp.is-active .qm-shape-ico::before { border-color: #fff; background: color-mix(in srgb, var(--d2-accent) 40%, transparent); }

/* Zoom column at the rail's foot. */
.qm-d2 .qm-fp-rail .qm-fp-zoom { gap: 4px; }
.qm-d2 .qm-fp-rail .qm-fp-zoom button {
    height: 30px; border: 0; border-radius: 10px; background: rgba(255, 255, 255, .05);
    color: rgba(255, 255, 255, .78); font-size: 11px;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--d2-t), color var(--d2-t);
}
.qm-d2 .qm-fp-rail .qm-fp-zoom button:hover { background: rgba(255, 255, 255, .12); color: #fff; }
.qm-d2 .qm-fp-zoom-val {
    font-size: 10.5px; font-weight: 700; text-align: center; padding: 2px 0;
    color: rgba(255, 255, 255, .55); font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------- context strip --- */
.qm-d2 .qm-fp-main { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.qm-d2 .qm-fp-ctx {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 10px 14px;
    background: var(--surface, #fff);
    border-bottom: 1px solid var(--d2-border);
    position: relative; z-index: 6;
}
.qm-d2 .qm-fp-ctx-spring { flex: 1 1 auto; }
.qm-d2 .qm-fp-zones { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.qm-d2 .qm-fp-zone {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 12.5px; font-weight: 700; text-decoration: none;
    color: var(--ink-600, #57534e);
    padding: 6px 12px; border-radius: 999px; border: 1px solid transparent;
    transition: background var(--d2-t), color var(--d2-t), border-color var(--d2-t);
}
.qm-d2 .qm-fp-zone:hover { background: var(--d2-accent-soft); }
.qm-d2 .qm-fp-zone.is-active { background: var(--d2-accent); color: #fff; }
.qm-d2 .qm-fp-zone-n { font-size: 10.5px; font-weight: 800; padding: 1px 7px; border-radius: 999px; background: rgba(0, 0, 0, .10); }
.qm-d2 .qm-fp-zone.is-active .qm-fp-zone-n { background: rgba(255, 255, 255, .22); }
.qm-d2 .qm-fp-zone-add { border-style: dashed; border-color: var(--d2-border); color: var(--ink-500, #78716c); }
.qm-d2 .qm-fp-zone-edit {
    width: 26px; height: 26px; border-radius: 8px; border: 0; background: transparent;
    color: var(--ink-400, #a8a29e); display: inline-flex; align-items: center; justify-content: center; font-size: 11px;
}
.qm-d2 .qm-fp-zone-edit:hover { background: var(--d2-accent-soft); color: var(--d2-accent); }
.qm-d2 .qm-fp-legend { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.qm-d2 .qm-fp-leg { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--ink-500, #78716c); }
.qm-d2 .qm-fp-leg b { color: var(--ink-800, #292524); }

/* ------------------------------------------------------------- the room --- */
.qm-d2 .qm-fp-stage {
    position: relative; flex: 1 1 auto; min-height: 0;
    overflow: hidden;
    background: color-mix(in srgb, var(--surface, #fff) 72%, #ece8e2);
    box-shadow: inset 0 1px 6px rgba(28, 25, 23, .06);
    touch-action: none;
}
.qm-d2 .qm-fp-floor {
    position: relative; transform-origin: 0 0;
    background:
        linear-gradient(color-mix(in srgb, var(--ink-900, #1c1917) 5%, transparent) 1px, transparent 1px) 0 0 / 20px 20px,
        linear-gradient(90deg, color-mix(in srgb, var(--ink-900, #1c1917) 5%, transparent) 1px, transparent 1px) 0 0 / 20px 20px,
        radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--ink-900, #1c1917) 10%, transparent) 1px, transparent 1.6px) 0 0 / 100px 100px,
        var(--surface, #fff);
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--ink-900, #1c1917) 10%, transparent);
    cursor: grab;
}
.qm-d2 .qm-fp-floor:active { cursor: grabbing; }
.qm-d2 .qm-fp.is-stamping .qm-fp-floor { cursor: copy; }
.qm-d2 .qm-fp-floor.is-empty::after {
    content: attr(data-empty);
    position: absolute; top: 120px; left: 50%; transform: translateX(-50%);
    font-size: 14px; font-weight: 600; color: var(--ink-400, #a8a29e);
}
/* Tables move only in edit mode; in service mode the room is read-and-act. */
.qm-d2 .qm-fp:not(.is-editing) .qm-table-card { cursor: pointer; }
.qm-d2 .qm-fp.is-editing .qm-table-card { cursor: move; }
.qm-d2 .qm-fp.is-editing .qm-tc-status { pointer-events: none; opacity: .55; }

/* The ghost: the armed shape riding the cursor until the click plants it. */
.qm-d2 .qm-fp-ghost { opacity: .6; pointer-events: none; z-index: 25; }
.qm-d2 .qm-fp-ghost .qm-tbl-top { box-shadow: 0 0 0 2px var(--d2-accent), 0 10px 22px color-mix(in srgb, var(--d2-accent) 25%, transparent); }

/* Furniture gets air: pills clear the chair extent, cards stop crowding. */
.qm-d2 .qm-table-card { width: 148px; min-height: 156px; gap: 8px; }
.qm-d2 .qm-table-card.is-shape-rectangle { width: 180px; }
.qm-d2 .qm-tbl-unit { margin: 26px auto 12px; }
.qm-d2 .qm-tc-status { position: relative; z-index: 2; }

/* --------------------------------------------------- inspector slide-over --- */
.qm-d2 .qm-fp-inspector {
    position: absolute; z-index: 30;
    top: 64px; bottom: 14px; inset-inline-end: 14px;
    width: min(384px, calc(100% - 28px));
    background: var(--surface, #fff);
    border: 1px solid var(--d2-border); border-radius: 14px;
    box-shadow: var(--d2-shadow-up);
    display: flex; flex-direction: column; overflow: hidden;
    animation: qm-fp-insp-in var(--d2-t) ease-out;
}
/* Direction-neutral entrance (the panel docks at either edge under RTL). */
@keyframes qm-fp-insp-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.qm-d2 .qm-fp-inspector form { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.qm-d2 .qm-fp-insp-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 16px 20px; border-bottom: 1px solid var(--d2-border);
}
.qm-d2 .qm-fp-insp-title { font-size: 15px; font-weight: 800; margin: 0; }
.qm-d2 .qm-fp-insp-body { padding: 20px; overflow: auto; flex: 1 1 auto; }
.qm-d2 .qm-fp-insp-body .form-label { font-size: 12px; font-weight: 700; margin-bottom: 7px; }
.qm-d2 .qm-fp-insp-body .row + .row { margin-top: 14px; }
.qm-d2 .qm-fp-insp-body .mb-3 { margin-bottom: 18px !important; }
.qm-d2 .qm-fp-insp-body .mt-3 { margin-top: 18px !important; }
.qm-d2 .qm-fp-insp-body .form-check.form-switch { padding-block: 4px; }
.qm-d2 .qm-fp-insp-foot {
    display: flex; align-items: center; gap: 10px;
    padding: 14px 20px; border-top: 1px solid var(--d2-border);
}
.qm-d2 .qm-fp-insp-foot .qm-btn { padding-inline: 18px; }
.qm-d2 .qm-fp-insp-spacer { flex: 1; }
.qm-d2 .qm-btn-danger-line { color: #b3123a; border-color: color-mix(in srgb, #b3123a 40%, #fff); }
.qm-d2 .qm-btn-danger-line:hover { background: color-mix(in srgb, #b3123a 8%, #fff); }

/* Shape tiles in the inspector: room to read as furniture, not thumbnails. */
.qm-d2 .qm-shape-picker { gap: 10px; }
.qm-d2 .qm-shape-opt { padding: 14px 8px 12px; gap: 10px; border-radius: 14px; }
.qm-d2 .qm-shape-picker .qm-shape-ico { --ico-w: 46px; --ico-h: 40px; }
.qm-d2 .qm-shape-name { font-size: 12px; }

/* Seats stepper: minus / value / plus as one control. */
.qm-d2 .qm-fp-stepper { display: flex; align-items: stretch; gap: 0; border: 1px solid var(--d2-border); border-radius: 10px; overflow: hidden; }
.qm-d2 .qm-fp-stepper button {
    width: 38px; border: 0; background: var(--surface, #fff);
    color: var(--ink-600, #57534e); font-size: 11px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--d2-t), color var(--d2-t);
}
.qm-d2 .qm-fp-stepper button:hover { background: var(--d2-accent-soft); color: var(--d2-accent); }
.qm-d2 .qm-fp-stepper input {
    border: 0; border-radius: 0; text-align: center; flex: 1 1 auto; min-width: 0;
    border-inline: 1px solid var(--d2-border);
    font-weight: 700; font-variant-numeric: tabular-nums;
    -moz-appearance: textfield; appearance: textfield;
}
.qm-d2 .qm-fp-stepper input::-webkit-outer-spin-button,
.qm-d2 .qm-fp-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Timeline view keeps the context strip (zones, legend, the switch itself):
   only the room chrome leaves; without this the switch vanished and the
   timeline was a one-way door. */
.qm-tables-views[data-tables-view='timeline'] .qm-tv-floor { display: block; }
.qm-tables-views[data-tables-view='timeline'] .qm-fp { grid-template-columns: minmax(0, 1fr); height: auto; box-shadow: none; }
.qm-tables-views[data-tables-view='timeline'] .qm-fp-rail,
.qm-tables-views[data-tables-view='timeline'] .qm-fp-stage,
.qm-tables-views[data-tables-view='timeline'] .qm-fp-inspector { display: none; }

/* Small screens: the rail lies down across the top, inspector fills the room. */
@media (max-width: 991.98px) {
    .qm-d2 .qm-fp { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto; height: auto; }
    .qm-d2 .qm-fp-main { height: clamp(460px, calc(100dvh - 330px), 760px); }
    .qm-d2 .qm-fp-rail { flex-direction: row; align-items: center; padding: 8px 10px; overflow-x: auto; overflow-y: hidden; }
    .qm-d2 .qm-fp-rail-grp { flex-direction: row; align-items: center; }
    .qm-d2 .qm-fp-rail-cap { display: none; }
    .qm-d2 .qm-fp-rail .qm-fp-stamp, .qm-d2 .qm-fp-rail .qm-fp-mode, .qm-d2 .qm-fp-rail .qm-fp-lock { flex-direction: row; padding: 7px 12px; }
    .qm-d2 .qm-fp-rail .qm-fp-zoom { flex-direction: row; }
    .qm-d2 .qm-fp-rail .qm-fp-zoom button { width: 34px; height: 30px; }
    .qm-d2 .qm-fp-inspector { top: auto; bottom: 10px; inset-inline: 10px; width: auto; max-height: calc(100% - 20px); }
}

/* Locked camera: the rail's lock freezes pan/zoom/drag — the room reads inert. */
.qm-d2 .qm-fp.is-locked .qm-fp-floor { cursor: default; }
.qm-d2 .qm-fp.is-locked .qm-fp-floor:active { cursor: default; }
.qm-d2 .qm-fp.is-locked.is-editing .qm-table-card { cursor: default; }
.qm-d2 .qm-fp.is-locked .qm-fp-zoom button { opacity: .38; pointer-events: none; }
.qm-d2 .qm-fp.is-locked .qm-fp-zoom-val { opacity: .38; }

/* Fullscreen (the topbar's expand): the chrome is gone, so the editor takes
   the whole viewport — without this it kept reserving header space and left
   a dead band under the window. */
body.qm-fullscreen .qm-d2 .qm-fp { height: calc(100dvh - 32px); }
@media (max-width: 991.98px) {
    body.qm-fullscreen .qm-d2 .qm-fp { height: auto; }
    body.qm-fullscreen .qm-d2 .qm-fp-main { height: calc(100dvh - 110px); }
}

/* Business-position modules (vendor dashboard): reviews / payouts / reach.
   Excerpt cards — a lead figure, then compact rows, equal heights per row. */
.qm-d2 .qm-biz-row { margin-bottom: var(--d2-gap, 14px); }
/* A flex item's min-width:auto lets a wide table set the card's floor width,
   pushing the page into sideways scroll on phones instead of letting
   .table-responsive do its job — pin the chain so the scroller scrolls. */
.qm-d2 .qm-biz-row .qm-card { min-width: 0; }
.qm-d2 .qm-biz-row .table-responsive { min-width: 0; max-width: 100%; }
.qm-d2 .qm-biz-card { flex: 1 1 auto; display: flex; flex-direction: column; margin-bottom: 0; }
.qm-d2 .qm-biz-lead { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; padding: 2px 0 10px; }
.qm-d2 .qm-biz-big { font-size: 23px; font-weight: 800; color: var(--ink-900, #1c1917); letter-spacing: -.01em; font-variant-numeric: tabular-nums; line-height: 1; }
.qm-d2 .qm-biz-list { display: flex; flex-direction: column; }
.qm-d2 .qm-biz-item { padding: 10px 0; border-top: 1px solid var(--d2-border); }
.qm-d2 .qm-biz-item-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; }
.qm-d2 .qm-biz-quote {
    margin: 3px 0 4px; font-size: 12.5px; color: var(--ink-600, #57534e);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.qm-d2 .qm-biz-stat { display: flex; flex-direction: column; gap: 1px; padding: 10px 0; border-top: 1px solid var(--d2-border); }
.qm-d2 .qm-biz-stat:first-of-type { border-top: 0; }
/* The biz row stretches all three cards equal, and a chart module shorter than
   its neighbours was left with one dead hole under the stat row. The spare
   height DISTRIBUTES instead: head, chart and stat row space out evenly, so
   the surplus reads as balanced breathing room, not an empty foot. */
.qm-d2 .qm-biz-card:has(> .qm-chart) { justify-content: space-between; }
/* A chart mounted directly in the module (admin plan mix) absorbs that spare
   height itself instead of floating in it — bars breathe, no dead band. */
.qm-d2 .qm-biz-card > .qm-chart { flex: 1 1 auto; min-height: 216px; }
/* A module with nothing to report yet shows a compact empty state — the
   full-page .qm-empty's 64px padding dwarfs a third-width card. Vertically
   centred in the stretched card so it reads as the module's answer. */
/* A table module shorter than its neighbour (one booking today) left its
   pager floating mid-card with a dead band beneath. The foot PINS to the
   card's bottom edge instead — however many rows the table has, the foot
   holds the same line as the neighbouring card's foot. */
.qm-d2 .qm-biz-card > .qm-table-foot { margin-top: auto; }
.qm-d2 .qm-biz-card .qm-empty { padding: 22px 12px; margin: auto 0; }
.qm-d2 .qm-biz-card .qm-empty > i { font-size: 28px; margin-bottom: 10px; }
.qm-d2 .qm-biz-card .qm-empty p { font-size: 13px; }
.qm-d2 .qm-biz-card .qm-empty .qm-btn { margin-top: 14px; }
/* Stat trio under a module chart (sponsored reach): one row, three columns,
   title + number only — the chart above carries the story, so the stacked
   per-stat rules give way to column dividers. */
.qm-d2 .qm-biz-statrow { display: flex; gap: 12px; margin-top: auto; padding-top: 10px; border-top: 1px solid var(--d2-border); }
/* The module's View-all rides the card's own bottom-right corner: brand red,
   square shoulders except the corner it shares with the card. The negative
   margins carry it through the card's padding so it sits truly flush. */
.qm-d2 .qm-biz-statrow .qm-biz-cta {
  margin: -10px calc(var(--d2-pad) * -1) calc(var(--d2-pad) * -1) auto;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 18px;
  font-size: 12.5px; font-weight: 700; text-decoration: none;
  background: var(--qm-primary); color: #fff;
  border-radius: 0 0 calc(var(--r-lg) - 1px) 0;
  transition: background var(--t-fast);
}
.qm-d2 .qm-biz-statrow .qm-biz-cta:hover { background: var(--qm-primary-600); color: #fff; }
.qm-d2 .qm-biz-statrow .qm-biz-cta i { font-size: 10px; }
/* Stars inside a footer stat read at label scale, tucked beside the average. */
.qm-d2 .qm-biz-statrow .qm-review-stars { font-size: 11px; margin-inline-start: 4px; }
.qm-d2 .qm-biz-statrow .qm-biz-stat { flex: 1 1 0; min-width: 0; padding: 0; border-top: 0; }
.qm-d2 .qm-biz-statrow .qm-biz-stat + .qm-biz-stat { border-inline-start: 1px solid var(--d2-border); padding-inline-start: 12px; }
.qm-d2 .qm-biz-statrow .qm-biz-stat-val { font-size: 19px; }
/* A per-plan cell's label is a plan NAME (data, any length), so the equal-split
   rule can't hold here — five even columns leave ~62px and clip "Enterprise".
   Every cell in the row instead grows from its own name's width, sharing the
   slack equally: the row still spreads edge to edge with an even rhythm, but
   no name is ever truncated. The ellipsis only guards runaway names, and on
   phones the set stands down: the bars right above tell the story there. */
.qm-d2 .qm-biz-statrow:has(.qm-biz-stat--plan) .qm-biz-stat { flex: 1 1 auto; }
.qm-d2 .qm-biz-statrow .qm-biz-stat--plan .qm-biz-stat-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 575.98px) {
  .qm-d2 .qm-biz-statrow .qm-biz-stat--plan { display: none; }
  /* A three-cell footer is a crowd on a phone: secondary cells stand down and
     the row keeps its headline stat beside the View-all. */
  .qm-d2 .qm-biz-statrow .qm-biz-stat--sm-off { display: none; }
}
/* Floating map foot: the module's stats + View-all riding ON the map, docked to
   its lower edge — the biz-card footer language lifted above the tiles (Leaflet
   controls stop at z-index 1000). The wrap owns the card slack the map filled.
   isolation keeps those z-indexes internal: without it the foot (1001) and the
   tiles outrank the sticky chrome (30) and paint over the top bar on scroll. */
.qm-d2 .qm-mapwrap { position: relative; isolation: isolate; flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.qm-d2 .qm-mapwrap > .qm-map { flex: 1 1 auto; }
.qm-d2 .qm-mapfoot {
  position: absolute; inset-inline: 12px; bottom: 12px; z-index: 1001;
  display: flex; align-items: stretch; gap: 12px;
  padding: 11px 14px;
  background: rgba(255, 255, 255, .84);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, .65);
  border-radius: var(--r-md);
  box-shadow: 0 10px 28px rgba(0, 0, 0, .16);
}
/* The base stat cell pads for card footers; inside the floating bar it centres
   instead — equal air above and below, dividers still spanning full height. */
.qm-d2 .qm-mapfoot .qm-biz-stat { flex: 1 1 0; min-width: 0; padding: 0; border-top: 0; justify-content: center; }
.qm-d2 .qm-mapfoot .qm-biz-stat + .qm-biz-stat { border-inline-start: 1px solid var(--d2-border); padding-inline-start: 12px; }
.qm-d2 .qm-mapfoot .qm-biz-stat-val { font-size: 19px; }
.qm-d2 .qm-mapfoot-cta {
  margin: -12px -15px -12px auto;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 12px 18px; font-size: 12.5px; font-weight: 700; text-decoration: none;
  background: var(--qm-primary); color: #fff;
  /* The card floats with every corner rounded, so the CTA caps its whole
     trailing edge — logical corners, so RTL mirrors it for free. */
  border-radius: 0; border-start-end-radius: var(--r-md); border-end-end-radius: var(--r-md);
  transition: background var(--t-fast);
}
.qm-d2 .qm-mapfoot-cta:hover { background: var(--qm-primary-600); color: #fff; }
.qm-d2 .qm-mapfoot-cta i { font-size: 10px; }
.qm-d2 .qm-biz-stat-label { font-size: 12px; font-weight: 700; color: var(--ink-600, #57534e); }
.qm-d2 .qm-biz-stat-val { font-size: 23px; font-weight: 800; color: var(--ink-900, #1c1917); letter-spacing: -.01em; font-variant-numeric: tabular-nums; line-height: 1.15; }

/* Dish-photo backdrop on a KPI card (kpi-card's opt-in $bgImage — e.g. the
   best seller): the dish shows at FULL strength on the card's trailing side —
   no tint, no dimming — and the mask feathers it out toward the text side,
   so the copy always sits on clean card white. */
.qm-kpi-panel .qm-kpi-bgimg {
    position: absolute; inset: 0; z-index: 0; pointer-events: none;
    background-size: cover; background-position: center;
    -webkit-mask-image: linear-gradient(to left, #000 34%, transparent 74%);
    mask-image: linear-gradient(to left, #000 34%, transparent 74%);
}
/* Copy on a photo tile stays in the white zone: capped short of the feather,
   wrapping instead of wandering onto the dish. */
.qm-kpi-panel.has-bgimg .qm-kpi-main { position: relative; z-index: 1; max-width: 60%; }
/* The best-seller tile (the one that carries a dish photo) wears the same
   leading-card ring the pricing cards use — the brand border marks it as the
   headline of the row. */
.qm-kpi-panel.has-bgimg { border-color: color-mix(in srgb, var(--qm-primary) 42%, var(--line)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 8%, transparent); }

/* Map filling a flex column card (dashboard 8/4 rows). */
.qm-d2 .qm-map-fill { flex: 1 1 auto; height: auto; min-height: 320px; }

/* --- 35h. Get-started setup console (admin/vendor) ------------------------
   A two-column console in the quiet-SaaS register: a sticky summary rail —
   the completion figure over a hairline meter, stage anchors, and the ONE
   accent CTA pointing at the next open step — beside a flat task list, one
   bordered card per stage with hairline-separated rows, open work sorted
   first. Colour is rationed: ink carries the page; the accent appears only
   on the CTA, the required markers and the current anchor; a finished task
   dims to a thin grey tick. No fills, no badges, no celebration; the only
   green is the meter fill once everything reaches 100%. */
.qm-d2 .qm-su { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 16px; align-items: start; }
/* Clear the full sticky chrome — top bar + titled subhead (168px) plus the
   breadcrumb pill that overhangs its bottom edge by half its height. */
.qm-d2 .qm-su-side { position: sticky; top: 192px; }
.qm-d2 .qm-su-panel { display: flex; flex-direction: column; gap: 18px; padding: 22px; }
.qm-d2 .qm-su-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.qm-d2 .qm-su-brand img { display: block; max-height: 26px; width: auto; max-width: 170px; }
.qm-d2 .qm-su-brand span { font-size: 12px; font-weight: 700; color: var(--ink-500, #78716c); text-transform: uppercase; letter-spacing: .05em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-d2 .qm-su-title h2 { font-size: var(--fs-800, 18px); font-weight: 800; letter-spacing: -.01em; margin: 0 0 4px; }
.qm-d2 .qm-su-title p { font-size: var(--fs-400, 13px); color: var(--ink-500, #78716c); margin: 0; line-height: 1.55; }
.qm-d2 .qm-su-meter { display: flex; flex-direction: column; gap: 8px; }
.qm-d2 .qm-su-meter-row { display: flex; align-items: baseline; justify-content: space-between; }
.qm-d2 .qm-su-meter-row strong { font-size: 26px; font-weight: 800; letter-spacing: -.02em; line-height: 1; color: var(--ink-900, #1c1917); font-variant-numeric: tabular-nums; }
.qm-d2 .qm-su-meter-row strong span { font-size: var(--fs-400, 13px); font-weight: 700; color: var(--ink-500, #78716c); }
.qm-d2 .qm-su-meter-row em { font-style: normal; font-size: 12px; font-weight: 700; color: var(--ink-500, #78716c); font-variant-numeric: tabular-nums; }
/* The meter states its verdict: accent while work remains, green at 100%. */
.qm-d2 .qm-su-bar { height: 4px; border-radius: 999px; background: color-mix(in srgb, var(--ink-900, #1c1917) 8%, transparent); overflow: hidden; }
.qm-d2 .qm-su-bar i { display: block; height: 100%; border-radius: inherit; background: var(--d2-accent); }
.qm-d2 .qm-su-bar.is-done i { background: var(--ok); }
.qm-d2 .qm-su-nav { display: flex; flex-direction: column; margin-inline: -10px; }
.qm-d2 .qm-su-nav a { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; border-radius: 8px; text-decoration: none; font-size: var(--fs-400, 13px); font-weight: 600; color: var(--ink-700, #44403c); box-shadow: inset 2px 0 0 transparent; transition: background var(--d2-t); }
.qm-d2 .qm-su-nav a:hover { background: color-mix(in srgb, var(--ink-900, #1c1917) 4%, transparent); }
.qm-d2 .qm-su-nav a em { font-style: normal; font-size: 11px; font-weight: 700; color: var(--ink-400, #a8a29e); font-variant-numeric: tabular-nums; }
.qm-d2 .qm-su-nav a.is-current { color: var(--ink-900, #1c1917); font-weight: 700; box-shadow: inset 2px 0 0 var(--d2-accent); border-radius: 0 8px 8px 0; }
.qm-d2 .qm-su-nav a.is-done { color: var(--ink-400, #a8a29e); }
.qm-d2 .qm-su-cta { width: 100%; text-align: center; }

.qm-d2 .qm-su-main { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.qm-d2 .qm-su-group { padding: 0; overflow: hidden; scroll-margin-top: 192px; }
.qm-d2 .qm-su-group-head { display: flex; align-items: baseline; gap: 10px; padding: 15px 20px 13px; border-bottom: 1px solid var(--d2-border); }
.qm-d2 .qm-su-group-head h3 { font-size: var(--fs-600, 15px); font-weight: 800; margin: 0; letter-spacing: -.01em; }
.qm-d2 .qm-su-group-head p { font-size: 12px; color: var(--ink-500, #78716c); margin: 0; flex: 1 1 auto; min-width: 0; }
.qm-d2 .qm-su-count { flex: 0 0 auto; font-size: 12px; font-weight: 700; color: var(--ink-400, #a8a29e); font-variant-numeric: tabular-nums; }
.qm-d2 .qm-su-list { list-style: none; margin: 0; padding: 0; }
.qm-d2 .qm-su-list li + li { border-top: 1px solid color-mix(in srgb, var(--ink-900, #1c1917) 6%, transparent); }
.qm-d2 .qm-su-row { display: flex; align-items: center; gap: 14px; padding: 13px 20px; text-decoration: none; color: inherit; transition: background var(--d2-t); }
.qm-d2 .qm-su-row:hover { background: color-mix(in srgb, var(--ink-900, #1c1917) 3%, transparent); }
/* Status is a 16px stroke: hollow circle while open (accent when required),
   a bare thin tick once done — never a filled disc. */
.qm-d2 .qm-su-st { flex: 0 0 auto; width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid color-mix(in srgb, var(--ink-900, #1c1917) 24%, transparent); display: inline-flex; align-items: center; justify-content: center; font-size: 9.5px; color: var(--ink-400, #a8a29e); }
.qm-d2 .qm-su-row.is-req .qm-su-st { border-color: var(--d2-accent); }
.qm-d2 .qm-su-row.is-done .qm-su-st { border-color: transparent; }
.qm-d2 .qm-su-tx { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-d2 .qm-su-tx strong { font-size: var(--fs-400, 13px); font-weight: 700; color: var(--ink-900, #1c1917); }
.qm-d2 .qm-su-tx small { font-size: 12px; color: var(--ink-500, #78716c); line-height: 1.5; }
.qm-d2 .qm-su-row.is-done .qm-su-tx strong { color: var(--ink-400, #a8a29e); font-weight: 600; }
.qm-d2 .qm-su-tag { flex: 0 0 auto; font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-400, #a8a29e); }
.qm-d2 .qm-su-row.is-req .qm-su-tag { color: var(--d2-accent); }
.qm-d2 .qm-su-go { flex: 0 0 auto; font-size: 11px; color: var(--ink-300, #d6d3d1); transition: color var(--d2-t), transform var(--d2-t); }
.qm-d2 .qm-su-row:hover .qm-su-go { color: var(--ink-700, #44403c); transform: translateX(2px); }
.qm-d2 .qm-su-row.is-next { box-shadow: inset 3px 0 0 var(--d2-accent); background: color-mix(in srgb, var(--d2-accent) 3%, transparent); }
.qm-d2 .qm-su-sr { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@media (max-width: 991.98px) {
    .qm-d2 .qm-su { grid-template-columns: 1fr; }
    .qm-d2 .qm-su-side { position: static; }
}

/* ============================== TV SLIDESHOW (vendor + admin) ==============================
   The click-to-order board picker on the vendor slideshow form, the slideshow
   marks on the screens list, and the admin surface's include-in-slideshow tick. */
.qm-d2 .qm-showpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.qm-d2 .qm-showpick-card {
  position: relative; display: block; padding: 0; text-align: start; overflow: hidden; cursor: pointer;
  border: 2px solid var(--d2-border); border-radius: var(--r-sm); background: #fff;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.qm-d2 .qm-showpick-card:hover { border-color: var(--ink-400, #a8a29e); }
.qm-d2 .qm-showpick-card.is-on { border-color: var(--qm-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--qm-primary) 16%, transparent); }
.qm-d2 .qm-showpick-frame { display: block; aspect-ratio: 16 / 9; background: #101215; pointer-events: none; }
.qm-d2 .qm-showpick-frame iframe { display: block; width: 100%; height: 100%; border: 0; pointer-events: none; }
/* The play-order number a chosen board wears (1, 2, 3… in click order). */
.qm-d2 .qm-showpick-badge {
  position: absolute; top: 8px; inset-inline-start: 8px; min-width: 26px; height: 26px; padding: 0 8px;
  display: none; align-items: center; justify-content: center; border-radius: 999px;
  background: var(--qm-primary); color: #fff; font-weight: 800; font-size: 13px;
}
.qm-d2 .qm-showpick-card.is-on .qm-showpick-badge { display: inline-flex; }
.qm-d2 .qm-showpick-name { display: block; padding: 8px 10px; font-size: 12.5px; font-weight: 700; color: var(--ink-900, #1c1917); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Screens list: a slideshow card is marked by a count chip riding its thumbnail. */
.qm-d2 .qm-show-chip {
  position: absolute; z-index: 3; top: 8px; inset-inline-start: 8px; pointer-events: none;
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px;
  background: rgba(13, 15, 18, .78); color: #fff; font-size: 11.5px; font-weight: 700;
}
.qm-d2 .qm-show-chip i { font-size: 9px; color: var(--qm-primary); }
.qm-d2 .qm-show-blank { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #4b535c; font-size: 34px; background: #101215; }

/* "Put this screen on a TV" — the shared pairing panel (partials/tv-pair.php),
   identical on the board editor and the slideshow form. The QR sits in its own
   framed tile with the connect address readable under it; the guidance and the
   pair form hold the right column, flush with the card like every other panel. */
/* Centered vertically: the QR tile is the tallest piece, so the guidance column
   rides its middle — equal breathing room above and below, not a heavy foot. */
.qm-tvpair { display: flex; flex-wrap: wrap; align-items: center; gap: 28px; }
.qm-tvpair-qr {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 14px 14px 12px; background: #fff;
  border: 1px solid var(--d2-border); border-radius: var(--r-sm);
}
.qm-tvpair-qr #tv-qr { width: 128px; height: 128px; }
.qm-tvpair-qr #tv-qr img, .qm-tvpair-qr #tv-qr canvas { width: 100% !important; height: 100% !important; display: block; }
/* The address is data, not decoration: readable size, its own case, tap-able. */
.qm-tvpair-url {
  display: block; max-width: 172px; font-size: 11.5px; font-weight: 600; line-height: 1.35;
  color: var(--ink-600, #57534e); text-align: center; text-transform: lowercase;
  word-break: keep-all; overflow-wrap: break-word; text-decoration: none;
}
.qm-tvpair-url:hover { color: var(--qm-primary); }
.qm-tvpair-main { flex: 1 1 320px; min-width: 280px; }
.tv-device-chip {
  display: inline-flex; align-items: center; gap: 8px; margin: 0 8px 8px 0;
  padding: 5px 6px 5px 12px; font-size: 13.5px; background: #fff;
  border: 1px solid var(--d2-border); border-radius: 999px;
}
.tv-device-chip .dot { width: 8px; height: 8px; border-radius: 50%; background: #16a34a; }
.tv-device-chip .dot.off { background: #cbd5e1; }

/* ------------------------------------------------------------------ */
/* 38. ADD MEDIA (vendor/media/add) — the direct door onto the shelf.  */
/* The drop zone is the page's one control: dashed idle, brand-lit     */
/* while a drag hovers it. Finished files reuse the library grid.      */
/* ------------------------------------------------------------------ */
.qm-media-adddrop { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); padding: var(--sp-8) var(--sp-5); border: 1.5px dashed var(--line); border-radius: var(--r-md); text-align: center; color: var(--ink-600); transition: border-color var(--t-fast), background var(--t-fast); }
.qm-media-adddrop > i { font-size: 28px; color: var(--ink-400); }
.qm-media-adddrop.is-drag { border-color: var(--qm-primary); background: color-mix(in srgb, var(--qm-primary) 4%, transparent); }

/* ------------------------------------------------------------------ */
/* 39. DONUT STAT FOOT — the ring's legend as a band of divided cells: */
/* dot + name over the bold value, one cell per slice, refreshed by    */
/* the same range switcher that swaps the ring (dashboard-charts.js).  */
/* ------------------------------------------------------------------ */
/* Bleeds to the card's own edges (padding 20/20/8, see .qm-chart-card) so the
   band's border runs the full card width, exactly as the reference draws it;
   the cells' own padding lands their content back on the card's grid. */
/* Inside the breakdown row the grid's own bottom margin would poke the map
   column that extra distance below the charts — the ROW owns the section
   spacing there. */
.qm-d2 .qm-biz-row .qm-charts-grid { margin-bottom: 0; }
.qm-donut-foot { display: flex; flex-wrap: nowrap; overflow: hidden; margin: var(--sp-2) -20px -8px; padding-inline: 8px; border-top: 1px solid var(--line); }
/* A stat-foot donut's mount may sit under the grid's 264 floor: the renderer
   sheds the foot's 58px from numeric mounts so ring + foot together equal a
   264 bar card - the floor steps down by exactly that much when a foot follows. */
.qm-d2 .qm-chart-card .qm-chart-figure:has(+ .qm-donut-foot) .qm-chart { min-height: 186px; }
/* Natural width, no shrinking: a cell either fits whole (then grows into the
   spare space with its peers) or is dropped by the trim — never squeezed into
   a truncated label. */
.qm-donut-cell { flex: 1 0 auto; min-width: 0; padding: 12px 12px 10px; display: flex; flex-direction: column; gap: 3px; }
.qm-donut-cell + .qm-donut-cell { border-inline-start: 1px solid var(--line); }
/* ONE row, never a second: syncDonutFoot() (dashboard-charts.js) removes the
   trailing cells that would not fit whole — the ring and its tooltip still
   carry every slice; the foot states what its width can state honestly. */
.qm-donut-top { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-500, #78716c); }
.qm-donut-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.qm-donut-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-donut-val { font-size: var(--fs-800); font-weight: 700; color: var(--ink-900, #1c1917); font-variant-numeric: tabular-nums; }

/* ===========================================================================
   40. Restaurant cards (admin -> Restaurants)
   The list is a card grid rather than a table: the cover photograph and the
   venue's own mark identify a restaurant far faster than a name in a cell.
   The card body is a stretched link to the admin detail page; the footer's
   controls sit above it so they stay clickable.
   =========================================================================== */
/* Scoped .qm-rc*; moves to app.css if approved. */
.qm-rc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(276px,1fr));gap:18px;margin-top:4px}
.qm-rc{position:relative;background:var(--surface,#fff);border:1px solid var(--line,#f1dbe2);border-radius:14px;
  overflow:hidden;box-shadow:0 2px 10px rgba(45,10,20,.06);display:flex;flex-direction:column;
  transition:transform .16s ease,box-shadow .16s ease}

/* The whole card opens the restaurant. A stretched overlay rather than wrapping the
   markup in an <a>: the footer holds forms and buttons, and a form inside an anchor is
   invalid HTML that browsers will not submit. The overlay sits under the controls, so
   the card is clickable everywhere the controls are not. */
.qm-rc-link{position:absolute;inset:0;z-index:1;border-radius:13px}
.qm-rc-link:focus-visible{outline:3px solid var(--qm-primary,#ff003d);outline-offset:-3px}
.qm-rc-acts,.qm-rc-view{position:relative;z-index:2}
.qm-rc:hover{transform:translateY(-3px);box-shadow:0 14px 32px rgba(45,10,20,.13)}

.qm-rc-cover{position:relative;height:132px;background:#f3eef0}
.qm-rc-cover>img{width:100%;height:100%;object-fit:cover;display:block}
.qm-rc-cover::after{content:'';position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(20,8,12,.34) 0%,rgba(20,8,12,0) 46%,rgba(20,8,12,.30) 100%)}
.qm-rc-tl{position:absolute;top:10px;left:10px;z-index:2}
.qm-rc-tr{position:absolute;top:10px;right:10px;z-index:2;display:flex;align-items:center;gap:6px}
.qm-rc-flag{width:28px;height:20px;border-radius:4px;object-fit:cover;display:block;
  box-shadow:0 1px 5px rgba(0,0,0,.35);border:1px solid rgba(255,255,255,.65)}
.qm-rc-fav{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;
  background:rgba(255,255,255,.94);color:var(--star);font-size:12px;box-shadow:0 2px 7px rgba(0,0,0,.22)}

/* The venue's own mark straddles the cover edge. */
.qm-rc-mark{position:relative;z-index:3;width:68px;height:68px;margin:-34px auto 0;border-radius:50%;
  border:4px solid #fff;box-shadow:0 5px 16px rgba(45,10,20,.20);overflow:hidden;background:#fff}
.qm-rc-mark .qm-brandmark,.qm-rc-mark img{width:100%!important;height:100%!important;
  border-radius:50%;object-fit:cover;display:flex;font-size:22px}
/* flex, not block: a restaurant without a logo gets a monogram here, and its initials are
   centred by the brandmark's own flex rules — block dropped them into the top-left corner. */

.qm-rc-body{padding:12px 16px 0;text-align:center}
.qm-rc-name{font-size:16px;font-weight:800;color:var(--ink-900,#1f0b12);line-height:1.25;margin:0}
.qm-rc-sub{font-size:12px;color:var(--ink-500,#754151);margin:3px 0 0;line-height:1.35}

/* The three cells hand their value and label straight to this grid (display:contents),
   so all three values share one row and all three labels share the next. They line up
   because the grid says so - no reserved min-height, which is what was padding dead
   space under short values like "4.5". */
.qm-rc-stats{display:grid;grid-template-columns:repeat(3,1fr);margin:14px 0 0;
  border-top:1px solid var(--line,#f1dbe2);border-bottom:1px solid var(--line,#f1dbe2)}
.qm-rc-stat{display:contents}
.qm-rc-stat b{display:flex;align-items:flex-end;justify-content:center;text-align:center;
  padding:9px 6px 0;font-size:12.5px;font-weight:800;color:var(--ink-900,#1f0b12);
  line-height:1.3;overflow-wrap:anywhere}
.qm-rc-stat span{display:block;text-align:center;padding:3px 6px 9px;font-size:10px;
  text-transform:uppercase;letter-spacing:.5px;color:var(--ink-400,#8a7278);font-weight:700;white-space:nowrap}
/* display:contents hands six items to the grid in DOM order - value, label, value,
   label ... - which auto-placement wraps across rows. Each one is therefore placed
   explicitly: values on row 1, labels on row 2, one column per stat. */
.qm-rc-stat:nth-child(1) b{grid-column:1;grid-row:1}
.qm-rc-stat:nth-child(1) span{grid-column:1;grid-row:2}
.qm-rc-stat:nth-child(2) b{grid-column:2;grid-row:1}
.qm-rc-stat:nth-child(2) span{grid-column:2;grid-row:2}
.qm-rc-stat:nth-child(3) b{grid-column:3;grid-row:1}
.qm-rc-stat:nth-child(3) span{grid-column:3;grid-row:2}
/* the divider has to run down both rows now that the cell box is gone */
.qm-rc-stat:nth-child(2) b,.qm-rc-stat:nth-child(2) span,
.qm-rc-stat:nth-child(3) b,.qm-rc-stat:nth-child(3) span{border-left:1px solid var(--line,#f1dbe2)}

/* Owner: face beside the name, the way every other identity cell in the product reads. */
.qm-rc-owner{display:flex;align-items:center;gap:10px;padding:12px 16px;min-width:0}
.qm-rc-owner .qm-brandmark{width:30px;height:30px;border-radius:50%;flex:0 0 auto;font-size:11px}
.qm-rc-owner-t{min-width:0}
.qm-rc-owner-t strong{display:block;font-size:12.5px;font-weight:700;color:var(--ink-800,#34151f);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.qm-rc-owner-t small{display:block;font-size:11px;color:var(--ink-400,#8a7278);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* The dashboard's top-restaurant card is the one instance with no footer, and it is
   stretched by its grid row to match the charts beside it. Nothing carried margin-top:auto
   in its absence, so the leftover height fell past the owner row and left it floating
   above a strip of dead space. The owner row takes that height instead and centres in it,
   which is what it already does inside its own box - so the face and the two lines sit
   properly seated whatever height the row beside them happens to be. */
.qm-rc--top .qm-rc-owner { flex: 1 1 auto; }

/* Footer: secondary icons left, one labelled primary right - the dashboard CTA idiom. */
.qm-rc-foot{margin-top:auto;display:flex;align-items:stretch;justify-content:space-between;gap:12px;
  padding:0 0 0 10px;border-top:1px solid var(--line,#f1dbe2);background:var(--subtle,#fff5f8)}
.qm-rc-acts{display:flex;align-items:center;gap:7px;padding:8px 0}
.qm-rc-acts form{display:inline-flex;margin:0}

/* Card action buttons. Deliberately NOT .qm-icon-btn - that circular treatment is the
   table row-action idiom, and reusing it here would say "this is a table row". Same
   glyphs, softer square-cornered chip so the card reads as its own surface. */
.qm-rc-btn{width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line,#f1dbe2);border-radius:9px;background:#fff;color:var(--ink-500,#754151);
  font-size:13px;line-height:1;padding:0;cursor:pointer;text-decoration:none;
  transition:background .15s,border-color .15s,color .15s}
.qm-rc-btn:hover{background:var(--qm-primary-50,#fff0f3);border-color:var(--qm-primary-100,#ffd6df);color:var(--qm-primary,#ff003d)}
.qm-rc-btn.is-danger:hover{background:#fdecea;border-color:#f3d3ce;color:#c0392b}
/* A featured venue's star is the review-star gold (--star) on the card and on the detail page's header button alike. */
.qm-rc-btn.is-on,.qm-btn-outline.is-on{background:#fff6e0;border-color:#f0dcae;color:var(--star)}
.qm-rc-view{display:inline-flex;align-items:center;gap:8px;padding:0 18px;flex:0 0 auto;
  font-size:12.5px;font-weight:700;text-decoration:none;background:var(--qm-primary,#ff003d);color:#fff;
  border-radius:0 0 13px 0;transition:background .15s}
.qm-rc-view:hover{background:var(--qm-primary-600,#cc0031);color:#fff;text-decoration:none}
.qm-rc-view i{font-size:10px}

/* Restaurant detail header — the wide relative of the restaurant card, so the page
   a card opens onto carries the same identity: cover photograph, then the venue's
   mark straddling its lower edge. The flag rides the cover instead of the pill row,
   where it was competing with the status badges. */
.qm-rdh-cover { position: relative; height: 150px; background: #f3eef0; }
/* :not() keeps this off the flag, which is also a direct child - without it the
   cover rule (0,1,1) outranks .qm-rdh-flag (0,1,0) and the flag fills the banner. */
.qm-rdh-cover > img:not(.qm-rdh-flag) { width: 100%; height: 100%; object-fit: cover; display: block; }
.qm-rdh-cover::after { content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(20,8,12,.30) 0%, rgba(20,8,12,0) 45%, rgba(20,8,12,.26) 100%); }
.qm-rdh-cover > img.qm-rdh-flag { position: absolute; top: 12px; left: 14px; z-index: 2; width: 30px; height: 21px;
  border-radius: 4px; object-fit: cover; border: 1px solid rgba(255,255,255,.65);
  box-shadow: 0 1px 6px rgba(0,0,0,.35); }
.qm-rdh .qm-tbl-id { align-items: flex-end; }
.qm-rdh-mark { position: relative; z-index: 2; display: block; margin-top: -46px; flex: 0 0 auto;
  width: 92px; height: 92px; border-radius: 50%; border: 4px solid #fff; overflow: hidden;
  background: #fff; box-shadow: 0 6px 18px rgba(45,10,20,.22); }
.qm-rdh-mark .qm-brandmark, .qm-rdh-mark img { width: 100% !important; height: 100% !important;
  border-radius: 50%; object-fit: cover; display: block; font-size: 26px; }
/* When there is no avatar or logo the brandmark is a <span> of initials, not an image.
   display:block strands those initials against the top of a 92px circle, because the
   brandmark's line-height is sized for a small tile - so a text mark keeps the centring
   it brings with it. Applies to every page on this header: restaurant, user, reservation. */
.qm-rdh-mark span.qm-brandmark { display: flex; align-items: center; justify-content: center; line-height: 1; }
.qm-rdh-cuisine { color: var(--ink-500); font-size: var(--fs-400); }

/* The facts below the header wear the restaurant card's rail: cells divided by hairlines
   and seated against a top border, instead of floating in grid gaps. Scoped to .qm-rdh
   because .qm-billing-meta is shared with the vendor plan card, which keeps its own look. */
.qm-rdh .qm-billing-meta { gap: 0; padding: 0; border-top: 1px solid var(--line); }
.qm-rdh .qm-billing-meta > div { padding: 13px 16px; border-left: 1px solid var(--line); }
.qm-rdh .qm-billing-meta > div:first-child { border-left: 0; }
.qm-rdh .qm-billing-meta dt { margin-bottom: 4px; }

/* Standing sits on the cover, right-aligned, the way the card badges its status. */
.qm-rdh-badges { position: absolute; top: 12px; right: 14px; z-index: 2;
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
/* A page-width band needs a tighter name row than a 273px tile: the mark is bigger,
   the meta is one line, and the actions sit on the same baseline as the title. */
.qm-rdh .qm-billing-plan-name { padding: 10px 16px 14px; }
.qm-rdh .qm-billing-plan-name h2 { margin-bottom: 2px; }
.qm-rdh-cuisine { color: var(--ink-500); font-size: 13px; }

/* Five facts, grouped rather than queued. An explicit 5-column track stops the
   auto-fit grid from wrapping mid-row, and the owner - the only cell carrying a face
   and two lines - gets the room its content actually needs. The heavier rule after
   Owner + Contact separates WHO this restaurant is from WHAT it costs, so the eye
   lands on two groups instead of six equal columns. */
.qm-rdh .qm-billing-meta { grid-template-columns: 1.45fr 1.1fr .95fr 1.2fr .95fr auto; }
.qm-rdh .qm-billing-meta > .qm-rdh-contact { border-right: 2px solid var(--line); }
.qm-rdh .qm-billing-meta > .qm-rdh-contact + div { border-left: 0; }
@media (max-width: 1100px) {
  .qm-rdh .qm-billing-meta { grid-template-columns: repeat(2, 1fr); }
  .qm-rdh .qm-billing-meta > div { border-left: 0; border-top: 1px solid var(--line); }
  .qm-rdh .qm-billing-meta > .qm-rdh-contact { border-right: 0; }
  .qm-rdh .qm-billing-meta > div:nth-child(2n) { border-left: 1px solid var(--line); }
}

/* The rail closes on an action, not another fact: the cell IS the button, filling its
   corner exactly as .qm-rc-view closes a restaurant card and .qm-biz-cta closes the
   dashboard's Payouts and Subscriptions panels. */
/* .qm-billing-meta sets align-items:start, so every cell shrinks to its content -
   which left this one 19px tall and nothing for the button to fill. It opts out. */
.qm-rdh .qm-billing-meta > .qm-rdh-ctacell { padding: 0; display: flex; align-self: stretch; }
/* The form sits between the cell and the button, so it has to be a flex parent too -
   otherwise align-self:stretch on the button resolves against a block and does nothing,
   leaving the button floating at the top of the cell. */
.qm-rdh-ctacell form { display: flex; flex: 1 1 auto; }
.qm-rdh-cta { display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  flex: 1 1 auto; padding: 0 22px; border: 0;
  font-size: 12.5px; font-weight: 700; text-decoration: none; cursor: pointer;
  background: var(--qm-primary); color: #fff; align-self: stretch;
  border-radius: 0 0 calc(var(--r-lg) - 1px) 0; transition: background var(--t-fast); }
.qm-rdh-cta:hover { background: var(--qm-primary-600); color: #fff; }
.qm-rdh-cta i { font-size: 12px; opacity: .95; }
@media (max-width: 1100px) {
  .qm-rdh .qm-billing-meta > .qm-rdh-ctacell { grid-column: 1 / -1; }
  .qm-rdh-cta { width: 100%; justify-content: center; padding: 14px 20px; border-radius: 0; }
}

/* The user header borrows the restaurant header's rail but carries four facts, not
   five, so it sets its own track list instead of inheriting a six-column one.
   The per-page track list has to restate the narrow-screen collapse: it matches the
   shared .qm-rdh rule on specificity and sits after it in the file, so without its own
   media block it would win inside the media query too and hold five tracks on a phone. */
.qm-udh .qm-billing-meta { grid-template-columns: 1.5fr 1fr 1fr 1fr auto; }
@media (max-width: 1100px) { .qm-udh .qm-billing-meta { grid-template-columns: repeat(2, 1fr); } }

/* The reservation header carries seven facts - the booking (when, party, table, deposit),
   then past the heavier rule the guest (phone, email, account). Wider tracks go to the
   two that hold real sentences, the timestamp and the email; party and table are a
   number and a short label and need almost nothing. */
.qm-resh .qm-billing-meta { grid-template-columns: 1.35fr .6fr .75fr .8fr 1fr 1.5fr 1.05fr auto; }
/* The venue's mark rides its name on the sub-line, small enough to stay subordinate to
   the guest's mark - the booking is filed under the guest, at that restaurant. */
.qm-resh .qm-rdh-cuisine { display: flex; align-items: center; gap: 7px; }
.qm-resh .qm-rdh-cuisine .qm-brandmark { width: 22px; height: 22px; flex: 0 0 auto; font-size: 9px; }

/* The campaign header: the buy (placement, period, price, payment) then - past the
   heavier rule - the subject. Same rail as the other detail bands, its own tracks. */
.qm-cmph .qm-billing-meta { grid-template-columns: 1fr 1.45fr .75fr 1.1fr .9fr auto; }
@media (max-width: 1100px) { .qm-cmph .qm-billing-meta { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 1100px) { .qm-resh .qm-billing-meta { grid-template-columns: repeat(2, 1fr); } }

/* The rail's closing action is a link here, not a form-wrapped button: nothing is being
   submitted, the operator is being handed through to the venue. */
.qm-rdh-ctacell > a.qm-rdh-cta { align-self: stretch; }

/* Email detail: a two-column key/value table stretched to the card's full width left
   its values marooned in the middle. The value column hugs the right edge instead, so
   each fact reads as one line. */
.qm-email-facts th { width: 40%; }
.qm-email-facts td { text-align: right; color: var(--ink-800); }


/* ==========================================================================
   STUDIO GRID - shared preview-card grid (was inline in partials/studio-grid.php)
   ========================================================================== */
  .qm-studio-grid { display: grid; gap: var(--sp-5); }
  /* Fixed-column frame grids — the same widths the TV Screens + Printable-menu managers had. */
  .qm-studio-grid.cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .qm-studio-grid.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .qm-studio-grid.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  @media (max-width: 1200px) { .qm-studio-grid.cols-4 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
  @media (max-width: 900px)  { .qm-studio-grid.cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (max-width: 560px)  { .qm-studio-grid.cols-4 { grid-template-columns: 1fr; } }
  @media (max-width: 1100px) { .qm-studio-grid.cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
  @media (max-width: 640px)  { .qm-studio-grid.cols-3 { grid-template-columns: 1fr; } }
  @media (max-width: 760px)  { .qm-studio-grid.cols-2 { grid-template-columns: 1fr; } }
  /* Object grid (QR cards) keeps the natural auto-fill flow it always had. */
  .qm-studio-grid.is-object { grid-template-columns: repeat(auto-fill, minmax(var(--tile-min, 210px), 1fr)); align-items: start; }
  .qm-studio-card { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; transition: box-shadow var(--t-fast, .15s) ease, transform var(--t-fast, .15s) ease, border-color var(--t-fast, .15s) ease; }
  .qm-studio-grid.is-flush .qm-studio-card { border-radius: 0; }
  .qm-studio-card:hover { box-shadow: var(--e-2); border-color: color-mix(in srgb, var(--qm-primary, #e11d64) 26%, var(--line)); }
  /* Frame preview — a fixed-ratio window onto an iframe/preview. */
  .qm-studio-frame { position: relative; aspect-ratio: var(--frame-ratio, 16 / 9); background: var(--frame-bg, var(--bg-soft, #f6f7f8)); overflow: hidden; }
  .qm-studio-frame.is-dark { --frame-bg: #000; }
  .qm-studio-frame.is-paper { --frame-bg: #eceae5; }
  /* Snapshot tile: the preview sits INSIDE the card as its own rounded tile rather than running to the
     card edge. Used where the previewed artefact is square-cornered by nature — a printable menu is a
     sheet of paper — so the rounding belongs to the card UI, never to the thing being previewed. */
  .qm-studio-grid.is-snap .qm-studio-frame { margin: var(--sp-3) var(--sp-3) 0; border-radius: var(--r-md, 10px); }
  /* 1px overscan, like a TV: the framed document sizes itself to the iframe viewport, and
     fractional device pixels leave a hairline of the frame matte visible along the bottom or
     right edge (a dark line on light previews). Bleeding the iframe 1px past the frame on
     every side crops nothing visible and removes the seam at any zoom or DPI. */
  .qm-studio-frame > iframe { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); border: 0; pointer-events: none; }
  .qm-studio-frame > .qm-studio-overlay { position: absolute; inset: 0; z-index: 2; }
  /* Double-sided flip: a card whose media is `.pm-flip` shows the FRONT and flips (3D) to the BACK on hover
     — the two-sided design reads clearly without cramming both sides onto one thumbnail. Dormant unless a
     card actually contains `.pm-flip`. */
  .qm-studio-frame > .pm-flip { position: absolute; inset: 0; perspective: 1600px; }
  .pm-flip-inner { position: relative; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform .7s cubic-bezier(.2, .75, .25, 1); }
  .qm-studio-card:hover .pm-flip-inner, .qm-studio-card:focus-within .pm-flip-inner { transform: rotateY(180deg); }
  .pm-flip-face { position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
  .pm-flip-back { transform: rotateY(180deg); }
  .pm-flip-face > iframe { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); border: 0; pointer-events: none; }
  .pm-flip-badge { position: absolute; z-index: 3; inset-block-end: 8px; inset-inline-end: 8px; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 999px; font-size: 11px; color: #fff; background: rgba(17, 24, 39, .72); pointer-events: none; transition: opacity .2s ease; }
  .qm-studio-card:hover .pm-flip-badge, .qm-studio-card:focus-within .pm-flip-badge { opacity: 0; }
  @media (prefers-reduced-motion: reduce) { .pm-flip-inner { transition: none; } }
  /* Object preview — a centred natural-size object (QR flip card). */
  .qm-studio-object { padding: var(--sp-3); display: flex; justify-content: center; }
  .qm-studio-object > * { width: 100%; }
  /* Meta + actions rhythm — one fixed padding scale across all surfaces. */
  .qm-studio-meta { padding: var(--sp-3) var(--sp-4) 0; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); min-width: 0; }
  .qm-studio-meta:only-child, .qm-studio-object + .qm-studio-meta { padding-block: var(--sp-3); }
  .qm-studio-name { font-weight: 700; color: var(--ink-800, #1f2430); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .qm-studio-card.is-object .qm-studio-name { text-align: center; width: 100%; }
  .qm-studio-status { font-size: 12.5px; white-space: nowrap; color: var(--ink-400, #6b7280); }
  .qm-studio-status .live { color: #16a34a; font-weight: 600; }
  .qm-studio-status .live::before { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: #16a34a; margin-inline-end: 6px; vertical-align: middle; }
  .qm-studio-actions { padding: var(--sp-2) var(--sp-4) var(--sp-4); display: flex; flex-wrap: wrap; gap: var(--sp-2); justify-content: flex-end; }
  .qm-studio-actions.is-center { justify-content: center; }
  .qm-studio-actions form { margin: 0; }
  .pm-flip-front { transform: rotateY(0deg); }


/* ==========================================================================
   INLINE-STYLE REPLACEMENTS
   Five presentational declarations that were written as style="" attributes in
   templates. Nothing here is new design - each rule is the exact value it
   replaced, moved so the markup carries classes and the stylesheet carries CSS.
   ========================================================================== */

/* The crop action sits beside the delete tile rather than under it. */
.qm-media-tile-crop { inset-inline-end: 38px; }

/* Reorder-handle column on the settings tables, which needs a real width rather
   than the shrink-to-fit the shared .qm-drag-col gives every other table. */
.qm-drag-col.is-wide { width: 52px; }

/* "Start blank" tile: the plus is the whole illustration, so it is sized as art. */
.qm-tpl-blank .fa-plus { font-size: 24px; }

/* A switch row that only REPORTS state - nothing to click, so no pointer. */
.qm-switch-row.is-static { cursor: default; }


/* ==========================================================================
   TEMPLATE GALLERY - the "pick a starting design" picker
   Was a <style> block in partials/template-gallery.php. The generic .pm-flip
   rules it repeated are not copied: they live in the STUDIO GRID section above
   and apply here too. Only the two card-scoped flip selectors are kept.
   The one value the template computes - the preview's aspect ratio - arrives as
   the --tpl-aspect custom property, the same way --tpl-cols and --tile-min do.
   ========================================================================== */
.qm-tpl-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.qm-tpl-toolbar label { font-weight: 600; color: var(--ink-600); margin: 0; }
.qm-tpl-toolbar select { max-width: 240px; }
/* Column count is the caller's (--tpl-cols): three by default, four where the cards are small
   enough to warrant it. The step-downs are caps, so a 3-column grid is unaffected by them. */
.qm-tpl-grid { display: grid; gap: 18px; grid-template-columns: repeat(var(--tpl-cols, 3), minmax(0, 1fr)); }
/* Cards STRETCH to the row by default, which is what makes the "Start blank" tile stand as tall as
   the starters beside it. Only a mixed-aspect grid opts out: there a short landscape card stretched
   to a tall portrait neighbour would carry the white band underneath it instead. */
.qm-tpl-grid.is-mixed { align-items: start; }
@media (max-width: 1280px) { .qm-tpl-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px) { .qm-tpl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .qm-tpl-grid { grid-template-columns: 1fr; } }
.qm-tpl-card { border: 1px solid var(--qm-border, rgba(0,0,0,.1)); border-radius: var(--r-md, 10px); overflow: hidden; background: var(--qm-surface, #fff); display: flex; flex-direction: column; }
.qm-tpl-card[hidden] { display: none; }
.qm-tpl-preview { position: relative; aspect-ratio: var(--tpl-aspect, 4 / 3); background: #eceae5; }
.qm-tpl-preview iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; pointer-events: none; }
.qm-tpl-meta { padding: 10px 12px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.qm-tpl-name { font-weight: 700; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qm-tpl-meta form { margin: 0; }
.qm-tpl-blank { display: flex; align-items: center; justify-content: center; min-height: 190px; border-style: dashed; }
.qm-tpl-blank a { text-align: center; color: var(--ink-600); text-decoration: none; font-weight: 600; }
.qm-tpl-blank a:hover { color: var(--qm-primary); }
/* Double-sided flip (print menu): the card shows the FRONT and flips to the BACK on hover. */
.qm-tpl-preview .pm-flip { position: absolute; inset: 0; perspective: 1600px; }
.qm-tpl-card:hover .pm-flip-inner, .qm-tpl-card:focus-within .pm-flip-inner { transform: rotateY(180deg); }

/* Live password checklist (partials/password-rules.php): neutral rings that fill with a
   tick as each rule is met. Sits where the old one-line hint sat, same voice, same size. */
.qm-pw-rules { list-style: none; margin: 8px 0 0; padding: 0; display: none; flex-direction: column; gap: 5px; font-size: 12.5px; line-height: 1.3; color: var(--ink-500); }
.qm-pw-rules.is-active { display: flex; }
.qm-pw-rules li { display: flex; align-items: center; gap: 8px; transition: color .15s ease; }
.qm-pw-rules li i { flex: 0 0 auto; width: 14px; font-size: 11px; color: currentColor; opacity: .55; transition: opacity .15s ease; }
.qm-pw-rules li.is-met { color: var(--ok); }
.qm-pw-rules li.is-met i { opacity: 1; }
.qm-pw-rules li[hidden] { display: none; }
.qm-pw-rules li.is-alert { color: var(--danger); }
.qm-pw-rules li.is-alert i { opacity: 1; }

/* Vendor sign-up: the plan strip carries name, price and cadence plus Change; the chooser it
   opens reuses the "Pay with" radio rows (one vocabulary on the page); each step after the
   first opens under a hairline so the form reads as sections, not one run of fields. */
.qm-vs-plan-price { color: var(--ink-600); font-weight: 600; }
.qm-vs-plan .qm-btn { white-space: nowrap; }
.qm-vs-chooser { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-4); margin: calc(-1 * var(--sp-2)) 0 var(--sp-5); background: var(--surface); }
.qm-vs-chooser[hidden] { display: none; }
.qm-vs-chooser-title { margin: 0 0 var(--sp-3); font-weight: 700; color: var(--ink-900); font-size: .95rem; text-align: center; }
.qm-vs-toggle { display: flex; width: max-content; margin: 0 auto var(--sp-4); }
.qm-vs-toggle[hidden] { display: none; }
/* Plans two-up: four tiers read as one block instead of a column the length of the form. */
.qm-vs-plans { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.qm-vs-plans .qm-pay-option { margin-bottom: 0; flex-wrap: nowrap; align-items: flex-start; }
/* In a half-width cell the label must shrink beside the radio, never drop under it. */
.qm-vs-plans .qm-pay-option > label { flex: 1 1 0; min-width: 0; }
.qm-vs-done { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
@media (max-width: 575.98px) { .qm-vs-plans { grid-template-columns: 1fr; } }
/* Outranks the card's own h2 margin reset, which was collapsing the gap to nothing. */
.qm-card h2.qm-vs-step, h2.qm-vs-step { margin-top: var(--sp-7); padding-top: var(--sp-6); border-top: 1px solid var(--line); }
/* Sign-up: four steps side by side - one form. Four columns from xl, two from md, one below; each column
   is its own card with its number, and the page never turns into tabs or pages. */
/* The steps start right under the title band: its own margin is the gap, not the section's top padding too. */
.qm-vs-section { padding-top: 0; }
.qm-vs-steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-5); align-items: start; }
@media (max-width: 1399.98px) { .qm-vs-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 767.98px) { .qm-vs-steps { grid-template-columns: 1fr; } }
.qm-vs-col { min-width: 0; }
.qm-vs-col .qm-form-grid { grid-template-columns: 1fr; gap: 0; }
.qm-vs-sec-body > .mb-3:last-child, .qm-vs-sec-body > .qm-form-grid:last-child > .mb-3:last-child, #vs-password-block > .mb-3:last-child { margin-bottom: 0; }
.qm-vs-head { display: flex; align-items: center; gap: 12px; margin: 0 0 var(--sp-5); padding-bottom: var(--sp-4); border-bottom: 1px solid var(--line); }
.qm-vs-head h2 { margin: 0; font-size: var(--fs-800); color: var(--ink-900); }
/* Under each title, one line on what the step is for. */
.qm-vs-head-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.qm-vs-sub { margin: 0; font-size: 12px; line-height: 1.4; color: var(--ink-500); }
.qm-vs-num { flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--qm-primary); color: #fff; font-weight: 800; font-size: var(--fs-600); }
.qm-vs-plans { grid-template-columns: 1fr; }
.qm-vs-save { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: var(--r-pill); background: var(--qm-primary-100); color: var(--qm-primary-600, #d10032); font-size: 11px; font-weight: 700; }
.qm-vs-or { display: flex; align-items: center; gap: 10px; margin: var(--sp-4) 0; color: var(--ink-400); font-size: var(--fs-400); }
.qm-vs-or::before, .qm-vs-or::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--line); }
.qm-vs-link { background: none; border: 0; padding: 0; color: var(--qm-primary); font-weight: 700; font-size: var(--fs-400); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.qm-vs-link:hover { color: var(--qm-primary-600, #d10032); }
.qm-vs-back { display: inline-flex; align-items: center; gap: 6px; margin-bottom: var(--sp-4); text-decoration: none; }
.qm-vs-back:hover { text-decoration: underline; }
.qm-vs-chip { display: flex; align-items: center; gap: 10px; padding: 10px 12px; margin-bottom: var(--sp-4); border: 1px solid var(--qm-primary-100); border-radius: var(--r-md); background: var(--qm-primary-50); color: var(--ink-800); font-size: var(--fs-400); }
.qm-vs-chip-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.qm-vs-chip .qm-btn { flex: 0 0 auto; }
.qm-vs-places { margin-bottom: var(--sp-3); position: relative; }
.qm-vs-results { margin-top: 6px; display: flex; flex-direction: column; gap: 6px; }
.qm-vs-place { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%; padding: 10px 12px; text-align: start; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer; }
.qm-vs-place:hover, .qm-vs-place:focus-visible { border-color: var(--qm-primary); background: var(--qm-primary-50); outline: none; }
.qm-vs-place strong { color: var(--ink-900); }
.qm-vs-place small { color: var(--ink-500); }
.qm-vs-none { margin: 6px 0 0; color: var(--ink-500); font-size: var(--fs-400); }
.qm-vs-gbp { margin-bottom: var(--sp-3); }
.qm-vs-listings .qm-pay-option { margin-bottom: 8px; }
.qm-vs-status { margin: 0 0 var(--sp-3); padding: 8px 12px; border-radius: var(--r-md); background: var(--ok-bg, #e7f6ec); color: var(--ok, #1e8e3e); font-size: var(--fs-400); font-weight: 600; }
.qm-vs-form .form-control.is-prefilled { background: var(--qm-primary-50); }
/* Step 1 price line - the pricing page's own: the struck monthly x 12 on Annual, the amount, "/mo" "/yr". */
.qm-vs-price { display: flex; align-items: baseline; flex-wrap: wrap; margin-top: 2px; }
.qm-vs-was { margin-inline-end: 8px; color: var(--ink-400); font-size: 12.5px; font-weight: 600; text-decoration: line-through; }
.qm-vs-was[hidden] { display: none; }
.qm-vs-amount { font-size: var(--fs-400); font-weight: 700; color: var(--ink-800); }
.qm-vs-cycle { margin-inline-start: 3px; font-size: 12.5px; color: var(--ink-400); }
/* The step check: grey ring until the step is complete, green disc then. */
.qm-vs-check { flex: 0 0 auto; margin-inline-start: auto; width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--line); display: inline-flex; align-items: center; justify-content: center; color: transparent; font-size: 12px; transition: background-color .15s, border-color .15s, color .15s; }
.qm-vs-check.is-done { background: var(--ok); border-color: var(--ok); color: #fff; }
.qm-vs-label { margin: 0 0 8px; font-size: var(--fs-400); font-weight: 600; color: var(--ink-800); }
/* Pressing the button is the agreement: the line under it, set small and quiet. */
.qm-vs-consent { margin: var(--sp-3) 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-500); text-align: center; }
.qm-vs-consent a { color: var(--qm-primary); font-weight: 600; }
/* Step 3 reads as three sections that open and close: Restaurant details, Opening hours, Menu. */
.qm-vs-sec { margin-top: var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.qm-vs-sec[hidden] { display: none; }
.qm-vs-sec-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; list-style: none; cursor: pointer; user-select: none; border-radius: var(--r-md); }
.qm-vs-sec-head::-webkit-details-marker { display: none; }
.qm-vs-sec-head::marker { content: ""; }
.qm-vs-sec-head:focus-visible { outline: 2px solid var(--qm-primary); outline-offset: -2px; }
.qm-vs-sec-title { font-size: var(--fs-400); font-weight: 700; color: var(--ink-900); white-space: nowrap; }
.qm-vs-sec-sum { margin-inline-start: auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: end; font-size: 12px; color: var(--ink-500); }
.qm-vs-sec-chev { flex: 0 0 auto; font-size: 11px; color: var(--ink-400); transition: transform .15s; }
.qm-vs-sec[open] > .qm-vs-sec-head { border-bottom: 1px solid var(--line-2); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.qm-vs-sec[open] > .qm-vs-sec-head .qm-vs-sec-chev { transform: rotate(180deg); }
.qm-vs-sec-body { padding: var(--sp-4) 14px 14px; }
/* Opening hours: one roomy row per day - the day, open-close, a switch; a closed day shows "Closed". */
.qm-vs-hours.qm-vs-sec-body { padding-top: 4px; padding-bottom: 6px; }
.qm-vs-day { display: grid; grid-template-columns: 40px minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line-2); }
.qm-vs-day:last-child { border-bottom: 0; }
.qm-vs-day-name { font-size: var(--fs-400); font-weight: 600; color: var(--ink-700); }
.qm-vs-day-times { display: flex; align-items: center; gap: 6px; min-width: 0; }
.qm-vs-day-times .form-control { flex: 1 1 0; min-width: 0; padding-left: 6px; padding-right: 6px; text-align: center; font-variant-numeric: tabular-nums; }
.qm-vs-day-dash { flex: 0 0 auto; color: var(--ink-400); }
.qm-vs-day-closed { display: none; font-size: var(--fs-400); color: var(--ink-400); }
.qm-vs-day.is-closed .qm-vs-day-times .form-control, .qm-vs-day.is-closed .qm-vs-day-dash { display: none; }
.qm-vs-day.is-closed .qm-vs-day-closed { display: inline; }
.qm-vs-day.is-closed .qm-vs-day-name { color: var(--ink-400); }
/* The day switch: the dashboard's pill toggle, one size down (the storefront keeps native checkboxes elsewhere). */
.qm-vs-day-switch { appearance: none; -webkit-appearance: none; flex: 0 0 auto; width: 40px; height: 22px; min-width: 40px; margin: 0; padding: 0; border: 0; border-radius: var(--r-pill); cursor: pointer; position: relative; background-color: #dfe3e8; box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08); transition: background-color var(--t-fast); }
.qm-vs-day-switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 2px 6px rgba(31, 11, 18, .28); transition: transform var(--t-fast); }
.qm-vs-day-switch:checked { background-color: var(--qm-primary); background-image: var(--qm-grad); }
.qm-vs-day-switch:checked::after { transform: translateX(18px); }
.qm-vs-day-switch:focus-visible { outline: 0; box-shadow: inset 0 1px 3px rgba(31, 11, 18, .08), 0 0 0 4px color-mix(in srgb, var(--qm-primary) 15%, transparent); }
/* Menu: the listing's dishes, section by section, a tick and a price each - set like the rest of the form. */
.qm-vs-menu-tools { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--sp-3); }
.qm-vs-menu-tools[hidden] { display: none; }
.qm-vs-menu-hint { font-size: 12px; line-height: 1.45; color: var(--ink-500); }
.qm-vs-menu-links { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink-400); }
.qm-vs-menu-links .qm-vs-link { font-size: 12px; font-weight: 600; text-decoration: none; }
.qm-vs-menu-links .qm-vs-link:hover { text-decoration: underline; }
.qm-vs-menu-list { max-height: 340px; overflow: auto; }
.qm-vs-menu-sec { margin: var(--sp-3) 0 2px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-400); }
.qm-vs-menu-sec:first-child { margin-top: 0; }
.qm-vs-dish { display: grid; grid-template-columns: auto minmax(0, 1fr) 78px; align-items: center; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--line-2); }
.qm-vs-dish:last-child { border-bottom: 0; }
.qm-vs-dish .form-check-input { margin: 0; float: none; }
.qm-vs-dish-name { margin: 0; font-size: var(--fs-400); font-weight: 500; color: var(--ink-800); overflow-wrap: anywhere; cursor: pointer; }
.qm-vs-dish-price { height: 34px; padding: 4px 8px; text-align: end; font-size: var(--fs-400); font-variant-numeric: tabular-nums; }
.qm-vs-dish-price.is-missing { border-color: var(--qm-primary); }
#vs-pay-block[hidden] { display: none; }
