/* bm-pdp.css: deliberate overrides of Broadcast's product page.
 *
 * HAND-WRITTEN. Unlike bm-custom.css (generated by port-home.py) this file is edited
 * directly. It exists because these rules must reach Broadcast's OWN markup, so they
 * cannot live under the `.ij` scope the ported sections use.
 *
 * Keep it small and keep every rule commented with WHY, because each one is a place a
 * future Broadcast update can collide with us.
 */

/* ---------------------------------------------------------------------------
 * 1. Main image must fit the frame.
 *
 * Broadcast sizes the photo box purely from the image's own aspect ratio:
 *     .product__photo { height: 0; padding-top: calc(1 / var(--aspect-ratio) * 100%) }
 * This store's product shots are 1007x1511 (aspect 0.67), so in a half-width column
 * that resolves to ~150% of the column width (roughly 1050px tall) and the image runs
 * past the viewport and gets cut off. `image_size` only changes the REQUESTED width
 * (small 525 / normal 770 / stretch 970); it never constrains height.
 *
 * So: replace the padding-top ratio box with a real capped height, and switch the image
 * from `cover` to `contain` so the whole garment is visible rather than cropped.
 * ------------------------------------------------------------------------ */

:root {
  --bm-pdp-media-h: min(74vh, 720px);
}

.product__wrapper .product__photo {
  height: var(--bm-pdp-media-h);
  padding-top: 0;
}

.product__wrapper .product__photo .image-wrapper,
.product__wrapper .product__photo .image-wrapper--cover {
  position: absolute;
  inset: 0;
  height: 100%;
  padding-top: 0;
}

/* `contain` is the point of the change: nothing gets cut off. The letterboxing sits on
 * the theme background, which is the same near-white as the studio backdrop. */
.product__wrapper .product__photo .image-wrapper img,
.product__wrapper .product__photo .image-wrapper--cover img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
}

/* The slide stack inherits the same cap so the grid row cannot grow past it. */
.product__images--thumbs .product__slides,
.product__images--mobile-thumbs .product__slides {
  max-height: var(--bm-pdp-media-h);
}

/* ---------------------------------------------------------------------------
 * 2. Thumbnails belong directly beneath the main image, in the image column.
 *
 * The DOM already nests .product__thumbs inside .product__images right after
 * .product__slides, but .product__images is `display: block` in the plain `thumbnails`
 * layout, which leaves the strip free to be pushed around by the surrounding grid.
 * Making the column an explicit flex column pins the order: image, then thumb strip.
 * ------------------------------------------------------------------------ */

.product__wrapper--thumbnails .product__images {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}

/* flex-basis MUST be reset on both children.
 *
 * Broadcast ships `.product__thumbs { flex: 1 1 100% }`, written for its ROW-based
 * thumbnails-left layout where 100% means full WIDTH. Inside a flex COLUMN that same
 * declaration means full HEIGHT, so the column split itself 402/402 and the thumb strip
 * landed on top of the image. Measured at 1440x900: photo ran y=77..743 while thumbs
 * started at y=493. `flex: 0 0 auto` puts both back to content sizing. */
.product__wrapper--thumbnails .product__slides {
  order: 1;
  width: 100%;
  flex: 0 0 auto;
}

.product__wrapper--thumbnails .product__thumbs {
  order: 2;
  width: 100%;
  margin: 14px 0 0;
  float: none;
  position: static;
  flex: 0 0 auto;
  min-height: 0;
}

/* Broadcast already makes the holder a horizontally scrolling strip with hidden
 * scrollbars. Restated here only to guarantee row direction and give the thumbs a
 * consistent size, since the client asked specifically for small boxes left to right. */
.product__wrapper--thumbnails .product__thumbs__holder {
  display: flex;
  flex-flow: row nowrap;
  gap: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-behavior: smooth;
}

.product__wrapper--thumbnails .product__thumbs__holder .product__thumb {
  flex: 0 0 auto;
  width: 78px;
  margin: 0;
}

@media screen and (max-width: 768px) {
  :root {
    --bm-pdp-media-h: min(62vh, 520px);
  }

  .product__wrapper--thumbnails .product__thumbs__holder .product__thumb {
    width: 64px;
  }
}

/* ---------------------------------------------------------------------------
 * 3. Judge.me reviews as an automatic left-to-right loop.
 *
 * The stock widget lays the reviews out as a ragged column-masonry grid: cards of
 * wildly uneven height with big gaps, which is what looked broken. Replaced with a
 * single-row marquee of fixed-width, equal-height cards.
 *
 * bm-reviews-marquee.js wraps the cards in .bm-revmarq__track and appends a duplicate
 * set, so translating exactly -50% returns to the starting frame with no visible seam.
 *
 * INERT since 2026-08-10. The .bm-revmarq class is only ever added by
 * bm-reviews-marquee.js, and that script is no longer loaded; see snippets/bm-head.liquid
 * for why. Every rule below is therefore dead until the loop is rebuilt on Judge.me's own
 * carousel. Kept rather than deleted so the card styling is not lost.
 *
 * Every rule in this block is now scoped to .bm-revmarq, including the sort-wrapper rule,
 * which was previously unscoped and was the block's only surviving effect. See its own
 * comment further down for why that was wrong.
 * ------------------------------------------------------------------------ */

.jdgm-rev-widg__reviews.bm-revmarq {
  position: relative;
  overflow: hidden;
  /* Kill the app's masonry columns. */
  column-count: auto !important;
  columns: auto !important;
  display: block !important;
}

.bm-revmarq .bm-revmarq__track {
  display: flex;
  flex-flow: row nowrap;
  align-items: stretch;
  gap: 16px;
  width: max-content;
  animation: bm-revmarq-scroll var(--bm-revmarq-dur, 60s) linear infinite;
}

/* Let people actually read one. */
.bm-revmarq:hover .bm-revmarq__track,
.bm-revmarq:focus-within .bm-revmarq__track {
  animation-play-state: paused;
}

.bm-revmarq .jdgm-rev {
  flex: 0 0 320px;
  width: 320px;
  min-height: 190px;
  margin: 0 !important;
  padding: 20px !important;
  box-sizing: border-box;
  border: 1px solid var(--border, #e7e7e3);
  background: var(--bg, #fff);
  /* The masonry layout relies on break-inside; neutralise it inside the flex row. */
  break-inside: auto !important;
  display: block !important;
  float: none !important;
}

.bm-revmarq .jdgm-rev__body {
  overflow: hidden;
}

@keyframes bm-revmarq-scroll {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* The sort dropdown and the load-more pager are meaningless once the list is an
 * infinite loop, and the full-width sort bar was part of what read as broken.
 *
 * SCOPED 2026-08-10. The sort-wrapper selector was unscoped, so it survived the marquee
 * being disabled and kept hiding Judge.me's sort dropdown on every PDP. Measured
 * display:none on the draft against display:block on live. Keeping it was the wrong
 * default: the HIDING is the change nobody asked for, so restoring live parity is the
 * neutral position, not preserving an accidental side effect of a disabled feature.
 * Now scoped to .bm-revmarq like the rest of this block, so it is inert until the loop
 * is rebuilt. */
.jdgm-rev-widg:has(.bm-revmarq) .jdgm-rev-widg__sort-wrapper,
.jdgm-rev-widg:has(.bm-revmarq) .jdgm-paginate {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .bm-revmarq .bm-revmarq__track {
    animation: none;
  }
  .jdgm-rev-widg__reviews.bm-revmarq {
    overflow-x: auto;
  }
}

@media screen and (max-width: 768px) {
  .bm-revmarq .jdgm-rev {
    flex-basis: 260px;
    width: 260px;
  }
}

/* --- cart bar: touch target (task 14, 2026-08-10) -----------------------------
 * Broadcast's sticky cart bar is already enabled (show_cart_bar = true on both product
 * templates) and works: it slides in once the buy box scrolls out of view. Its button
 * measured 39px tall at 390 and 50px at 1440, so on mobile the PRIMARY buying control was
 * shorter than the main add-to-cart button 44px above it. This is a full-width button, so
 * unlike the product-card size pills there is no crowding risk in growing it.
 *
 * NOT FIXED HERE, and deliberately: on the ENGLISH storefront this button reads "Configure"
 * while the main button reads "Add to cart". That is Broadcast's own design: the button
 * carries data-cart-bar-scroll and sends you up to the size picker rather than adding blind.
 * The label lives in products.product.configure, which is a VENDOR locale file; the Dutch
 * translator happened to collapse configure and add_to_cart to the same string, which is why
 * /nl/ reads consistently and EN does not. Changing it means a store-level translation
 * override, so it is the client's call, not a theme edit.
 * -------------------------------------------------------------------------- */
.cart-bar .product__submit__add {
  min-height: 44px;
}

/* --- PDP USP row: STATIC (client 2026-08-11: keep it fixed rather than moving) -------
 * The continuous marquee built on 2026-08-11 (task 29) is REMOVED: the client green-circled
 * the static 2x2 grid on his phone and asked for exactly that. No rules needed - with the
 * marquee gone, Broadcast's own flex-wrap 2x2 icon row renders untouched. The two disabled
 * icon blocks in the templates stay disabled (merchant content, the client's call).
 * ------------------------------------------------------------------------------------- */

/* --- PDP service USPs: three icon bullets with a gentle float (the client 2026-08-11) -----------
 * Replaces the plain text service line the client called ugly. Three full-width Broadcast icon
 * blocks directly under add-to-cart; the icons float softly, staggered, which is the
 * "moving icon" he asked for without becoming an attention device.
 *
 * SCOPED BY COUNT: :has(exactly 3 containers) - the OTHER icon row on this page is the
 * 4-item USP marquee, whose own guard requires exactly 4. The two can never both match.
 * PAUSE FOR FREE: the existing delegated tap listener toggles .bm-usp-paused on ANY
 * .block__icon__row, so this row inherits the same WCAG pause mechanism as the marquee.
 * -------------------------------------------------------------------------------------- */
/* the client 2026-08-11: from a gentle float to a proper little bounce (double-dip profile). */
@keyframes ijFloat {
  0%, 55%, 100% { transform: translateY(0); }
  20%           { transform: translateY(-6px); }
  35%           { transform: translateY(0); }
  45%           { transform: translateY(-3px); }
}
.template-product .block__icon__row:has(> .block__icon__container:nth-child(1):nth-last-child(3)) .block__icon {
  animation: ijFloat 2.8s ease-in-out infinite;
  will-change: transform;
}
.template-product .block__icon__row:has(> .block__icon__container:nth-child(1):nth-last-child(3)) > :nth-child(2) .block__icon { animation-delay: -.9s; }
.template-product .block__icon__row:has(> .block__icon__container:nth-child(1):nth-last-child(3)) > :nth-child(3) .block__icon { animation-delay: -1.8s; }

.template-product .block__icon__row.bm-usp-paused .block__icon {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .template-product .block__icon__row .block__icon {
    animation: none !important;
  }
}

/* --- Sticky gallery (the client 2026-08-11): left column pins while the info column scrolls ----
 * Broadcast's own product_sticky_enable is true yet nothing computes position:sticky in the
 * thumbnails layout, so it is done directly. Desktop only - on mobile the columns stack and
 * sticky would trap the gallery over the buy box. top clears the sticky header. The sticky
 * naturally releases at the end of the section: a sticky element never leaves its parent. */
@media (min-width: 1024px) {
  .product__images {
    position: sticky;
    top: 84px;
    align-self: flex-start;
  }
}

/* Related products heading + breathing room (2026-08-11)
 * The vendor renders that heading as a 12px h6 (.tab__title.heading-x-small) and the section
 * starts 0px after the product section - measured. Scoped to the related section only via
 * [id$="__related"]; .tab__title elsewhere (the description tabs) is untouched. */
.template-product .shopify-section[id$="__related"] {
  padding-top: clamp(56px, 7vw, 96px);
}
.template-product .shopify-section[id$="__related"] .tab__title {
  font-size: clamp(1.4rem, 2.4vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.1;
  margin-bottom: 40px;
}

/* Sticky ATC follows the WHOLE product page.
 * `bm-visible` is toggled by bm-cart-bar.js whenever the real buy button is off-screen.
 * It mirrors the vendor's `.is-visible` so either class shows the bar - the vendor's own
 * scroll toggle is never fought, only complemented. */
.cart-bar.bm-visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s linear 0s;
}
