/* ==========================================================================
   Product detail — digital-product page (AE scripts, AE/C4D project files,
   tutorials). Two-column top block (media gallery + sticky purchase panel),
   then a full-width description/spec/review/related stack below. Built to
   handle either an image OR a video in the gallery, and a generic spec
   list so it works for any product type without redesigning per-item.
   ========================================================================== */

/* padding-top matches every other inner page's own top section (Shop,
   Blog, Portfolio, the Blog article template) — var(--space-16), the
   same fixed gap below .page-header's own hairline everywhere. */
.product {
  padding: var(--space-16) 0;
  background: var(--color-bg);
}

/* 1.1fr / 0.9fr AND gap: var(--space-8) are the exact same split css/
   shop.css uses for its featured banner (.shop__featured) — both the
   ratio and the gap have to match for the gallery column below to render
   at the same pixel width as that banner's own picture (a different gap
   eats into the same 1.1fr/0.9fr split differently, which is genuinely
   what was causing a several-px mismatch here before — not just
   rounding). align-items: start (overriding grid's stretch default) is
   deliberate: .product__panel's own content (title/price/specs/...) is
   naturally taller than the gallery's image+thumbnails, so plain stretch
   would size BOTH columns off the panel's height instead — leaving dead
   space below the gallery's own content while the panel filled the row
   exactly. js/product-panel-height.js measures the gallery's real
   (unstretched) height and sets it as the panel's own height directly,
   so the panel's bottom edge lands exactly on the small shots' bottom
   edge instead of wherever the row happened to end. */
.product__top {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: var(--space-8);
  align-items: start;
}

/* --- Gallery --- */

.product__gallery-main {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
}

/* Overlaid on the picture itself, same dark-glass recipe as
   .shop__card-badge (components.css) — matches how every other card's
   category tag sits on its own thumbnail across the site, instead of
   living in its own row up in the purchase panel. z-index: 1 keeps it
   above whichever .product__gallery-slide is currently fading in/out. */
.product__gallery-badge {
  position: absolute;
  z-index: 1;
  top: var(--space-3);
  left: var(--space-3);
  display: inline-flex;
  height: 26px;
  padding: 0 12px;
  align-items: center;
  border-radius: var(--radius-sm);
  background: rgba(5, 5, 6, 0.72);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text);
  /* Category/type label — chrome, not readable content, so not selectable. */
  -webkit-user-select: none;
  user-select: none;
}

/* Mirrors .product__gallery-badge on the opposite corner — same
   dark-glass recipe, icon only. Not nested in a link (unlike the Shop
   grid's own card version of this), so no click-through to prevent.
   The resting ring is a box-shadow, not a real border — same reasoning
   as .filter-pill/.pagination__btn (css/filter-pill.css): a same-width
   transparent border sitting on top of a gradient background rendered as
   a stray edge stripe on some browsers/zoom levels, which is exactly
   what the hover state below switches this to a gradient background —
   box-shadow doesn't have that failure mode. */
.product__gallery-share {
  position: absolute;
  z-index: 1;
  top: var(--space-3);
  right: var(--space-3);
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  background: rgba(5, 5, 6, 0.72);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.14);
  color: var(--color-text);
  cursor: pointer;
  /* No square tap flash on phones — the button already signals the tap by
     swapping to the gradient (.is-copied); the browser's default tap
     highlight box on top of that just looked like a stray selection. */
  -webkit-tap-highlight-color: transparent;
  transition: background var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}

/* Drop the default focus ring on pointer/tap, keep a real one for keyboard
   users only (matches the .filter-pill focus recipe). */
.product__gallery-share:focus {
  outline: none;
}

.product__gallery-share:focus-visible {
  outline: 2px solid var(--color-orange);
  outline-offset: 2px;
}

/* On hover this switches to the same brand-gradient recipe as .btn--cta
   (header.css) / .filter-pill.is-active, rather than just a lighter
   glass tint — matches how every other "active/engaged" control on the
   site reads (Buy Now, an active filter pill, a selected page number).
   color:#fff (not just var(--color-text)) makes the icon unambiguously
   white against the gradient rather than the theme's slightly warm
   off-white. */
.product__gallery-share:hover {
  background: var(--gradient-brand-btn);
  box-shadow: var(--shadow-brand);
  color: #fff;
}

.product__gallery-share svg {
  width: 14px;
  height: 14px;
}

/* Copied confirmation (js/share.js): the icon swaps to a check and the
   button flashes the brand gradient with a quick pop. */
.product__gallery-share.is-copied {
  background: var(--gradient-brand-btn);
  box-shadow: var(--shadow-brand);
  color: #fff;
  animation: sharePop 320ms var(--ease-standard);
}

@keyframes sharePop {
  0% { transform: scale(1); }
  40% { transform: scale(1.12); }
  100% { transform: scale(1); }
}

.product__gallery-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 450ms var(--ease-standard);
}

.product__gallery-slide.is-active {
  opacity: 1;
  pointer-events: auto;
}

.product__gallery-slide img,
.product__gallery-slide video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.product__gallery-play {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 74px;
  height: 56px;
  margin: -28px 0 0 -37px;
  border-radius: var(--radius-sm);
  background: var(--gradient-brand-btn);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.16);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  transition: opacity 300ms var(--ease-standard);
}

.product__gallery-play svg {
  width: 22px;
  height: 22px;
  margin-left: 3px;
}

.product__gallery-slide.is-playing .product__gallery-play {
  opacity: 0;
}

/* Extra product shots — same 16:9 ratio as the main gallery image above,
   4 across so the row spans that same column width. Also the gallery's
   thumbnail strip (js/product-gallery.js) — clicking one crossfades the
   main image into that same shot via .product__gallery-slide's existing
   opacity transition, so shot 1 and the main image start out identical
   (both work-01.jpg) since there are only 4 real photos/clips total to
   show, split between "big" and "small". */
.product__extra-shots {
  margin-top: var(--space-3);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-2);
}

.product__extra-shot {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1.5px solid transparent;
  background: var(--color-bg-elevated);
  transition: border-color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
}

.product__extra-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Plain translucent-white border, no glow — the same --color-border-strong
   token the Shop grid's own card hover uses (components.css
   .shop__card:hover), for one consistent "highlighted" border color
   across the site rather than a one-off accent. */
.product__extra-shot.is-active,
.product__extra-shot:hover {
  border-color: var(--color-border-strong);
}

.product__extra-shot-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 5, 6, 0.36);
}

.product__extra-shot-play svg {
  width: 16px;
  height: 16px;
  fill: #fff;
}

/* --- Purchase panel --- */

/* Height is set inline, in px, by js/product-panel-height.js — see the
   note on .product__top above for why this isn't plain CSS stretch.
   overflow-y: auto is a safety net for a product whose spec list (or
   any other panel content) genuinely doesn't fit the gallery's own
   height — it scrolls internally rather than pushing the panel taller
   than the picture it has to match. */
/* No internal scroll: js/product-panel-height.js only matches the gallery
   height while the content fits, otherwise the panel keeps its natural
   height and grows. So there's never anything to scroll here. */
.product__panel {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  border-radius: var(--radius-md);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
}

.product__title {
  font-size: 26px;
  line-height: 1.2;
}

.product__rating {
  margin-top: var(--space-3);
  display: flex;
  align-items: center;
  gap: 6px;
}

.product__rating-star {
  width: 14px;
  height: 14px;
}

.product__rating-text {
  font-size: 13px;
  color: var(--color-text-faint);
}

.product__rating-text a {
  color: var(--color-text-muted);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.product__desc {
  margin-top: var(--space-4);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* margin-top: auto pushes this row — and .product__buy/.product__specs
   right after it, in normal flow — all the way to the bottom of the
   panel, regardless of how short .product__desc above happens to be.
   Same "pin the fixed-content group to the bottom" recipe as
   .shop__card-rating in css/shop.css. */
.product__price-row {
  margin-top: auto;
  padding-top: var(--space-5);
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

/* When a product has NO price row (e.g. an unpriced/free item), the price row
   can't be the auto-margin anchor — so pin the button (the first bottom element
   then) to the bottom instead, keeping the "free space above, button + specs at
   the bottom" behaviour for every product. */
.product__panel:not(:has(.product__price-row)) .product__buy {
  margin-top: auto;
}

/* 48px matches css/shop.css's .shop__featured-price exactly — same
   banner-price size, since this panel plays the same role here. */
.product__price {
  font-family: var(--font-heading);
  font-size: 48px;
  font-weight: 700;
  background: var(--gradient-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.product__buy {
  margin-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.product__buy .btn {
  width: 100%;
}

/* Generic key/value spec list — works for any product type (software,
   file size, format, license, resolution, whatever's relevant). */
.product__specs {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.product__spec-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: 13px;
}

.product__spec-label {
  color: var(--color-text-faint);
}

.product__spec-value {
  color: var(--color-text);
  font-weight: 600;
  text-align: right;
}

/* --- Below-the-fold stack --- */

/* Plain full-width blocks below .product__top — max-width caps a
   comfortable reading width for prose; "You might also like" opts out
   where it genuinely needs the fuller width (inline style). */
.product__section {
  margin-top: var(--space-16);
  max-width: 860px;
}

/* Same width as .product__gallery-main above — literally the same
   1.1fr-of-(100% - gap) formula .product__top uses to size that column
   (see its own comment), so Reviews' right edge lines up with the
   picture's right edge instead of landing at the generic 860px prose
   width every other section uses. */
#product-reviews {
  max-width: calc((100% - var(--space-8)) * 1.1 / 2);
}

.product__section-title {
  font-size: 26px;
  margin-bottom: var(--space-5);
}

/* Headings above card content (Reviews list, "You might also like" carousel)
   get 24px instead of 20 so the gap LOOKS the same as the 20px above the
   "About this script" body text — cards start flush with no line-leading.
   See the same note on .services__section-title. */
#product-reviews .product__section-title,
.product__section:has(.product__related-carousel) .product__section-title {
  margin-bottom: var(--space-6);
}

.product__body {
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--color-text-muted);
}

.product__body p {
  margin-bottom: var(--space-4);
}

.product__includes {
  margin-top: var(--space-6);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-6);
}

.product__includes-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--fs-small);
  color: var(--color-text-muted);
}

.product__includes-item svg {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 2px;
  color: var(--check, #8a5cff);
}
/* Text stays one flex item so inline formatting (bold/colour/link on a word)
   flows inline instead of splitting into its own cell. */
.product__includes-text {
  min-width: 0;
}

/* --- Reviews (visual language borrowed from the pitch-section review
   cards, reproduced under this page's own class names) --- */

.product__reviews {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.product__review {
  padding: var(--space-4) var(--space-5);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.06);
}

.product__review-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.product__review-author {
  font-size: 14px;
  font-weight: 700;
  color: var(--color-text);
}

.product__review-stars {
  display: flex;
  gap: 3px;
}

.product__review-stars svg {
  width: 12px;
  height: 12px;
}

.product__review-text {
  margin-top: var(--space-2);
  font-size: 14px;
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* --- Related products carousel ---
   Same visual language as the Shop grid's own cards (css/shop.css) —
   badge, title, rating pinned to the bottom, price + Buy button — just
   narrower, since 5 need to fit across the column at once instead of 4
   across the full page width. js/product-related-carousel.js drives the
   actual sliding: every 5s (or on an arrow click) the track moves by
   exactly one card's width, right-to-left, looping seamlessly by
   cloning the first few cards onto the end. */

.product__related-carousel {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.product__related-arrow {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  background: var(--color-bg-elevated);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard);
}

.product__related-arrow:hover {
  background: var(--color-bg-elevated-hover);
  color: var(--color-text);
  border-color: var(--color-border-strong);
}

.product__related-arrow svg {
  width: 20px;
  height: 20px;
}

/* container-type lets .product__related-card size itself off THIS box's
   width (via cqw, below) rather than the track's — the track is wider
   than the viewport (it holds every card end to end), so a plain
   percentage on the card would resolve against that oversized width
   instead of the visible 5-card window. */
.product__related-viewport {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  container-type: inline-size;
}

/* transition is long and eased both in and out (not the sharper
   --ease-standard) — a soft, unhurried glide rather than a quick snap,
   matching the Shop banner's own slide. */
.product__related-track {
  display: flex;
  gap: var(--space-4);
  transition: transform 900ms ease-in-out;
}

/* Exactly 5 fit the viewport width (4 gaps between them) — cqw resolves
   against .product__related-viewport above, not this track. */
.product__related-card {
  flex: 0 0 calc((100cqw - 4 * var(--space-4)) / 5);
  display: flex;
  flex-direction: column;
  min-height: 230px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  transition: border-color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
}

/* Border-only highlight — no lift, no shadow, no image zoom (unlike the
   shared card hover-lift recipe in components.css, which this card is
   deliberately excluded from). The carousel can be mid-slide under the
   cursor, so a card that also jumped/zoomed on hover would read as
   fighting the slide animation; a plain border makes "this one's
   focused" clear without adding movement of its own. Same
   --color-border-strong token the Shop grid's own card hover uses
   (components.css .shop__card:hover) — see the identical note on
   .product__extra-shot.is-active in this file.
   .product__related-badge is still defined once in css/components.css,
   shared with the equivalent Shop/Services/Blog card recipes. */
.product__related-card:hover {
  border-color: var(--color-border-strong);
}

/* Own 16:9 ratio (see the note on this in components.css) — matches the
   Shop grid card and the gallery above rather than the shared 4:3 used
   by Product/Services "related" strips elsewhere. */
.product__related-media {
  aspect-ratio: 16 / 9;
}

.product__related-body {
  padding: var(--space-3) var(--space-3) var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}

.product__related-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--color-text);
  line-height: 1.3;
}

/* margin-top: auto docks rating (and .product__related-footer right
   after it) to the bottom of the card — same fixed-position-regardless-
   of-title-length recipe as .shop__card-rating in css/shop.css. */
.product__related-rating {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: auto;
}

.product__related-star {
  width: 11px;
  height: 11px;
}

.product__related-footer {
  margin-top: var(--space-1);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.product__related-price {
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: 700;
  background: var(--gradient-brand);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.product__related-buy {
  height: 30px;
  padding: 0 var(--space-3);
  gap: 5px;
  font-size: 11px;
  white-space: nowrap;
}

.product__related-buy svg {
  width: 13px;
  height: 13px;
}

@media (max-width: 980px) {
  .product__top {
    grid-template-columns: 1fr;
  }

  /* Stacked: the purchase panel (title/price/specs/buy) moves to full width
     ABOVE the gallery — order:-1 lifts it past the gallery, which is first
     in the DOM. */
  .product__panel {
    order: -1;
  }

  .product__includes {
    grid-template-columns: 1fr;
  }

  /* Reviews drop their desktop half-width cap (which aligned them under the
     gallery column) and go full width once the layout has stacked. */
  #product-reviews {
    max-width: none;
  }
}

/* "You might also like" carousel: reduce how many cards show at once as the
   screen narrows — the same responsive idea as the Explainer Video spotlight
   strip (css/services.css). Desktop shows 5; below that: 3 / 2 / 1. Without
   this, 5 cards stayed crammed into a phone's width and rendered unusably
   small. (The carousel JS slides one card at a time and clones 5 either
   side, so a smaller visible count needs no JS change.) */
@media (max-width: 1024px) {
  .product__related-card {
    flex-basis: calc((100cqw - 2 * var(--space-4)) / 3);
  }
}

@media (max-width: 767px) {
  .product__related-card {
    flex-basis: calc((100cqw - var(--space-4)) / 2);
  }
}

@media (max-width: 480px) {
  .product__related-card {
    flex-basis: 100cqw;
  }
}

/* Description constructor under the gallery — the renderme/section blocks span
   the SAME working width as the gallery+panel block above (the product page
   container), instead of the section builder's own narrower default. The
   section adds its own max-width + side padding; zero them here so the content
   lines up with the top block. (Reviews/checklist keep their own tuned widths.) */
.product__body .rm-section {
  max-width: none;
  padding-inline: 0;
}
.product__body .rm-section__full > .rm-el > .about__title,
.product__body .rm-section__full > .rm-el > .about__text,
.product__body .rm-section__full > .rm-el > .about__prose,
.product__body .rm-section__full > .rm-el > .about__list,
.product__body .rm-section__full > .rm-el > .about__quote {
  max-width: none;
}

/* Carousel block on a product page: break it out to full width like on the
   services/blog pages (its .services__spotlight-inner keeps the content centred).
   MAIN uses overflow-x: clip, so 100vw creates no horizontal scroll. */
.product__body .services__spotlight {
  width: 100vw;
  margin-left: calc(50% - 50vw);
}
