/* ===== Restaurant detail (scoped, design-system tokens) =====
   Per-page stylesheet (enqueued via $pageStyles in MenuController) so the view
   carries no inline \3c style>. 100% local. Colours use app.css tokens. */
.qm-rd-hero { position: relative; background-size: cover; background-position: center; padding: var(--sp-8) 0 var(--sp-7); color: #fff; }
/* No cover uploaded: a clean branded banner in the platform theme colour (set by the admin), never a stock photo. */
.qm-rd-hero--brand { background: linear-gradient(-135deg, var(--qm-primary, #ff003d), color-mix(in srgb, var(--qm-primary, #ff003d) 60%, #17060d)); }
@media (max-width:640px){ .qm-rd-hero { padding: var(--sp-7) 0 var(--sp-6, 28px) } }
/* Reservation page only: the cover sits flush under the page-head band (its 36px
   margin-bottom cancelled here), and the form card overlaps the cover's lower edge -
   a card-over-cover-photo composition. The hero gets the exact extra bottom padding
   the card's negative margin reclaims, so the overlap eats only newly added image
   space, never the logo/name/pills row above it. .qm-resv-form-card is unique to
   this page's own stylesheet load, so the rule can't leak onto the vendor dashboard's
   reservation form (same class, but restaurant-detail.css isn't loaded there). */
.qm-page-head + .qm-rd-hero { margin-top: calc(var(--sp-7) * -1); }
/* The overlap itself is desktop-only (>=992px, Bootstrap's own lg breakpoint): below
   that, .col-lg-4/.col-lg-8 stack into one column, so the sidebar's first card sits
   directly under the form card - pulling either one up would overlap THAT sibling
   instead of the hero. Mobile keeps the plain, non-overlapping flush layout. */
@media (min-width: 992px) {
    .qm-rd-hero--overlap { padding-bottom: calc(var(--sp-7) + var(--sp-10, 84px)); }
    .qm-resv-form-card { margin-top: calc(var(--sp-10, 84px) * -1); }
    /* The sidebar's own first card overlaps by the same amount, so both columns start
       on one clean, aligned line instead of the form floating up while the sidebar
       sits flush below - matched via this page's own aside.col-lg-4 (restaurant-detail.php's
       own sidebar is aside.col-lg-3, so this can't leak onto that page). position:relative
       is required here: .qm-rd-hero is itself `position:relative`, and a positioned
       element always paints above plain static content regardless of DOM order - without
       this, the card's overlapping top portion renders HIDDEN behind the hero instead of
       over it (.qm-resv-form-card never had this bug because it's already position:relative
       for its own ::before accent bar). */
    aside.col-lg-4 > .qm-rd-info:first-child { position: relative; margin-top: calc(var(--sp-10, 84px) * -1); }
}
/* Content hierarchy: the page-head's "Reserve a table" is the true H1 here (the
   restaurant is context, not the subject), so the hero name must read clearly
   smaller than .qm-page-title (clamp 26-38px) - restaurant-detail.php's own hero,
   where the name IS the subject, is untouched. Compound selector (not just the
   modifier alone) so it outranks .qm-rd-hero-name's own rule regardless of
   source order in this file. */
.qm-rd-hero-name.qm-rd-hero-name--sub { font-size: clamp(18px, 2.2vw, 24px); }
.qm-rd-crumb { font-size: .82rem; color: #fff; margin-bottom: var(--sp-5, 20px); text-shadow: 0 2px 8px rgba(0,0,0,.45); }
.qm-rd-crumb a { color: #fff; text-decoration: none; }
.qm-rd-crumb a:hover { color: #fff; text-decoration: underline; }
.qm-rd-crumb span[aria-hidden] { margin: 0 .5rem; opacity: .6; }
.qm-rd-crumb span[aria-current] { color: #fff; font-weight: 600; }
.qm-rd-hero-card { display: flex; align-items: center; gap: var(--sp-5, 20px); flex-wrap: wrap; }
.qm-rd-hero-logo { border: 3px solid #fff; box-shadow: var(--e-2); background: #fff; flex: 0 0 auto; }
.qm-rd-hero-main { flex: 1 1 320px; min-width: 0; }
.qm-rd-hero-name { font-size: clamp(1.7rem, 3.4vw, 2.5rem); font-weight: 800; margin: 0 0 .25rem; line-height: 1.1; color: #fff; text-shadow: 0 2px 18px rgba(0,0,0,.55); }
.qm-rd-hero-tag { font-size: 1rem; color: rgba(255,255,255,.92); margin: 0 0 var(--sp-3, 12px); max-width: 60ch; }
.qm-rd-hero-meta { display: flex; flex-wrap: wrap; gap: var(--sp-2, 8px); }
.qm-rd-pill { display: inline-flex; align-items: center; gap: .4rem; background: rgba(255,255,255,.16); backdrop-filter: blur(6px); border: 1px solid rgba(255,255,255,.22); padding: .34rem .7rem; border-radius: var(--r-pill); font-size: .82rem; font-weight: 600; color: #fff; }
.qm-rd-pill small { font-weight: 500; opacity: .85; }
.qm-rd-pill i { font-size: .8em; }
/* Rating pill: a gold star (matches the review stars everywhere), not a green tier. */
.qm-rd-rate .fa-star { color: var(--star, #eda712); }
.qm-rd-hero-actions { display: flex; gap: var(--sp-2, 8px); flex: 0 0 auto; align-items: flex-end; }
.qm-rd-hero-actions .qm-fav-form { margin: 0; }
/* Cuisine flag — a static badge popping over the lower-left corner of the logo (rounded, glossy). */
.qm-rd-hero-logowrap { position: relative; flex: 0 0 auto; }
.qm-rd-flag { position: absolute; right: -7px; bottom: -7px; z-index: 3; width: 34px; height: 34px; border-radius: 50%; overflow: hidden; background: #fff;
    border: 2px solid #fff; box-shadow: 0 4px 14px rgba(0,0,0,.3); }
.qm-rd-flag img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qm-rd-action { display: inline-flex; align-items: center; gap: .45rem; background: #fff; color: var(--ink-900, #1f0b12); border: 1px solid transparent; border-radius: var(--r-pill); padding: .55rem 1rem; font-size: .85rem; font-weight: 700; cursor: pointer; transition: transform var(--t-fast), box-shadow var(--t), background var(--t-fast), color var(--t-fast); box-shadow: var(--e-1); }
.qm-rd-action:hover { transform: translateY(-2px); box-shadow: var(--e-2); }
/* Override shared .qm-fav-btn sizing so the hero action stays a labelled pill. */
.qm-rd-action.qm-fav-btn { width: auto; height: auto; border-radius: var(--r-pill); font-size: .85rem; }
.qm-rd-fav i { color: var(--qm-primary, #ff003d); }
.qm-rd-fav:hover { transform: translateY(-2px); }
.qm-rd-fav.is-fav { background: var(--qm-primary, #ff003d); color: #fff; }
.qm-rd-fav.is-fav i { color: #fff; }

/* Sticky tab bar */
.qm-rd-tabs { position: sticky; top: 68px; z-index: 30; background: var(--surface, #fff); border-bottom: 1px solid var(--line, #ffdbe5); box-shadow: var(--e-1); }
/* The bar now carries the tab list plus the live open/closed status, so its row
   becomes a flex line: the list takes the free space (keeping its own horizontal
   scroll) and the status pins to the end. Flex is what guarantees the two can
   never overlap at narrow widths — the tabs scroll instead. Logical flex/gap, no
   physical offsets, so the RTL build flips it without a hand-written override. */
.qm-rd-tabs > .container-fluid { display: flex; align-items: center; gap: var(--sp-3, 12px); }
/* End-cap: a rule separates the fixed status from the scrolling tab list, so a
   tab that is only half-scrolled-into-view at narrow widths reads as "this list
   scrolls" instead of as a label being chopped by the badge. Logical padding /
   border, so RTL needs no override at all. */
.qm-rd-tabs-status { flex: 0 0 auto; font-size: .82rem; padding-inline-start: var(--sp-3, 12px); padding-inline-end: var(--sp-4, 16px); border-inline-start: 1px solid var(--line, #ffdbe5); }
.qm-rd-tablist { flex: 1 1 auto; min-width: 0; list-style: none; display: flex; gap: var(--sp-2, 8px); margin: 0; padding: 0; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.qm-rd-tablist::-webkit-scrollbar { display: none; }
.qm-rd-tablist a { display: inline-flex; align-items: center; gap: .45rem; white-space: nowrap; padding: var(--sp-4, 16px) var(--sp-3, 12px); font-size: .9rem; font-weight: 600; color: var(--ink-500, #754151); text-decoration: none; border-bottom: 3px solid transparent; transition: color var(--t-fast), border-color var(--t-fast); }
.qm-rd-tablist a:hover { color: var(--qm-primary, #ff003d); }
.qm-rd-tablist a.is-active { color: var(--qm-primary, #ff003d); border-bottom-color: var(--qm-primary, #ff003d); }
.qm-rd-tablist a i { font-size: .82em; }

/* One sticky offset for every column on this page. The sub-nav tab bar is itself sticky
   (68px header + ~56px tabs), so the menu jump-nav and the Your-order cart must stick
   BELOW it with a gap — otherwise they tuck under / butt against the tabs while scrolling. */
.qm-rd-body { padding-top: var(--sp-6); --rd-stick-top: calc(68px + 56px + var(--sp-4, 16px)); }
.qm-rd-h2 { font-size: clamp(1.3rem, 2.2vw, 1.7rem); font-weight: 800; color: var(--ink-900, #1f0b12); margin: 0; line-height: 1.2; }
.qm-rd-menu-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: var(--sp-5, 20px); }
.qm-rd-menu-head-text { min-width: 0; }
.qm-rd-sec-head { margin-bottom: var(--sp-5, 20px); }
.qm-rd-section { scroll-margin-top: calc(68px + var(--sp-6)); }
#menu, #about, #map, #gallery, #reviews, #similar { scroll-margin-top: calc(68px + var(--sp-6)); }
.qm-rd-h3 { font-size: clamp(1.1rem, 1.8vw, 1.35rem); font-weight: 800; color: var(--ink-900, #1f0b12); margin: 0; line-height: 1.2; }
.qm-rd-cat-count { display: inline-flex; align-items: center; justify-content: center; min-width: 1.5rem; height: 1.5rem; padding: 0 .4rem; margin-right: .4rem; font-size: .72rem; font-weight: 700; color: var(--qm-primary, #ff003d); background: var(--qm-primary-50); border-radius: var(--r-pill); vertical-align: middle; }
.qm-loyalty-strip-cap { font-size: .82rem; color: var(--ink-500, #754151); flex: 1 1 160px; }
.qm-loyalty-strip a { color: var(--qm-primary-600); font-weight: 700; }

/* Left info card */
/* Info + the "Active deal" promo share this aside column; keeping the info sticky
   made the promo overlap it on scroll, so the info now stays in normal flow. */
.qm-rd-info { position: static; }
.qm-rd-info-title { font-size: 1.05rem; font-weight: 800; color: var(--ink-900, #1f0b12); margin: 0 0 var(--sp-3, 12px); }
/* Title line with the live open/closed status pinned to the opposite end. Logical
   flex, so the RTL build mirrors it without a hand-written override. */
.qm-rd-info-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: var(--sp-3, 12px); }
.qm-rd-info-head .qm-rd-info-title { margin: 0; }
.qm-rd-info-head .qm-rd-open-now { flex-shrink: 0; }
.qm-rd-info-about { font-size: .9rem; color: var(--ink-700, #4a2630); margin: 0 0 var(--sp-4, 16px); }
.qm-rd-info-sub { display: flex; align-items: center; gap: .5rem; font-size: .82rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--ink-900, #1f0b12); margin: var(--sp-4, 16px) 0 var(--sp-2, 8px); padding-top: var(--sp-4, 16px); border-top: 1px solid var(--line, #ffdbe5); }
.qm-rd-info-sub:first-of-type { border-top: none; padding-top: 0; }
.qm-rd-info-sub i { color: var(--qm-primary, #ff003d); font-size: .9em; }
.qm-rd-hours { list-style: none; margin: 0 0 var(--sp-2, 8px); padding: 0; }
.qm-rd-hours-row { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); padding: .5rem .65rem; font-size: .86rem; border-bottom: 1px solid var(--line, #ffdbe5); }
.qm-rd-hours-row:last-child { border-bottom: 0; }
.qm-rd-hours-day { display: inline-flex; align-items: center; gap: .5rem; font-weight: 600; color: var(--ink-700, #4a2630); }
.qm-rd-hours-time { font-weight: 600; color: var(--ink-500, #754151); font-variant-numeric: tabular-nums; white-space: nowrap; }
.qm-rd-hours-time.is-closed { color: var(--danger, #c0392b); }
.qm-rd-hours-row.is-today { background: var(--qm-primary-50); border-bottom-color: transparent; border-radius: var(--r-sm, 8px); padding-right: .7rem; padding-left: .7rem; }
.qm-rd-hours-row.is-today + .qm-rd-hours-row { border-top: 1px solid var(--line, #ffdbe5); }
.qm-rd-hours-row.is-today .qm-rd-hours-day,
.qm-rd-hours-row.is-today .qm-rd-hours-time { color: var(--qm-primary-600); font-weight: 700; }
.qm-rd-today { display: inline-block; font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; color: #fff; background: var(--qm-grad); padding: .14rem .5rem; border-radius: var(--r-pill); vertical-align: middle; }
.qm-rd-open-now { display: flex; align-items: center; gap: .5rem; font-size: .9rem; font-weight: 600; color: var(--ok); margin: 0; }
.qm-rd-dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 18%, transparent); }
/* Open state: the green dot pulses "live" (reuses the dashboard's qm-kpi-pulse ring).
   Closed state keeps a static red dot - no pulse when the kitchen is shut. */
.qm-rd-open-now:not(.is-closed) .qm-rd-dot { --kc: var(--ok); animation: qm-kpi-pulse 1.8s ease-out infinite; }
/* Closed-now variant of the status line (red dot + text). */
.qm-rd-open-now.is-closed { color: var(--danger); }
.qm-rd-open-now.is-closed .qm-rd-dot { background: var(--danger); box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 16%, transparent); }
/* "Closed right now" banner shown above the menu. */
.qm-closed-banner { display: flex; align-items: center; gap: 12px; margin: 0 0 20px; padding: 13px 18px;
  background: var(--warn-bg); color: var(--warn); border: 1px solid color-mix(in srgb, var(--warn) 28%, transparent); border-radius: 12px; font-size: 14px; line-height: 1.45; }
.qm-closed-banner i { font-size: var(--fs-800); flex: 0 0 auto; }
.qm-rd-deliv { list-style: none; margin: 0; padding: 0; }
.qm-rd-deliv li { display: flex; justify-content: space-between; gap: var(--sp-2, 8px); font-size: .86rem; padding: .3rem 0; color: var(--ink-500, #754151); }
.qm-rd-deliv strong { color: var(--ink-900, #1f0b12); }
.qm-rd-contact { list-style: none; margin: 0; padding: 0; }
.qm-rd-contact li { display: flex; align-items: flex-start; gap: .55rem; font-size: .86rem; padding: .3rem 0; color: var(--ink-700, #4a2630); }
.qm-rd-contact li i { color: var(--qm-primary, #ff003d); margin-top: .15rem; width: 1rem; text-align: center; flex: 0 0 auto; }
.qm-rd-contact a { color: var(--ink-700, #4a2630); text-decoration: none; word-break: break-word; }
.qm-rd-contact a:hover { color: var(--qm-primary, #ff003d); text-decoration: underline; }

/* Left promo */
.qm-rd-promo { margin-top: var(--sp-4, 16px); background: linear-gradient(-135deg, var(--qm-primary, #ff003d), var(--qm-primary-700)); color: #fff; border: none; }
.qm-rd-promo-badge { display: inline-flex; align-items: center; gap: .35rem; background: rgba(255,255,255,.2); padding: .2rem .55rem; border-radius: var(--r-pill); font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
/* Scoped to .qm-rd-promo on purpose: the generic `.qm-card :is(h1,…,h6)` rule in
   app.css outranks a bare .qm-rd-promo-title, and was flattening this heading to
   ink-900 with `margin: 0 0 16px` — a black title on the brand gradient, sitting
   flush against the deal tag above it. Two class names beat that. #fff is the
   card's established on-brand foreground (.qm-rd-promo and .qm-rd-promo-text
   already set it), not a hardcoded brand colour. 1.2rem/800 = 19.2px bold, which
   clears the WCAG large-text threshold, so white passes AA on the gradient. */
.qm-rd-promo .qm-rd-promo-title { font-size: 1.2rem; font-weight: 800; color: #fff; margin: var(--sp-3, 12px) 0 .4rem; }
.qm-rd-promo-text { font-size: .85rem; color: rgba(255,255,255,.92); margin: 0 0 var(--sp-3, 12px); }

/* Right mini-map */
/* Sits UNDER the cart in the order sidebar; margin-top separates it from the cart. */
.qm-rd-mini-map { margin-top: var(--sp-4, 16px); display: block; text-decoration: none; overflow: hidden; padding: 0; }
.qm-rd-mini-map-canvas { position: relative; height: 120px; background: linear-gradient(-135deg, #fff4f8, #ffe3ec); background-image: repeating-linear-gradient(0deg, rgba(255,201,218,.7) 0 1px, transparent 1px 28px), repeating-linear-gradient(-90deg, rgba(255,201,218,.7) 0 1px, transparent 1px 34px); }
/* Live (opt-in) embedded map preview. */
.qm-rd-mini-map-frame { display: block; width: 100%; height: 160px; border: 0; }
a.qm-rd-mini-map-foot { display: block; text-decoration: none; transition: background var(--t-fast); }
a.qm-rd-mini-map-foot:hover { background: var(--qm-primary-50); }
.qm-rd-mini-pin { position: absolute; top: 50%; right: 50%; transform: translate(50%, -100%); color: var(--qm-primary, #ff003d); font-size: 1.7rem; filter: drop-shadow(0 4px 6px color-mix(in srgb, var(--qm-primary) 40%, transparent)); }
.qm-rd-mini-map-foot { padding: var(--sp-3, 12px) var(--sp-4, 16px); }
.qm-rd-mini-map-foot strong { display: flex; align-items: center; gap: .4rem; font-size: .9rem; color: var(--ink-900, #1f0b12); }
.qm-rd-mini-map-foot strong i { color: var(--qm-primary, #ff003d); }
.qm-rd-mini-map-foot span { display: block; font-size: .8rem; color: var(--ink-500, #754151); margin-top: .2rem; }

/* About / signature-dish figure (static captioned photo, not a fake video player) */
.qm-rd-about-text { font-size: .98rem; line-height: 1.7; color: var(--ink-700, #4a2630); margin: 0 0 var(--sp-5, 20px); }
.qm-rd-stats { display: flex; gap: var(--sp-3, 12px); flex-wrap: wrap; }
.qm-rd-stat { flex: 1 1 120px; background: var(--surface, #fff); border: 1px solid var(--line, #ffdbe5); border-radius: var(--r-md); padding: var(--sp-4, 16px); text-align: center; box-shadow: var(--e-1); }
.qm-rd-stat-num { display: block; font-size: 1.6rem; font-weight: 800; color: var(--qm-primary, #ff003d); line-height: 1; }
.qm-rd-stat-num small { font-size: .8rem; font-weight: 600; color: var(--ink-500, #754151); margin-right: .15rem; }
.qm-rd-stat-lbl { display: block; font-size: .78rem; color: var(--ink-500, #754151); margin-top: .35rem; }
/* Fixed frame: the media always fills its column and is cropped to a set, fluid height, so
   a portrait clip or an oversized still can never stretch the About block. Height is driven
   by the viewport (not an aspect ratio) — pairing aspect-ratio with a max-height would cap
   the WIDTH too and leave the column short. */
.qm-rd-figure { position: relative; margin: 0; width: 100%; height: clamp(220px, 24vw, 340px);
  border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); }
/* One frame for all three media types an owner can set: still, uploaded MP4, or YouTube embed. */
.qm-rd-figure-img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; border: 0; }
.qm-rd-figure-cap { position: absolute; right: var(--sp-4, 16px); bottom: var(--sp-4, 16px); left: var(--sp-4, 16px); color: #fff; font-size: .85rem; font-weight: 600; text-shadow: 0 2px 8px rgba(0,0,0,.55); }
.qm-rd-figure::after { content: ""; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(31,11,18,.45), rgba(31,11,18,.05) 55%); pointer-events: none; }

/* Map section (decorative stylized location illustration; labelled via role=img) */
.qm-rd-map { position: relative; border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); min-height: 360px; }
.qm-rd-map-svg { display: block; width: 100%; height: 360px; }
/* Live (opt-in) Google embed fills the same box; the info card overlays it. */
.qm-rd-map-frame { display: block; width: 100%; height: 360px; border: 0; }
.qm-rd-map-pin { position: absolute; top: 46%; right: 50%; transform: translate(50%, -100%); color: var(--qm-primary, #ff003d); font-size: 2.6rem; filter: drop-shadow(0 6px 8px color-mix(in srgb, var(--qm-primary) 45%, transparent)); }
.qm-rd-map-pulse { position: absolute; right: 50%; bottom: -6px; width: 26px; height: 26px; border-radius: 50%; background: color-mix(in srgb, var(--qm-primary) 30%, transparent); transform: translateX(50%); animation: qmRdMapPulse 2s ease-out infinite; }
@keyframes qmRdMapPulse { 0% { transform: translateX(50%) scale(.5); opacity: .8; } 100% { transform: translateX(50%) scale(2.4); opacity: 0; } }
.qm-rd-map-card { position: absolute; right: var(--sp-5, 20px); bottom: var(--sp-5, 20px); max-width: 320px; background: var(--surface, #fff); border-radius: var(--r-md); padding: var(--sp-4, 16px); box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); }
.qm-rd-map-card strong { display: flex; align-items: center; gap: .45rem; font-size: 1rem; color: var(--ink-900, #1f0b12); }
.qm-rd-map-card strong i { color: var(--qm-primary, #ff003d); }
.qm-rd-map-card p { font-size: .85rem; color: var(--ink-500, #754151); margin: .35rem 0 .5rem; }
.qm-rd-map-radius { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; font-weight: 600; color: var(--qm-primary, #ff003d); background: var(--qm-primary-50); padding: .3rem .6rem; border-radius: var(--r-pill); }

/* Gallery */
.qm-rd-gallery { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--sp-3, 12px); }
.qm-rd-gitem { position: relative; border: none; padding: 0; margin: 0; cursor: pointer; border-radius: var(--r-md); overflow: hidden; aspect-ratio: 1 / 1; background: var(--qm-primary-50); box-shadow: var(--e-1); }
.qm-rd-gitem img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s ease; image-rendering: auto; }
.qm-rd-gitem:hover img { transform: scale(1.08); }
.qm-rd-gzoom { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(31,11,18,.32); color: #fff; font-size: 1.4rem; opacity: 0; transition: opacity var(--t-fast); }
.qm-rd-gitem:hover .qm-rd-gzoom, .qm-rd-gitem:focus-visible .qm-rd-gzoom { opacity: 1; }
/* The lightbox overlay itself is now global (app.css / js/qm-lightbox.js), shared
   with the footer gallery — only the per-thumbnail styles above stay page-specific. */

/* Reviews tweaks */
.qm-rd-review-form { position: sticky; top: var(--rd-stick-top, 124px); }
.qm-rd-review-note { display: flex; gap: .65rem; align-items: flex-start; background: var(--qm-primary-50); border-color: var(--qm-primary-100); }
.qm-rd-review-note i { color: var(--qm-primary, #ff003d); font-size: 1.2rem; margin-top: .1rem; }
.qm-rd-review-note p { margin: 0; font-size: .88rem; color: var(--ink-700, #4a2630); }

/* ===== Offers / coupons strip ===== */
.qm-rd-offers { background: var(--surface, #fff); border: 1px solid var(--line, #ffdbe5); border-radius: var(--r-lg); padding: var(--sp-5, 20px); margin-bottom: var(--sp-5, 20px); box-shadow: var(--e-1); }
.qm-rd-offers-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3, 12px); margin-bottom: var(--sp-4, 16px); flex-wrap: wrap; }
.qm-rd-offers-head .qm-rd-h3 i { color: var(--qm-primary, #ff003d); }
.qm-rd-offers-sub { font-size: .8rem; font-weight: 600; color: var(--ink-500, #754151); }
.qm-rd-offer-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: var(--sp-4, 16px); }
.qm-rd-offer { display: flex; align-items: center; gap: var(--sp-4, 16px); padding: var(--sp-4, 16px); border: 1px dashed var(--qm-primary-100); border-radius: var(--r-md); background: linear-gradient(-180deg, #fff, #fff4f8); transition: transform var(--t-fast), box-shadow var(--t); }
.qm-rd-offer:hover { transform: translateY(-2px); box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); }
.qm-rd-offer-icon { flex: 0 0 auto; width: 46px; height: 46px; border-radius: var(--r-sm); display: flex; align-items: center; justify-content: center; background: var(--qm-grad); color: #fff; font-size: 1.15rem; box-shadow: 0 8px 16px color-mix(in srgb, var(--qm-primary) 28%, transparent); }
.qm-rd-offer-body { flex: 1 1 auto; min-width: 0; }
.qm-rd-offer-title { display: block; font-size: .92rem; font-weight: 700; color: var(--ink-900, #1f0b12); line-height: 1.3; }
.qm-rd-offer-text { display: block; font-size: .78rem; color: var(--ink-500, #754151); margin-top: .15rem; }
.qm-rd-offer-code { flex: 0 0 auto; font-size: .74rem; font-weight: 800; letter-spacing: .05em; color: var(--qm-primary-600); background: #fff; border: 1px dashed var(--qm-primary-100); border-radius: 8px; padding: .3rem .55rem; }

/* ===== Popular this week rail ===== */
.qm-rd-popular { margin-bottom: var(--sp-6); }
.qm-rd-pop-head { margin-bottom: var(--sp-4, 16px); }
/* Card width only. Scrolling, snapping, the hidden scrollbar and drag-to-scroll all come
   from the shared .qm-carousel-track (app.css) - the same rail the homepage "Trending
   this week" uses - so the prev/next buttons behave identically here. */
.qm-rd-pop-rail.qm-carousel-track { grid-auto-columns: 220px; gap: var(--sp-3, 12px); }
.qm-rd-pop-card { position: relative; background: var(--surface, #fff); border: 1px solid var(--line, #ffdbe5); border-radius: var(--r-md); overflow: hidden; box-shadow: var(--e-1); transition: transform var(--t-fast), box-shadow var(--t); cursor: pointer; }
.qm-rd-pop-card.qm-open-item:hover { transform: translateY(-3px); box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); }
.qm-rd-pop-img { height: 120px; background: var(--qm-primary-50); }
.qm-rd-pop-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.qm-rd-pop-info { padding: var(--sp-3, 12px) var(--sp-3, 12px) var(--sp-4, 16px); }
.qm-rd-pop-name { font-size: .92rem; font-weight: 700; color: var(--ink-900, #1f0b12); margin: 0 0 .25rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qm-rd-pop-price { font-size: .88rem; font-weight: 700; color: var(--ink-900); }
.qm-rd-pop-add { position: absolute; top: 96px; left: var(--sp-3, 12px); width: 38px; height: 38px; border-radius: 50%; border: none; background: var(--qm-primary, #ff003d); color: #fff; font-size: .95rem; cursor: pointer; box-shadow: var(--e-1); display: flex; align-items: center; justify-content: center; transition: transform var(--t-fast); }
.qm-rd-pop-add:hover { transform: scale(1.08); }

/* ===== Reviews summary card with rating bars ===== */
.qm-rd-rsum { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-5, 20px); align-items: center; margin-bottom: var(--sp-4, 16px); }
.qm-rd-rsum-score { text-align: center; padding-left: var(--sp-4, 16px); border-left: 1px solid var(--line, #ffdbe5); }
.qm-rd-rsum-num { display: block; font-size: 2.6rem; font-weight: 800; color: var(--ink-900, #1f0b12); line-height: 1; }
.qm-rd-rsum-stars { display: block; color: var(--star, #eda712); font-size: .9rem; margin: .35rem 0 .2rem; }
.qm-rd-rsum-stars .far { color: var(--star-empty, #e6d7b8); }
.qm-rd-rsum-count { font-size: .76rem; color: var(--ink-500, #754151); }
.qm-rd-rsum-bars { display: flex; flex-direction: column; gap: .35rem; min-width: 0; }
.qm-rd-rsum-row { display: flex; align-items: center; gap: var(--sp-2, 8px); }
.qm-rd-rsum-lbl { flex: 0 0 2.4rem; font-size: .78rem; font-weight: 600; color: var(--ink-700, #4a2630); white-space: nowrap; }
.qm-rd-rsum-lbl i { color: var(--star, #eda712); font-size: .72em; }
.qm-rd-rsum-track { flex: 1 1 auto; height: 8px; border-radius: var(--r-pill); background: var(--star-empty, #e6d7b8); overflow: hidden; }
.qm-rd-rsum-fill { display: block; height: 100%; border-radius: var(--r-pill); background: var(--star, #eda712); }
.qm-rd-rsum-pct { flex: 0 0 2.6rem; text-align: left; font-size: .74rem; color: var(--ink-500, #754151); }

/* Similar restaurants now reuse the shared modern restaurant card
   (partials/cards/restaurant.php + .qm-rest-card* in app.css) inside a Bootstrap
   row, so the old bespoke .qm-rd-sim-* / .qm-rd-similar rules were removed. */

/* ===== Style 2: highlights row ===== */
/* Menu list/grid toggle: the switch sits on the menu-header row (right of the title),
   not in a band of its own. Cards reuse .qm-pop-card and the switch reuses .qm-seg -
   no new card or control style, only layout + visibility here. */
.qm-menu-view-seg { flex: 0 0 auto; }
.qm-menu-views[data-menu-view='list'] .qm-menu-view-grid { display: none; }
.qm-menu-views[data-menu-view='grid'] .qm-menu-view-list { display: none; }
.qm-food-grid-card--off { opacity: .55; }
/* Mobile: the list/grid switch shows icons only — the fa-bars / fa-th-large glyphs read
   as list/grid on their own, and the labels crowd the menu-header row on a phone. */
@media (max-width: 575.98px) {
    .qm-menu-view-seg .qm-seg-btn span { display: none; }
}

/* Top gap so the sticky "Your order" panel above never butts against this row. */
.qm-rd-highlights { margin-top: var(--sp-6, 28px); margin-bottom: var(--sp-5, 20px); }
/* The relocated location map fills the space beside the tall info card. */
.qm-rd-highlights-map .qm-rd-map { min-height: 260px; }
/* Both columns stretch to the row height so the block reads as one aligned band:
   the info card finishes level with the map, and the review card matches the deal
   beside it. The map is the flexible element - it absorbs whatever height the info
   card sets, rather than leaving a ragged edge below it. */
.qm-rd-highlights > [class*='col-'] { display: flex; flex-direction: column; }
/* The card's own bottom margin would leave it 20px short of the map; the row gap
   already spaces this band from what follows. */
.qm-rd-highlights > [class*='col-'] > .qm-card { flex: 1 1 auto; margin-bottom: 0; }
.qm-rd-highlights-map { flex: 1 1 auto; display: flex; flex-direction: column; }
.qm-rd-highlights-map .qm-rd-map { flex: 1 1 auto; display: flex; flex-direction: column; }
/* ...and the actual map fills that stretched box. The live Leaflet frame and the
   stylized SVG are pinned to 360px as a standalone band in the other layouts; here they
   must grow with their wrapper, or the map sits 360px tall inside a taller box beside
   the info card. The address overlay card stays position:absolute, so flex ignores it. */
.qm-rd-highlights-map .qm-rd-map-frame,
.qm-rd-highlights-map .qm-rd-map-svg { flex: 1 1 auto; height: auto; }
.qm-rd-highlights .row > [class*='col-'] > * { height: 100%; }
.qm-rd-body--s2 .qm-rd-info { position: static; height: 100%; }
.qm-rd-body--s2 .qm-rd-promo { margin-top: 0; height: 100%; }
.qm-rd-body--s2 .qm-cart-side { position: sticky; top: var(--rd-stick-top, 124px); }

@media (max-width: 991.98px) {
    .qm-rd-info, .qm-rd-review-form { position: static; }
    .qm-rd-body--s2 .qm-cart-side { position: static; }
    #menu, #about, #map, #gallery, #reviews, #similar, .qm-rd-section { scroll-margin-top: calc(68px + var(--sp-6)); }
    .qm-rd-gallery { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 575.98px) {
    .qm-rd-gallery { grid-template-columns: repeat(3, 1fr); }
    .qm-rd-rsum { grid-template-columns: 1fr; }
    .qm-rd-rsum-score { padding-left: 0; padding-bottom: var(--sp-3, 12px); border-left: none; border-bottom: 1px solid var(--line, #ffdbe5); }
    .qm-rd-hero-actions { width: 100%; }
    .qm-rd-action { flex: 1 1 auto; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
    .qm-rd-map-pulse, .qm-rd-open-now .qm-rd-dot { animation: none; }
    .qm-rd-gitem img, .qm-rd-pop-card { transition: none; }
}

/* ===== Item popup — wide 2-pane, tabbed ingredients (design-system tokens) ===== */
.qm-item-modal-wide .modal-dialog { max-width: 880px; }
.qm-item-modal .modal-content { border: none; border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--e-4, 0 26px 58px rgba(78,8,29,.24)); position: relative; }
.qm-item-modal-close { position: absolute; top: 14px; left: 14px; z-index: 5; width: 2.2rem; height: 2.2rem; display: inline-flex; align-items: center; justify-content: center; background: #fff; color: var(--ink-900); border: none; border-radius: 50%; font-size: 1.05rem; line-height: 1; opacity: 1; box-shadow: 0 2px 10px rgba(20,20,20,.2); transition: background var(--t-fast), color var(--t-fast); }
.qm-item-modal-close:hover, .qm-item-modal-close:focus-visible { background: var(--qm-primary, #ff003d); color: #fff; outline: none; }
.qm-im-grid { display: grid; grid-template-columns: 320px 1fr; align-items: start; }
.qm-im-left { padding: var(--sp-5, 20px); background: linear-gradient(-180deg, #fff, var(--tint)); border-left: 1px solid var(--line); }
.qm-im-media { line-height: 0; border-radius: var(--r-md); overflow: hidden; box-shadow: var(--e-2, 0 10px 24px rgba(78,8,29,.14)); }
.qm-item-modal-img { width: 100%; height: 210px; object-fit: cover; display: block; }
.qm-item-modal-name { font-size: 1.3rem; font-weight: 800; color: var(--ink-900); margin: var(--sp-4, 16px) 0 var(--sp-2, 8px); }
.qm-item-modal-sub { display: flex; align-items: center; gap: var(--sp-3, 12px); flex-wrap: wrap; margin-bottom: var(--sp-2, 8px); }
.qm-item-modal-price { font-size: 1.1rem; font-weight: 800; color: var(--qm-primary, #ff003d); }
.qm-item-modal-cal { font-size: .85rem; color: var(--ink-500); }
.qm-item-modal-desc { color: var(--ink-700); font-size: .92rem; margin: var(--sp-2, 8px) 0 0; }
.qm-item-modal-allergens { font-size: .82rem; color: var(--ink-500); margin: var(--sp-3, 12px) 0 0; }
/* Extra top padding, not the flat 20px the left pane uses: the close button is a 35px pill floating
   in this corner, so at 20px the first section's title sat level with its middle and the whole pane
   read as starting under it. The content now clears the button instead of racing it. */
.qm-im-right { padding: 34px var(--sp-5, 20px) var(--sp-5, 20px); max-height: 64vh; overflow-y: auto; }
/* Each option group folds. A <details>, the same collapse the account nav uses, so the marker is
   suppressed the same way and the caret turns on [open] rather than a class being scripted. */
.qm-item-modal-group { padding: 0 0 var(--sp-5, 20px); }
/* One choice group to the next gets the same rule that separates the pane's top-level sections, so
   "Size" and "Extras" read as two things rather than one run of controls — and on the same 20/20
   rhythm, so a group's band is not centred differently from a section's. */
.qm-item-modal-group + .qm-item-modal-group { padding-top: var(--sp-5, 20px); border-top: 1px solid var(--line); margin-top: 0; }
.qm-item-modal-group > summary { list-style: none; cursor: pointer; }
.qm-item-modal-group > summary::-webkit-details-marker { display: none; }
/* center, not baseline: the row carries a 16px title, a small-caps badge and an icon, and aligning
   three different type sizes on their baselines left the badge and the caret sitting low against
   the title. Centring puts them on one optical line. */
.qm-item-modal-group-head { display: flex; align-items: center; gap: var(--sp-2, 8px); margin-bottom: var(--sp-3, 12px); }
/* A closed group has nothing under its header, so the header's bottom margin is dead space that
   only widens the gap to the next section. */
.qm-item-modal-group:not([open]) > .qm-item-modal-group-head { margin-bottom: 0; }
/* Only the section that actually sits under the close button needs to stop short of it — that is
   "Customize your order", which carries the inset itself. A dish with no ingredients hides that
   section, and then the first choice group inherits the corner, so it takes the inset instead. */
#qm-im-ing-wrap[hidden] + .qm-item-modal-groups > .qm-item-modal-group:first-child > .qm-item-modal-group-head { padding-left: 44px; }
.qm-item-modal-group-title { font-size: 1rem; font-weight: 800; color: var(--ink-900); margin: 0; }
.qm-item-modal-group-req { font-size: .72rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--qm-primary, #ff003d); }
.qm-item-modal-group-opt { font-size: .78rem; color: var(--ink-500); }
/* What is chosen inside, so a folded group still answers "and what did I pick?". It sits beside the
   badge rather than at the far end: only the caret takes the auto margin, so the caret is pinned to
   the same spot whether the group is open or closed instead of hopping across the row. */
.qm-item-modal-group-sel { font-size: .82rem; color: var(--ink-500); }
.qm-item-modal-group[open] .qm-item-modal-group-sel { display: none; }
/* Pinned to the end of the row, with room around it so it is a target rather than a mark jammed
   against the text. No negative margin: that pulled the glyph 6px past the pane's content edge, so
   the carets lined up with the floating close button instead of with the option prices below them. */
.qm-item-modal-group-caret { margin-inline-start: auto; flex: 0 0 auto; padding: 4px var(--sp-3, 12px) 4px 0; font-size: .7rem; color: var(--ink-400); transition: transform var(--t-fast, .15s ease); }
.qm-item-modal-group[open] .qm-item-modal-group-caret { transform: rotate(-180deg); }
.qm-item-modal-opt { display: flex; align-items: center; gap: var(--sp-3, 12px); padding: 11px 0; cursor: pointer; }
.qm-item-modal-opt input { accent-color: var(--qm-primary, #ff003d); width: 1.1rem; height: 1.1rem; flex: 0 0 auto; }
.qm-item-modal-opt-name { flex: 1 1 auto; color: var(--ink-700); font-size: .92rem; }
.qm-item-modal-opt-delta { color: var(--ink-500); font-size: .88rem; font-weight: 600; }
/* padding-right reserves space for the absolute close (X) button (top-right of the
   modal) so the right-aligned "Tap to add or remove" hint never slides under it. */
.qm-im-ing-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2, 8px); margin-bottom: var(--sp-3, 12px); padding-left: 44px; }
.qm-im-ing-title { font-size: 1rem; font-weight: 800; color: var(--ink-900); margin: 0; }
.qm-im-ing-hint { font-size: .78rem; color: var(--ink-500); }
.qm-im-ing-tabs { display: flex; flex-wrap: wrap; gap: 6px; border-bottom: 1px solid var(--line); padding-bottom: var(--sp-2, 8px); margin-bottom: var(--sp-3, 12px); }
/* The scroll cap the ingredient list has always rendered with. It used to arrive
   by accident from the vendor builder's .qm-ing-panes in app.css; the modal now
   owns it outright so the two components no longer share a class name. */
.qm-im-ing-panes { max-height: 52vh; overflow-y: auto; padding-left: 4px; }
.qm-im-ing-tab { border: 1px solid var(--line); background: var(--surface); color: var(--ink-700); border-radius: var(--r-pill); padding: .35rem .8rem; font-size: .82rem; font-weight: 700; cursor: pointer; transition: all .15s; }
.qm-im-ing-tab:hover { border-color: var(--qm-primary, #ff003d); color: var(--qm-primary, #ff003d); }
.qm-im-ing-tab.is-active { background: var(--qm-primary, #ff003d); border-color: var(--qm-primary, #ff003d); color: #fff; box-shadow: 0 6px 12px color-mix(in srgb, var(--qm-primary) 28%, transparent); }
.qm-im-ing-pane { display: none; }
.qm-im-ing-pane.is-active { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2, 8px); }
@media (max-width: 540px) { .qm-im-ing-pane.is-active { grid-template-columns: 1fr; } }
.qm-im-ing-chip { display: inline-flex; align-items: center; gap: .55rem; width: 100%; padding: .45rem .45rem .45rem .7rem; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--surface); color: var(--ink-700); font-size: .88rem; font-weight: 600; cursor: pointer; transition: border-color .15s, background .15s, color .15s, box-shadow .15s; text-align: right; }
.qm-im-ing-chip:hover { border-color: var(--qm-primary, #ff003d); }
.qm-im-ing-chip:focus-visible { outline: 2px solid var(--qm-primary, #ff003d); outline-offset: 2px; }
.qm-im-ing-chip-icon { width: 1.9rem; height: 1.9rem; border-radius: 50%; object-fit: contain; background: #fff; flex: 0 0 auto; box-shadow: 0 2px 5px rgba(78,8,29,.12); }
.qm-im-ing-chip-name { line-height: 1.1; flex: 1 1 auto; }
.qm-im-ing-chip-price { font-size: .82rem; color: var(--qm-primary, #ff003d); font-weight: 800; }
.qm-im-ing-chip[data-kind="included"] { background: var(--qm-primary-50); border-color: var(--qm-primary, #ff003d); color: var(--ink-900); }
.qm-im-ing-chip[data-kind="included"][aria-pressed="true"] { background: #fff; border-color: var(--line); color: var(--ink-500); text-decoration: line-through; }
.qm-im-ing-chip[data-kind="extra"][aria-pressed="true"] { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-im-ing-chip[data-kind="extra"][aria-pressed="true"] .qm-im-ing-chip-price { color: #fff; }
/* The right pane is a stack of sections — the choice groups, the ingredient picker, the notes box —
   and only the notes box carried a divider, so the first two ran into each other with nothing to
   say where one ended. One rule for every section boundary, taking the spacing the notes box
   already defined; `* + *` means the first section keeps a flush top. */
/* Equal space either side of every dividing line: 20px of padding above the content and 20px below,
   and NO margin. A margin sits outside the border, so the 12px that used to be here widened the gap
   BELOW a section without widening the gap above the next one — every header ended up 17px under
   its own line and 28px above the following one, which is why the sections read as sitting high in
   their bands. Padding on both sides is the only way the two distances can be equal. */
.qm-im-right > * { padding-bottom: var(--sp-5, 20px); }
.qm-im-right > * + * { padding-top: var(--sp-5, 20px); border-top: 1px solid var(--line); margin-top: 0; }
/* The wrapper holds no content of its own — its groups each carry their own bottom padding, so
   giving it one too would double the space before the next section. */
.qm-item-modal-groups { padding-bottom: 0; }
/* A dish with no choice groups leaves this container in the markup but empty, and an empty section
   must not draw the divider that separates it from the next one. */
.qm-item-modal-groups:empty { display: none; }
.qm-item-modal-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); border-top: 1px solid var(--line); }
.qm-qty-stepper { display: inline-flex; align-items: center; gap: var(--sp-3, 12px); border: 1px solid var(--line); border-radius: var(--r-pill); padding: .25rem .5rem; }
.qm-qty-btn { width: 2rem; height: 2rem; border: none; border-radius: 50%; background: var(--surface); color: var(--ink-900); font-size: 1.1rem; line-height: 1; cursor: pointer; }
.qm-qty-btn:hover { background: var(--qm-primary, #ff003d); color: #fff; }
.qm-qty-value { min-width: 1.5rem; text-align: center; font-weight: 700; }
.qm-item-modal-add { flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: space-between; gap: var(--sp-3, 12px); }
.qm-item-modal-add[disabled] { opacity: .6; cursor: not-allowed; }
.qm-item-modal-add-total { font-weight: 800; }
.qm-item-modal-add.is-added { background: var(--qm-primary-600); border-color: var(--qm-primary-600); color: #fff; }
.qm-food.qm-open-item { cursor: pointer; }
.qm-food-opts-hint { margin-top: var(--sp-2, 8px); font-size: .8rem; color: var(--ink-500); display: inline-flex; align-items: center; gap: .35rem; }
.qm-food-opts-hint i { color: var(--qm-primary, #ff003d); }
.qm-food-allergens { margin: var(--sp-2, 8px) 0 0; font-size: .8rem; color: var(--ink-500); }
@media (max-width: 680px) {
  .qm-im-grid { grid-template-columns: 1fr; }
  .qm-im-left { border-left: none; border-bottom: 1px solid var(--line); }
  .qm-im-right { max-height: none; }
  .qm-qty-btn { width: 2.75rem; height: 2.75rem; }
  .qm-item-modal-close { width: 2.75rem; height: 2.75rem; }
}
@media (pointer: coarse) {
  .qm-rd-pop-add { min-width: 44px; min-height: 44px; }
}

/* ============================================================
   Dine-in QR scan-to-order page — banner + sticky bar.
   Loaded only on /table/{qr_token}; reuses the menu/item classes
   above so the food list + add-to-cart modal are identical.
   ============================================================ */
.qm-table-page { padding-bottom: 5.5rem; } /* clearance for the sticky bar */
.qm-table-hero { padding-block: clamp(1.75rem, 5vw, 3.25rem); }
.qm-table-banner { max-width: 56rem; }
.qm-table-badge {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .3rem .8rem; border-radius: var(--r-pill);
  background: var(--qm-primary, #ff003d); color: #fff;
  font-size: .8rem; font-weight: 700; letter-spacing: .02em;
  text-transform: uppercase;
}
.qm-table-title {
  margin: var(--sp-3, 12px) 0 0; color: #fff;
  font-size: clamp(1.5rem, 3.5vw, 2.25rem); font-weight: 800; line-height: 1.15;
}
.qm-table-sub { margin: var(--sp-2, 8px) 0 0; color: rgba(255,255,255,.86); max-width: 42rem; }
.qm-table-meta { margin-top: var(--sp-4, 16px); }
.qm-table-body { padding-top: clamp(1.5rem, 4vw, 2.5rem); }

/* Sticky "view cart & checkout" bar pinned to the viewport bottom. */
.qm-table-bar {
  position: fixed; right: 0; left: 0; bottom: 0; z-index: 1030;
  background: var(--surface);
  border-top: 1px solid var(--line);
  box-shadow: 0 -6px 24px rgba(20, 20, 20, .12);
}
.qm-table-bar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3, 12px); padding-block: var(--sp-3, 12px);
}
.qm-table-bar-info { display: inline-flex; align-items: center; gap: var(--sp-3, 12px); min-width: 0; }
.qm-table-bar-info > i { color: var(--qm-primary, #ff003d); font-size: 1.25rem; }
.qm-table-bar-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.qm-table-bar-text strong { color: var(--ink-900); }
.qm-table-bar-count { font-size: .85rem; color: var(--ink-500); }
.qm-table-bar-btn { white-space: nowrap; display: inline-flex; align-items: center; gap: .5rem; }
@media (max-width: 560px) {
  .qm-table-bar-btn span,
  .qm-table-bar-text strong { font-size: .95rem; }
  .qm-table-bar-inner { gap: var(--sp-2, 8px); }
}

/* "Load more" reveals the next batch of reviews; centred under the list. */
.qm-review-more { display: flex; justify-content: center; margin-top: var(--sp-4, 16px); }
