/******************************
 Marquee module
 ------------------------------
 Loaded on demand by ControllerJournal3Marquee::afterRender(). It lives outside
 style.min.css deliberately: this repo ships no SCSS source for the theme, so
 the compiled bundle cannot be regenerated. Fold these rules into
 scss/modules/marquee.scss if the theme build is ever restored.

 Everything the merchant can tune arrives as a custom property written by the
 schema (system/library/journal3/data/settings/module/marquee/*.json); the
 values below are only fallbacks.
*******************************/

.module-marquee {
  --marquee-duration: 30s;
  --marquee-delay: 0s;
  --marquee-play: running;
  --marquee-hover-factor: 1;
  --item-gap: 24px;
  --row-gap: 12px;
  --media-gap: 8px;
  --text-gap: 2px;
  --fader-color: var(--background-color, #fff);
  position: relative;
}

/* Fade widths are resolved once here so the gradient overlays and the mask mode
   can share them, and so `off` genuinely costs nothing. */
.marquee-fade-off { --fade-s: 0px; --fade-e: 0px; }
.marquee-fade-both { --fade-s: var(--fader-width-left, 120px); --fade-e: var(--fader-width-right, 120px); }
.marquee-fade-start { --fade-s: var(--fader-width-left, 120px); --fade-e: 0px; }
.marquee-fade-end { --fade-s: 0px; --fade-e: var(--fader-width-right, 120px); }

.module-marquee .marquee {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--row-gap);
  margin: 0 auto;
}

.marquee-axis-y .marquee {
  flex-direction: row;
}

.marquee-row {
  display: flex;
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.marquee-axis-y .marquee-row {
  height: var(--marquee-height, 240px);
  overflow: hidden;
}

/* ---- the animated track --------------------------------------------------
   N identical tracks tile the row and each translates by exactly its own
   width/height, so the loop is seamless for any number of tracks — no
   "swap" keyframe needed. `animation-name` and the --dir-* pair come from the
   schema's `direction` rules (which carry rtlRules, so left/right mirror).
   Longhands, not the shorthand: the duration has to multiply by the hover
   factor, and the shorthand would reset animation-name set by the schema. */
/* The duration is FIXED for the life of the animation. Changing it mid-flight
   is what "slow down on hover" used to do, and CSS derives progress as
   elapsed ÷ duration — so stretching the duration re-maps the same elapsed time
   onto a much earlier point in the loop and the track visibly snaps backwards.
   Speed changes go through the Web Animations API's playback rate instead
   (marquee.js), which preserves the current position. */
.marquee-track {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  animation-name: marquee-x;
  animation-duration: var(--marquee-duration);
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-delay: var(--marquee-delay);
  animation-play-state: var(--marquee-play);
  animation-direction: var(--dir-a, normal);
  will-change: transform;
}

.marquee-row-alt .marquee-track {
  animation-direction: var(--dir-b, reverse);
}

@keyframes marquee-x {
  to { transform: translate3d(-100%, 0, 0); }
}

/* RTL tiles the tracks from the row's RIGHT edge leftward, so the extra tracks
   sit to the LEFT and only a RIGHTWARD shift stays covered. Travelling to
   -100% moves track 1 off its own start slot with nothing behind it, leaving a
   bare strip at the right edge for the whole loop — the RTL-only "words vanish
   at the right" bug. Keeping the shift in [0, +100%] is covered at both ends:
   the right edge by track 1 itself, the left by tracks 2..n (which the
   auto-fill condition n*T >= C + T guarantees). `animation-direction` then
   picks which way it travels within that safe range. */
@keyframes marquee-x-rtl {
  to { transform: translate3d(100%, 0, 0); }
}

@keyframes marquee-y {
  to { transform: translate3d(0, -100%, 0); }
}

/* ---- items --------------------------------------------------------------
   The gap is a trailing margin rather than `gap`, because `gap` would not
   separate the last item of one track from the first item of the next — the
   seam would visibly close up once per loop. */
.marquee-item {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--media-gap);
  min-width: var(--item-min-width, auto);
  max-width: var(--item-max-width, none);
  text-decoration: none !important;
  transition: transform 200ms ease-out, background-color 200ms ease-out,
              border-color 200ms ease-out, box-shadow 200ms ease-out;
}

.marquee-axis-x .marquee-item { margin-inline-end: var(--item-gap); }
.marquee-axis-y .marquee-item { margin-block-end: var(--item-gap); }

.marquee-item-media {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}

.marquee-item-img {
  display: block;
  width: var(--img-w, auto);
  height: var(--img-h, auto);
  max-width: none;
  transition: opacity 200ms ease-out, filter 200ms ease-out;
}

.marquee-item-img-hover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}

.module-marquee .has-media-hover:hover .marquee-item-img-hover { opacity: 1; }
.module-marquee .has-media-hover:hover .marquee-item-img:not(.marquee-item-img-hover) { opacity: 0; }

.marquee-item-content {
  display: flex;
  flex-direction: column;
  gap: var(--text-gap);
  min-width: 0;
}

/* `plaintext` takes each label's direction from its own first strong character
   instead of the page's. Without it an English string on an RTL store is
   reordered by the bidi algorithm — "30-Day Returns" renders "Day Returns-30" —
   while hardcoding `direction: ltr` would break the Arabic copy it exists for. */
.marquee-item-title,
.marquee-item-subtitle {
  display: block;
  white-space: nowrap;
  unicode-bidi: plaintext;
}

.marquee-item-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.marquee-item-icon::before {
  display: inline-block;
  line-height: 1;
}

/* ---- divider between items ----------------------------------------------
   `content` is deliberately NOT declared for the icon/text modes: the schema
   writes it (Icon emits the glyph codepoint, dividerText a literal string), and
   a pseudo-element with no content never renders — which is exactly what
   should happen when the merchant picks a mode but configures no glyph. */
.marquee-item::after {
  display: none;
}

.marquee-divider-icon .marquee-item::after,
.marquee-divider-text .marquee-item::after {
  display: inline-flex;
  align-items: center;
  align-self: center;
  line-height: 1;
  vertical-align: middle;
  margin-inline-start: var(--item-gap);
}

.marquee-divider-line .marquee-item::after,
.marquee-divider-image .marquee-item::after {
  content: "";
  display: inline-block;
  flex: 0 0 auto;
  align-self: center;
  margin-inline-start: var(--item-gap);
}

.marquee-divider-line .marquee-item::after {
  width: var(--divider-width, 1px);
  height: var(--divider-height, 1em);
  background: currentColor;
}

.marquee-divider-image .marquee-item::after {
  width: var(--divider-width, 16px);
  height: var(--divider-height, 16px);
  background-image: var(--divider-image);
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.marquee-item-divider-glyph {
  display: inline-block;
  width: var(--divider-width, 1px);
  height: var(--divider-height, 1em);
  background: currentColor;
}

/* ---- edge fade ----------------------------------------------------------- */
.marquee-fade-mode-gradient .marquee::before,
.marquee-fade-mode-gradient .marquee::after {
  content: "";
  position: absolute;
  pointer-events: none;
  z-index: 2;
  border-radius: inherit;
}

.marquee-fade-mode-gradient.marquee-axis-x .marquee::before {
  inset-block: 0;
  left: 0;
  width: min(var(--fade-s), 40%);
  background-image: linear-gradient(to right, var(--fader-color), rgba(255, 255, 255, 0));
}

.marquee-fade-mode-gradient.marquee-axis-x .marquee::after {
  inset-block: 0;
  right: 0;
  width: min(var(--fade-e), 40%);
  background-image: linear-gradient(to left, var(--fader-color), rgba(255, 255, 255, 0));
}

.marquee-fade-mode-gradient.marquee-axis-y .marquee::before {
  inset-inline: 0;
  top: 0;
  height: min(var(--fade-s), 40%);
  background-image: linear-gradient(to bottom, var(--fader-color), rgba(255, 255, 255, 0));
}

.marquee-fade-mode-gradient.marquee-axis-y .marquee::after {
  inset-inline: 0;
  bottom: 0;
  height: min(var(--fade-e), 40%);
  background-image: linear-gradient(to top, var(--fader-color), rgba(255, 255, 255, 0));
}

/* Mask mode fades to whatever is actually behind the marquee (an image, a
   gradient, a video), where the overlay above can only fake a solid colour. */
.marquee-fade-mode-mask.marquee-axis-x .marquee {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-s), #000 calc(100% - var(--fade-e)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-s), #000 calc(100% - var(--fade-e)), transparent 100%);
}

.marquee-fade-mode-mask.marquee-axis-y .marquee {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-s), #000 calc(100% - var(--fade-e)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--fade-s), #000 calc(100% - var(--fade-e)), transparent 100%);
}

/* `start`/`end` are reading-order edges, so under RTL they swap physical sides.
   The mask/gradient geometry above is physical, so swap the values instead of
   duplicating every rule. */
html[dir="rtl"] .marquee-fade-start { --fade-s: 0px; --fade-e: var(--fader-width-left, 120px); }
html[dir="rtl"] .marquee-fade-end { --fade-s: var(--fader-width-right, 120px); --fade-e: 0px; }

/* ---- JS-driven rows ------------------------------------------------------
   Only drag / scroll-linked motion / hover-reverse need frame-by-frame control.
   Those rows drop the CSS animation and take a shared transform instead: every
   track in the row moves by the same amount, and because the tracks are
   identical, resetting the offset by one track size is visually invisible —
   which is what makes the wrap seamless without a wrapper element. */
.marquee-row.is-js-driven .marquee-track {
  animation: none !important;
  transform: translate3d(var(--js-x, 0px), var(--js-y, 0px), 0);
}

/* ---- interaction states -------------------------------------------------- */
.module-marquee .marquee.is-dragging {
  cursor: grabbing;
}

.module-marquee .marquee.is-dragging .marquee-track {
  animation-play-state: paused;
}

.module-marquee .marquee.is-dragging a {
  pointer-events: none;
}

/* The button is server-rendered `hidden` and unhidden by marquee.js, so a
   visitor without JS is never shown a control that cannot work. */
.module-marquee .marquee-toggle[hidden] {
  display: none;
}

/* Centred and clamped to the module's own height: a marquee is often a single
   24px line, and a fixed 32px control anchored to the bottom spills out of the
   bar. It still floats OVER the moving content by design — pair it with the
   edge fade if the overlap reads badly. */
.module-marquee .marquee-toggle {
  position: absolute;
  inset-inline-end: 8px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: min(32px, 100%);
  height: min(32px, 100%);
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, .45);
  cursor: pointer;
}

.module-marquee .marquee-toggle-icon {
  width: 10px;
  height: 12px;
  border-inline-start: 3px solid #fff;
  border-inline-end: 3px solid #fff;
}

.module-marquee.is-paused .marquee-toggle-icon {
  width: 0;
  height: 0;
  border: 6px solid transparent;
  border-inline-start: 10px solid #fff;
  border-inline-end: 0;
}

/* ---- reduced motion ------------------------------------------------------
   The Option system can only emit width-based media queries, so `reducedMotion`
   is a php:true value the controller turns into a root class and the real
   media query lives here. */
@media (prefers-reduced-motion: reduce) {
  .marquee-rm-pause .marquee-track {
    animation-play-state: paused !important;
  }

  .marquee-rm-static .marquee-track {
    animation: none !important;
  }

  .marquee-rm-static .marquee-track[data-clone] {
    display: none !important;
  }
}
