/* =============================================================================
 * Rahkar — «باندل‌ساز» (Bundle Builder) page.
 * -----------------------------------------------------------------------------
 * Two zones: the product grid (.rh-bundle__main) and the "customize your
 * bundle" tracker (.rh-bundle__panel) — a sticky sidebar on desktop, a fixed
 * bottom bar on mobile. Selection state (.is-selected / slot fill / CTA text)
 * is toggled entirely by assets/js/bundle-builder.js; this file only defines
 * what each state LOOKS like. RTL-first: logical properties throughout, no
 * *_rtl.css companion (see RAHKAR-THEME-GUIDE.md §4).
 * ========================================================================== */

/* ---- Hero (title + persuasive description) -------------------------------
 * Same classes/look as catalog/_partials/category-hero.tpl's box
 * (listing-facets.css .rpa-cat-hero*) — copied here rather than loading that
 * whole (much larger, facet-search-specific) stylesheet on this page just
 * for one box. Keep both in sync if the category hero's look changes. */
/* Flush under the header AND edge-to-edge, like the category page — the
   generic Bootstrap `.card.card-block` classes on #content (classic base
   theme.css) carry their own 1.25rem padding on EVERY side on any page that
   doesn't explicitly zero it: stacked with the hero's/grid's own spacing,
   that read as "too much space" on top AND as an inset gutter on the
   left/right that the category page doesn't have. This page positions its
   own hero/grid/panel spacing itself, so none of it is needed here. */
body.page-bundle #content.page-content {
  padding: 0;
}

.rpa-cat-hero {
  margin-block: 14px 24px;
  padding: clamp(28px, 5vw, 48px) 24px;
  border-radius: var(--wp--custom--radius--lg);
  background: color-mix(in srgb, var(--wp--preset--color--secondary) 50%, transparent);
  text-align: center;
}

.rpa-cat-hero__title {
  margin: 0;
  font-size: clamp(1.5rem, 3vw, 2.25rem);
  font-weight: var(--rh-fw-strong);
  line-height: 1.3;
  color: var(--wp--preset--color--contrast);
}

.rpa-cat-hero__desc {
  margin: 10px auto 0;
  max-width: 56ch;
  font-size: clamp(0.9rem, 1.1vw, 1rem);
  line-height: 1.8;
  color: var(--wp--preset--color--text);
}

.rpa-cat-hero__desc > * { margin-block: 0.6em; }
.rpa-cat-hero__desc > *:first-child { margin-block-start: 0; }
.rpa-cat-hero__desc > *:last-child { margin-block-end: 0; }

@media (max-width: 767px) {
  .rpa-cat-hero {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    margin-block: 0;
    border-radius: 0;
  }
}

/* Mobile header back button (header.tpl [data-rpa-back]) — same rule as
 * listing-facets.css's (copied here, not loaded there, for the same reason
 * as the hero box above: that file is otherwise unrelated to this page). */
.rpa-archive__back { display: none; }
@media (max-width: 991px) {
  .rpa-archive__back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    min-width: 40px;
    height: 40px;
    padding: 0 8px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  .rpa-archive__back:active { background: var(--wp--preset--color--surface); }
  .rh-row--mobile .rh-zone--start { gap: 2px; }
}

body.page-bundle #rh-bnav {
  /* The page has its own fixed bottom bar (.rh-bundle__panel on mobile);
     stacking the site's global bottom-nav under/behind it too is redundant
     and eats vertical space the grid needs. */
  display: none;
}

.rh-bundle {
  display: flex;
  flex-wrap: wrap;
  gap: var(--wp--preset--spacing--50);
  align-items: flex-start;
  padding-block: var(--wp--preset--spacing--40);
}

.rh-bundle__main {
  flex: 1 1 640px;
  min-width: 0;
}

/* ---- Filter pills ------------------------------------------------------- */
.rh-bundle__pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-block-end: var(--wp--preset--spacing--40);
}

.rh-bundle__pill {
  padding: 8px 16px;
  border-radius: var(--wp--custom--radius--pill);
  border: 1px solid var(--wp--preset--color--border);
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--text);
  font-size: var(--wp--preset--font-size--sm);
  font-weight: var(--rh-fw-strong);
  cursor: pointer;
}

.rh-bundle__pill.is-active {
  background: var(--wp--preset--color--primary);
  border-color: var(--wp--preset--color--primary);
  color: var(--wp--preset--color--base);
}

/* ---- Product grid ---------------------------------------------------------
 * The grid ITSELF is the sitewide `.rpa` / `.rpa-grid` / `.rpa--card-*`
 * system (product-archive.css + product-card.css, see bundle.tpl) — no
 * custom grid rules needed here, by request: "exactly like the category
 * product grid." */

/* The card itself is `.rpc-card` — the EXACT sitewide product-card look
   (assets/css/product-card.css, registered on this page via theme.yml) —
   by request: "exactly the category card style, plus an add-to-pack
   button." `.rh-bundle-card` only adds what that shared style doesn't
   provide: the grid-shrink fix and a positioning context for the two
   buttons this page bolts on (both deliberately OUTSIDE .rpc-card__link —
   an interactive element must never nest inside a link). */
.rh-bundle-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0; /* grid items default to min-width:auto — without this, an
    unbreakable price/button label forces the column (and the whole page)
    wider than the viewport instead of the text just wrapping/shrinking. */
}

/* .rpc-card__link's OWN height:100% (product-card.css) assumes it's the
   ONLY thing inside .rpc-card — fine everywhere else that class is used,
   but here a CSS Grid row stretches .rh-bundle-card to a definite height
   (default align-items:stretch) and the link then claimed ALL of it,
   pushing our appended button below the card's own bottom edge — invisible,
   silently clipped by .rpc-card's own overflow:hidden. Making the card a
   flex column and the link `flex:1` instead makes the two share that
   stretched height properly: image/title/price grow to fill it, the button
   keeps its own natural height at the bottom. */
.rh-bundle-card > .rpc-card__link {
  height: auto;
  flex: 1 1 auto;
  min-height: 0;
}

/* Selected state is communicated by the button label alone ("حذف از پک") —
   no border/outline on the card itself (dropped per feedback: a red ring
   around every picked card read as noisy against the grid). */

.rh-bundle-card__actions {
  flex: none;
  padding: 0 10px 10px;
}

/* LV mode (the shop's default) already pads the WHOLE card 12px/12px/16px
   and resets .rpc-card__link's own padding to 0 (product-card.css) — our
   sibling actions div sits in that same padded box, so it needs a top gap
   only, not another lateral inset (which would double it to ~22px). */
.rpa--card-lv .rh-bundle-card__actions {
  padding: 8px 0 0;
}

/* «مشاهده سریع» — same mechanism/dialog as the sitewide quick-view button
   (assets/js/quickview.js, generic `[data-rh-quickview]` delegate; see
   _partials/quickview-modal.tpl included once in bundle.tpl). Deliberately
   small, icon-only, and unobtrusive: "افزودن به پک" is the point of this
   page, so quick-view — a secondary action — must not visually compete with
   it (a wide labeled pill sitting on every photo did). Positioned against
   .rh-bundle-card (the whole article), not the media box, since it has to
   sit outside .rpc-card__link — lands on the photo's corner regardless,
   because the link's own 10px padding starts right at the card's edge. */
.rh-bundle-card__quick {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 8px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, .85);
  color: var(--wp--preset--color--contrast);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s ease;
  box-shadow: var(--wp--preset--shadow--sm);
  z-index: 1;
}

.rh-bundle-card:hover .rh-bundle-card__quick,
.rh-bundle-card:focus-within .rh-bundle-card__quick {
  opacity: 1;
}

@media (hover: none) {
  /* No hover on touch devices — keep the button reachable/visible always. */
  .rh-bundle-card__quick { opacity: 1; }
}

.rh-bundle-card__btn {
  width: 100%;
  height: var(--wp--custom--button--h--md);
  border-radius: var(--wp--custom--button--radius);
  border: none;
  background: var(--wp--preset--color--primary);
  color: var(--wp--preset--color--base);
  font-weight: var(--wp--custom--button--fw);
  cursor: pointer;
}

.rh-bundle-card__btn[aria-pressed="true"] {
  background: var(--wp--preset--color--base);
  color: var(--wp--preset--color--primary);
  border: 1px solid var(--wp--preset--color--primary);
}

.rh-bundle-card__btn:disabled {
  background: var(--wp--preset--color--muted);
  color: var(--wp--preset--color--text-muted);
  cursor: not-allowed;
}

.rh-bundle-card__btn[aria-busy="true"] { opacity: .6; pointer-events: none; }

.rh-bundle__empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--wp--preset--color--text-muted);
  padding: var(--wp--preset--spacing--60) 0;
}

/* ---- The tracker panel ---------------------------------------------------- */
.rh-bundle__panel {
  flex: 0 0 340px;
  position: sticky;
  top: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface);
  border-radius: var(--wp--custom--radius--lg);
  padding: var(--wp--preset--spacing--40);
  text-align: center;
}

.rh-bundle__panel-title {
  color: var(--wp--preset--color--contrast, #0f1115);
  font-weight: var(--rh-fw-strong);
  margin: 0 0 var(--wp--preset--spacing--40);
}

/* Card prices on this page read washed out in the archive's lighter 'lv'
   card weight (by request: «قیمت‌ها پررنگ بشه») — full-contrast and bold
   here only; the sitewide archive keeps its own weight. */
.rh-bundle .rpa .rpc-price-current,
.rh-bundle .rpa .rpc-price-default {
  color: var(--wp--preset--color--contrast, #0f1115);
  font-weight: var(--rh-fw-strong);
}

.rh-bundle__chain {
  list-style: none;
  margin: 0 0 var(--wp--preset--spacing--40);
  padding: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--wp--preset--spacing--30);
}

.rh-bundle__slot {
  width: 84px;
  height: 84px;
  border: 2px dashed var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius--md);
  /* NOT overflow:hidden — the connector line (below, at 100%) and the ×
     badge (negative inset, see .rh-bundle__slot-remove) both deliberately
     sit OUTSIDE this box; clipping overflow here clips them too. The photo
     is rounded on itself instead (.rh-bundle__slot-img's own border-radius). */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  position: relative;
  font-size: var(--wp--preset--font-size--xs);
  background: var(--wp--preset--color--base);
}

/* "10% off" is Latin (data-no-fa, opted out of the sitewide digit conversion)
   sitting inside an otherwise RTL page — without an explicit direction the
   browser's bidi algorithm can still reorder/reshape the digit+sign+word run
   next to the RTL content around it. Pin it LTR outright (the tpl's own
   dir="ltr" is the primary fix; this is belt-and-suspenders for anywhere the
   markup attribute doesn't reach, e.g. if this class is ever reused). */
.rh-bundle__slot-pct {
  direction: ltr;
  unicode-bidi: isolate;
}

/* Locked/empty slots get a soft frosted-glass treatment (theme-wide
   --rh-glass-* tokens, glass.css) + a shadow so they read clearly against
   the panel's own similarly light background — plain white on
   near-white was the "خیلی خامه" (looks raw/unfinished) complaint,
   especially at the smaller mobile size. Filled slots skip this: a real
   photo already provides all the contrast/definition it needs. */
.rh-bundle__slot:not(.is-filled) {
  background: var(--rh-glass-bg, rgba(255, 255, 255, .65));
  -webkit-backdrop-filter: blur(var(--rh-glass-blur-soft, 14px));
  backdrop-filter: blur(var(--rh-glass-blur-soft, 14px));
  box-shadow: var(--wp--preset--shadow--sm);
}

/* The connecting line lives in the GAP between slots (absolutely positioned,
   not an in-flow child) — putting it in flow inside a fixed-size, centered
   flex box just traps it invisibly behind the lock icon instead of actually
   spanning the space between two slots. Colored once the slot it leaves is
   filled, so the line visually "fills in" as the shopper adds products. */
.rh-bundle__slot:not(:last-child)::after {
  content: '';
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 50%;
  transform: translateX(-50%);
  width: 3px;
  border-radius: var(--wp--custom--radius--pill);
  height: var(--wp--preset--spacing--30);
  background: var(--wp--preset--color--border);
}

.rh-bundle__slot.is-filled:not(:last-child)::after {
  background: var(--wp--preset--color--primary);
}

.rh-bundle__slot--empty { border-style: dashed; }

.rh-bundle__slot.is-filled {
  border-style: solid;
  border-color: var(--wp--preset--color--primary);
}

.rh-bundle__slot.is-unlocked .rh-bundle__slot-lock { display: none; }

/* Once a slot holds a product photo, the lock/percent text underneath it
   would just show through as clutter behind the image — hide both. */
.rh-bundle__slot.is-filled .rh-bundle__slot-lock,
.rh-bundle__slot.is-filled .rh-bundle__slot-pct {
  display: none;
}

.rh-bundle__slot-pct {
  line-height: 1.15;
}

.rh-bundle__slot-lock {
  /* flex: 0 0 22px — NOT just width/height:22px — because as a plain flex
     item (column-direction parent) it was letting flex-shrink squash its
     own height down to ~16px while width stayed 22px, i.e. an ellipse
     instead of a circle ("آیکون قفل دورش دایره نیست"). Forcing the flex
     basis (and disabling shrink/grow) makes it a true, fixed 22×22 square
     no matter how tight the slot gets. */
  flex: 0 0 22px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--wp--preset--color--primary);
  color: var(--wp--preset--color--base);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The crop wrapper — sized to fill the slot, clips to a square regardless of
   the source photo's own aspect ratio. A SIBLING of .rh-bundle__slot-remove,
   not its ancestor, so the × badge (deliberately half outside the slot's
   edge) never gets clipped by this wrapper's own overflow:hidden. */
.rh-bundle__slot-photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: var(--wp--custom--radius--md);
}

.rh-bundle__slot-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.rh-bundle__slot-img[hidden],
.rh-bundle__slot-remove[hidden] {
  display: none;
}

.rh-bundle__slot-remove {
  position: absolute;
  inset-block-start: -7px;
  inset-inline-end: -7px;
  width: 24px;
  height: 24px;
  border: 2px solid var(--wp--preset--color--base);
  border-radius: 50%;
  background: var(--wp--preset--color--danger);
  color: var(--wp--preset--color--base);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1;
  box-shadow: var(--wp--preset--shadow--sm);
}

.rh-bundle__slot-remove svg { width: 13px; height: 13px; }

.rh-bundle__panel-msg {
  /* No min-height: empty (before any selection), this must collapse to
     ~nothing, not reserve a line of blank space — see the CTA-area rules
     below for the fuller "the empty panel had a big dead gap" fix. */
  font-size: var(--wp--preset--font-size--sm);
  color: var(--wp--preset--color--text-muted);
  margin: 0 0 var(--wp--preset--spacing--30);
}

.rh-bundle__panel-msg.has-discount { color: var(--wp--preset--color--success); font-weight: var(--rh-fw-strong); }

/* Price sits ABOVE the button (see bundle.tpl) — old struck (no currency
   word) beside the current price (with the word), one line, centered.
   Keeping it OUT of the button is what keeps the button itself a normal,
   tidy size regardless of label length (a button holding icon + a long
   label + a two-line price at once just read as one oversized, cluttered
   element). */
.rh-bundle__price {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  margin: 0 0 2px;
  /* Explicit — a <p>, so it otherwise picked up the classic base's gray
     paragraph color («قیمت خاکستریه»). */
  color: var(--wp--preset--color--contrast, #0f1115);
}

/* Nothing chosen yet — collapse this whole CTA cluster (message/price/saved)
   down to just the button, instead of three empty paragraphs each still
   holding their own margin. That reserved-but-blank space is what read as
   "a big gap that then jumps full" the moment the first item is picked. */
.rh-bundle__panel-msg:empty,
.rh-bundle__price:empty,
.rh-bundle__cta-saved:empty {
  margin: 0;
}

.rh-bundle__price del {
  color: var(--wp--preset--color--text-muted, #5b6271);
  font-size: var(--wp--preset--font-size--sm);
  text-decoration: line-through;
}

.rh-bundle__price ins {
  font-size: var(--wp--preset--font-size--md);
  font-weight: var(--rh-fw-strong);
  text-decoration: none;
}

/* "در حال محاسبه…" — shown the instant a selection changes, replaced by the
   real (server-formatted) price once action=bundlepreview answers. Purely
   cosmetic feedback; no number is computed here. */
.rh-bundle__price.is-loading {
  font-size: var(--wp--preset--font-size--sm);
  color: var(--wp--preset--color--text-muted);
  font-weight: var(--rh-fw-base);
}

.rh-bundle__cta {
  width: 100%;
  height: var(--wp--custom--button--h--lg);
  padding-inline: 16px;
  border-radius: var(--wp--custom--button--radius);
  border: none;
  background: var(--wp--preset--color--muted);
  color: var(--wp--preset--color--text-muted);
  font-weight: var(--wp--custom--button--fw);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  cursor: not-allowed;
}

.rh-bundle__cta:not(:disabled) {
  background: var(--wp--preset--color--primary);
  color: var(--wp--preset--color--base);
  cursor: pointer;
}

.rh-bundle__cta[aria-busy="true"] {
  opacity: .85;
  pointer-events: none;
  cursor: progress;
}
/* Busy IS disabled (JS sets both together) — without this the button would
   fall back to the plain :disabled muted-gray look for the whole submit,
   reading as "broken" rather than "working" ("لودینگ باشه" was the ask, not
   "greyed out"). */
.rh-bundle__cta[aria-busy="true"]:disabled {
  background: var(--wp--preset--color--primary);
  color: var(--wp--preset--color--base);
}

/* Opt OUT of the sitewide generic busy convention (loading.css:
   `button[aria-busy="true"]{color:transparent !important}` +
   `> *{visibility:hidden}` + a bare ::after ring) — this button's whole
   point while busy is a STILL-READABLE label ("در حال ساخت پک..."), not a
   label hidden behind a spinner. The `!important` on color has to be beaten
   with one of our own — visibility:visible alone left the text invisible
   (correct box, transparent ink, inherited straight through by the label's
   own un-set color). */
.rh-bundle__cta[aria-busy="true"] {
  color: var(--wp--preset--color--base) !important;
}
.rh-bundle__cta[aria-busy="true"] > * { visibility: visible; }
.rh-bundle__cta[aria-busy="true"]::after { content: none; }
.rh-bundle__cta[aria-busy="true"] .rh-bundle__cta-icon { display: none; }

.rh-bundle__cta-spinner {
  display: none;
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: var(--wp--preset--color--base);
  animation: rh-bundle-cta-spin .7s linear infinite;
}
.rh-bundle__cta[aria-busy="true"] .rh-bundle__cta-spinner { display: inline-block; }

@keyframes rh-bundle-cta-spin {
  to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  .rh-bundle__cta-spinner { animation-duration: 1.6s; }
}

.rh-bundle__cta-note {
  margin: 10px 0 0;
  font-size: var(--wp--preset--font-size--xs);
  line-height: 1.6;
  color: var(--wp--preset--color--text-muted);
  text-align: center;
}
.rh-bundle__cta-note a {
  color: var(--wp--preset--color--primary);
  text-decoration: underline;
}

/* Icon + item-count badge, centered on top of the bag glyph. */
.rh-bundle__cta-icon {
  position: relative;
  flex: none;
  width: 20px;
  height: 26px;
}

.rh-bundle__cta-icon svg { display: block; width: 100%; height: 100%; }

.rh-bundle__cta-count {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-top: 5px; /* clear of the handle loop, centered on the bag body */
  font-size: 11px;
  font-weight: var(--rh-fw-strong);
  color: var(--wp--preset--color--primary);
}

.rh-bundle__cta-saved {
  margin: 0 0 12px;
  font-size: var(--wp--preset--font-size--xs);
  font-weight: var(--rh-fw-strong);
  color: var(--wp--preset--color--success);
  text-align: center;
}

/* ---- A pack is already in the cart (bundle-builder.js → applyBundleStatus) --
   The panel shows ONLY that pack + a one-tap remove; everything else in the
   panel (title, chain, message, price, CTA) steps aside until it's gone. */
.rh-bundle.has-existing-bundle .rh-bundle__panel > :not(.rh-bundle__existing):not(.rh-bundle__collapse) {
  display: none !important;
}
.rh-bundle__existing {
  text-align: center;
}
.rh-bundle__existing-title {
  margin: 0 0 12px;
  color: var(--wp--preset--color--contrast, #0f1115);
  font-weight: var(--rh-fw-strong);
  transition: color .25s ease;
}
.rh-bundle__existing-items {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  display: flex;
  justify-content: center;
  gap: 8px;
  transition: opacity .3s ease, transform .3s ease;
}
.rh-bundle__existing-items li {
  width: 52px;
  height: 52px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--wp--preset--color--base, #fff);
  box-shadow: 0 1px 4px rgba(0, 0, 0, .1);
}
.rh-bundle__existing-items img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.rh-bundle__existing-note {
  margin: 0 0 12px;
  font-size: var(--wp--preset--font-size--xs);
  color: var(--wp--preset--color--text-muted, #5b6271);
}
.rh-bundle__existing-cart {
  display: inline-block;
  margin-top: 10px;
  font-size: var(--wp--preset--font-size--sm);
  color: var(--wp--preset--color--contrast, #0f1115);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.rh-bundle__existing-error {
  margin: 8px 0 0;
  font-size: var(--wp--preset--font-size--xs);
  color: #b3261e;
}
/* Removed — confirm in place for a moment before the builder returns. */
.rh-bundle__existing.is-done .rh-bundle__existing-title {
  color: var(--wp--preset--color--success);
}
.rh-bundle__existing.is-done .rh-bundle__existing-items {
  opacity: 0;
  transform: scale(.85);
}
.rh-bundle__existing.is-done .rh-bundle__existing-note,
.rh-bundle__existing.is-done .rh-bundle__existing-cart,
.rh-bundle__existing.is-done .rh-bundle__cta {
  visibility: hidden;
}
/* Tapped «افزودن به پک» while a pack is still in the cart — a small shake
   pointing at what's in the way. */
.rh-bundle__existing.is-nudge {
  animation: rh-bundle-nudge .4s ease;
}
@keyframes rh-bundle-nudge {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-6px); }
  40%      { transform: translateX(6px); }
  60%      { transform: translateX(-4px); }
  80%      { transform: translateX(3px); }
}
/* The builder coming back after the removal. */
.rh-bundle.is-builder-back .rh-bundle__panel > :not(.rh-bundle__collapse) {
  animation: rh-bundle-back-in .45s ease-out both;
}
@keyframes rh-bundle-back-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .rh-bundle__existing.is-nudge,
  .rh-bundle.is-builder-back .rh-bundle__panel > * { animation: none; }
}

/* «آهن‌ربا» add animation (bundle-builder.js → flyToPack): the flying copy
   of the card photo, the slot hiding its own photo until the copy lands,
   and the slot's "gulp" on landing. Both breakpoints. */
.rh-bundle-fly {
  position: fixed;
  z-index: 1001;
  margin: 0;
  object-fit: cover;
  transform-origin: 0 0;
  pointer-events: none;
  will-change: transform, opacity;
  background: var(--wp--preset--color--base, #fff);
}
.rh-bundle__slot.is-incoming .rh-bundle__slot-img,
.rh-bundle__slot.is-incoming .rh-bundle__slot-remove {
  opacity: 0;
}
.rh-bundle__slot.is-landed {
  animation: rh-bundle-slot-gulp .42s cubic-bezier(.3, 1.6, .5, 1);
}
@keyframes rh-bundle-slot-gulp {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.14); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .rh-bundle__slot.is-landed { animation: none; }
}

/* Collapse handle + reopen pill are mobile-only (see the block below). */
.rh-bundle__collapse,
.rh-bundle__reopen,
.rh-bundle__toast {
  display: none;
}

/* ---- Mobile: the panel becomes a fixed bottom bar ------------------------- */
@media (max-width: 900px) {
  .rh-bundle__panel {
    position: fixed;
    /* MUST reset `top` — the desktop rule above sets `top` for its sticky
       behaviour, and a fixed box with BOTH `top` and `bottom` set stretches
       to fill the space between them (auto-height rule for absolutely/fixed
       positioned boxes) instead of sizing to its content. Left in, this
       silently blew the panel up to ~full-viewport height and hid the grid
       underneath it. */
    top: auto;
    inset-inline: 0;
    bottom: 0;
    z-index: 40;
    border-radius: var(--wp--custom--radius--lg) var(--wp--custom--radius--lg) 0 0;
    box-shadow: var(--wp--preset--shadow--lg);
    /* More top/bottom air than the desktop sidebar needs — flush against the
       bar's own top/bottom edge is what read as "stuck to top and bottom"
       (a fixed bar has no page margin around it the way the desktop sidebar
       does, so it needs its OWN breathing room instead). */
    padding-block: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
    /* Frosted glass, same tokens/look as the mobile header (header-glass.css)
       and bottom-nav dock (bottom-nav.css `.rh-bnav`) — a plain solid surface
       here read as out of place next to those (by request: "مانند هدر موبایل
       که شیشه ای هست"). Border-top instead of the desktop rule's none, same
       reason the dock has one: a hairline to separate the glass bar from the
       page scrolling underneath it.
       Values are the owner's own, picked live with a temporary glass tuner:
       clearer than the shared tokens, no visible edge, and a
       stronger shadow in its place (overrides the preset shadow above). */
    background: rgba(255, 255, 255, 0.33);
    -webkit-backdrop-filter: blur(4px) saturate(147%);
    backdrop-filter: blur(4px) saturate(147%);
    border-top: 1px solid transparent;
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.16);
  }
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .rh-bundle__panel { background: rgba(255, 255, 255, 0.96); }
  }

  .rh-bundle__chain {
    flex-direction: row;
    justify-content: center;
    gap: var(--wp--preset--spacing--30);
  }

  /* Was 44px with the percent label hidden — too small to read anything in,
     which is exactly the "raw, nothing written in it" complaint. Big enough
     now to keep the lock icon + "X% تخفیف" both legible. Bumped again to
     68px (from 64px) with padding on BOTH ends (not just the top) — at 64px
     the lock icon sat flush against the top edge and the percent text flush
     against the bottom edge ("چسبیده به بالا و پایین"): the two-line label
     nearly filled the box, leaving no margin on either side even though the
     content itself was centered. */
  .rh-bundle__slot {
    width: 68px;
    height: 68px;
    gap: 6px;
    padding-block: 6px;
  }

  .rh-bundle__slot:not(:last-child)::after {
    inset-block-start: 50%;
    inset-inline-start: 100%;
    transform: translateY(-50%);
    width: var(--wp--preset--spacing--30);
    height: 3px;
  }

  /* Kept, NOT hidden — with no title, the bar was just a row of floating
     circles with no explanation of what they were ("کاربر نمی‌فهمه"). Smaller
     and tighter than the desktop title so it still fits a slim bar. */
  .rh-bundle__panel-title {
    font-size: var(--wp--preset--font-size--md);
    margin: 0 0 10px;
  }

  /* Leave room at the bottom of the page/grid so the fixed bar never covers
     the last row of cards. */
  .rh-bundle { padding-bottom: 170px; }

  /* Legibility on the (very clear) glass bar: busy product photos show
     through behind the text, so every text line gets a soft white halo and
     full contrast. Cheaper and less heavy than a more opaque bar, which the
     owner explicitly tuned away from. */
  .rh-bundle__panel-title {
    color: var(--wp--preset--color--contrast, #0f1115);
  }
  .rh-bundle__panel-title,
  .rh-bundle__panel-msg,
  .rh-bundle__price,
  .rh-bundle__cta-saved,
  .rh-bundle__cta-note {
    text-shadow: 0 0 2px rgba(255, 255, 255, .95), 0 0 12px rgba(255, 255, 255, .9);
  }
  .rh-bundle__price ins {
    font-size: var(--wp--preset--font-size--lg);
  }

  /* ---- Collapse / reopen --------------------------------------------- */
  .rh-bundle__panel {
    transition: transform .28s cubic-bezier(.2, 0, 0, 1), visibility 0s linear 0s;
  }
  .rh-bundle.is-panel-collapsed .rh-bundle__panel {
    transform: translateY(calc(100% + 24px));
    visibility: hidden;
    transition: transform .28s cubic-bezier(.2, 0, 0, 1), visibility 0s linear .28s;
  }
  .rh-bundle.is-panel-collapsed { padding-bottom: 80px; }

  .rh-bundle__collapse {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 10px;
    inset-inline-end: 12px;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .75);
    color: var(--wp--preset--color--contrast, #0f1115);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .12);
    cursor: pointer;
  }

  /* Collapsed pill — slim, and the same clear glass as the bar it stands in
     for (owner-tuned values, see .rh-bundle__panel above) so the two read as
     one control in two states. */
  .rh-bundle__reopen:not([hidden]) {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    position: fixed;
    z-index: 41;
    left: 50%;
    /* Sits on the screen's bottom edge like a tab (owner's own tweak in
       the inspector): flush bottom, only the top corners rounded. The
       safe-area inset goes into padding so the label stays above the
       home indicator. */
    bottom: 0;
    transform: translateX(-50%);
    height: calc(40px + env(safe-area-inset-bottom, 0px));
    padding: 0 14px env(safe-area-inset-bottom, 0px) 10px;
    border: 1px solid rgba(255, 255, 255, .55);
    border-bottom: 0;
    border-radius: 12px 12px 0 0;
    background: rgba(255, 255, 255, .33);
    -webkit-backdrop-filter: blur(4px) saturate(147%);
    backdrop-filter: blur(4px) saturate(147%);
    color: var(--wp--preset--color--contrast, #0f1115);
    font: inherit;
    font-size: var(--wp--preset--font-size--sm);
    font-weight: var(--rh-fw-strong);
    text-shadow: 0 0 2px rgba(255, 255, 255, .95), 0 0 10px rgba(255, 255, 255, .9);
    white-space: nowrap;
    box-shadow: 0 6px 20px rgba(0, 0, 0, .16);
    cursor: pointer;
    animation: rh-bundle-pill-in .25s ease-out;
  }
  .rh-bundle__reopen:not([hidden]):active { transform: translateX(-50%) scale(.96); }
  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .rh-bundle__reopen:not([hidden]) { background: rgba(255, 255, 255, .96); }
  }
  .rh-bundle__reopen-count:empty { display: none; }
  .rh-bundle__reopen-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 999px;
    background: var(--wp--preset--color--primary);
    color: var(--wp--preset--color--base, #fff);
    font-size: var(--wp--preset--font-size--xs);
    text-shadow: none;
  }
  /* Pick/unpick feedback while collapsed (bundle-builder.js → bump()). */
  .rh-bundle__reopen.is-bump { animation: rh-bundle-pill-bump .4s cubic-bezier(.3, 1.6, .5, 1); }
  .rh-bundle__reopen-count.is-bump { animation: rh-bundle-count-bump .45s cubic-bezier(.3, 1.8, .5, 1); }
  @keyframes rh-bundle-pill-in {
    from { opacity: 0; transform: translate(-50%, 12px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
  }
  @keyframes rh-bundle-pill-bump {
    0%, 100% { transform: translateX(-50%) scale(1); }
    40%      { transform: translateX(-50%) scale(1.08); }
  }
  @keyframes rh-bundle-count-bump {
    0%, 100% { transform: scale(1); }
    45%      { transform: scale(1.45); }
  }

  /* Toast above the pill — a slightly firmer glass than the pill so a
     sentence stays readable over product photos. */
  .rh-bundle__toast:not([hidden]) {
    position: fixed;
    z-index: 41;
    left: 50%;
    bottom: calc(52px + env(safe-area-inset-bottom, 0px));
    width: max-content;
    max-width: calc(100vw - 48px);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 8px 16px;
    border: 1px solid rgba(255, 255, 255, .6);
    border-radius: 14px;
    background: rgba(255, 255, 255, .72);
    -webkit-backdrop-filter: blur(10px) saturate(150%);
    backdrop-filter: blur(10px) saturate(150%);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .14);
    color: var(--wp--preset--color--contrast, #0f1115);
    font-size: var(--wp--preset--font-size--sm);
    text-align: center;
    pointer-events: none;
    opacity: 0;
    transform: translate(-50%, 10px) scale(.96);
    transition: opacity .22s ease, transform .3s cubic-bezier(.2, 1.4, .4, 1);
  }
  .rh-bundle__toast.is-in {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
  }
  .rh-bundle__toast-title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: var(--rh-fw-strong);
  }
  .rh-bundle__toast-title svg { color: var(--wp--preset--color--success); }
  /* A new discount tier just unlocked — green, a touch bigger, a little pop. */
  .rh-bundle__toast.is-celebrate {
    background: rgba(236, 248, 240, .85);
    border-color: rgba(46, 160, 90, .35);
  }
  .rh-bundle__toast.is-celebrate .rh-bundle__toast-title {
    color: var(--wp--preset--color--success);
  }
  .rh-bundle__toast.is-celebrate.is-in { animation: rh-bundle-toast-pop .5s cubic-bezier(.3, 1.6, .5, 1); }
  @keyframes rh-bundle-toast-pop {
    0%   { transform: translate(-50%, 10px) scale(.9); }
    60%  { transform: translate(-50%, 0) scale(1.05); }
    100% { transform: translate(-50%, 0) scale(1); }
  }
  @media (prefers-reduced-motion: reduce) {
    .rh-bundle__panel,
    .rh-bundle.is-panel-collapsed .rh-bundle__panel { transition: none; }
    .rh-bundle__reopen:not([hidden]),
    .rh-bundle__reopen.is-bump,
    .rh-bundle__reopen-count.is-bump,
    .rh-bundle__toast.is-celebrate.is-in { animation: none; }
    .rh-bundle__toast:not([hidden]) { transition: opacity .2s linear; }
  }

  /* Sized for a 3-4 column desktop grid; at 2-up on a phone it read as
     oversized relative to the card. */
  .rh-bundle-card__btn {
    height: var(--wp--custom--button--h--sm);
    font-size: var(--wp--preset--font-size--sm);
  }
}
