/* =========================================================================
 * MOMO 5 — fx.css
 * All fx-specific styles for the 7 vanilla effect modules.
 * STAND-ALONE: design tokens are inlined as literal values so this file
 * works regardless of momo.css load order.
 *
 * Tokens: bg #ffffff / #f5f5f7 · ink #1d1d1f · accent #7c3aed
 *         lavender #a78bfa · tint #ede9fe / #f3eefd
 *
 * Rules: no viewport-width units anywhere · svh with vh @supports fallback ·
 * RTL handled via [dir=rtl] overrides and the core.js dir() convention.
 * ========================================================================= */

/* -------------------------------------------------------------------------
 * Shared canvas containers — absolute inset-0, pointer-events none unless
 * the module is interactive (arc handles its own pointer events).
 * ------------------------------------------------------------------------ */
.fx-liquid,
.fx-glbg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.fx-liquid canvas,
.fx-glbg canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  image-rendering: auto;
}

/* CSS fallbacks (no WebGL / low-power / context failure). */
.fx-liquid--fallback {
  background: radial-gradient(120% 90% at 50% 0%, var(--m3-accent-tint-2) 0%, var(--bg-base) 60%);
}

.fx-glbg--fallback {
  background: linear-gradient(180deg, var(--bg-base) 0%, #f3f0ff 55%, var(--m3-accent-tint-2) 100%);
}

.fx-glbg--fallback-cta {
  background: linear-gradient(180deg, var(--bg-base) 0%, #f3f0ff 45%, var(--m3-accent-tint-2) 100%);
}

/* -------------------------------------------------------------------------
 * §1 Vapour text
 * ------------------------------------------------------------------------ */
.fx-vapour {
  position: relative;
  width: 100%;
  height: 160px;
  max-height: 480px;
  margin-inline: auto;
}

@media (min-width: 901px) {
  .fx-vapour { height: 220px; }
}

.fx-vapour canvas {
  display: block;
  margin-inline: auto;
}

/* Screen-reader phrase list — hidden while canvas mode is live. */
.fx-vapour .sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Reduced-motion: canvas removed, static phrase rendered in DOM. */
.fx-vapour--static {
  display: flex;
  align-items: center;
  justify-content: center;
}

.fx-vapour--static .sr-only {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
  white-space: normal;
  font-family: "IBM Plex Sans Arabic", "Inter", sans-serif;
  font-size: 42px;
  font-weight: 700;
  line-height: 1.65;
  letter-spacing: 0;
  color: var(--ink-1);
  text-align: center;
}

@media (max-width: 900px) {
  .fx-vapour--static .sr-only { font-size: 30px; }
}

/* -------------------------------------------------------------------------
 * §3 Shiny button — purple pill with periodic specular sweep.
 * Transform/opacity only; compositor thread.
 * The button SKIN is single-sourced in momo.css (.btn-primary); fx.css owns
 * only the sweep plumbing. This rule provides the positioning context the
 * ::after band clips to.
 * ------------------------------------------------------------------------ */
.fx-shiny-btn {
  position: relative;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}

/* Specular band. */
.fx-shiny-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 40%, rgba(255, 255, 255, 0.55) 50%, transparent 60%);
  transform: translateX(-130%);
  pointer-events: none;
}

/* JS toggles .shine-run on a jittered interval. LTR: sweep left → right. */
.fx-shiny-btn.shine-run::after {
  animation: fx-shine-sweep 900ms ease;
}

@keyframes fx-shine-sweep {
  from { transform: translateX(-130%); }
  to   { transform: translateX(130%); }
}

/* RTL: sign flipped — the band sweeps visually right → left, matching
 * reading direction. */
[dir="rtl"] .fx-shiny-btn::after {
  transform: translateX(130%);
}

[dir="rtl"] .fx-shiny-btn.shine-run::after {
  animation-name: fx-shine-sweep-rtl;
}

@keyframes fx-shine-sweep-rtl {
  from { transform: translateX(130%); }
  to   { transform: translateX(-130%); }
}

/* Hover lift / active press / focus ring — owned by momo.css (.btn-primary). */

/* Reduced-motion: no periodic sweep (JS gates it off); a single sweep is
 * still allowed on hover only. */
@media (prefers-reduced-motion: reduce) {
  .fx-shiny-btn {
    transition: none;
  }
  .fx-shiny-btn.shine-run::after {
    animation: none;
  }
  .fx-shiny-btn:hover::after {
    animation: fx-shine-sweep 900ms ease;
  }
  [dir="rtl"] .fx-shiny-btn:hover::after {
    animation-name: fx-shine-sweep-rtl;
  }
}

/* -------------------------------------------------------------------------
 * §4 Scroll-morph hero — 220svh runway, sticky child, morphing card.
 * (JS sets the exact runway height inline; these are sane defaults.)
 * ------------------------------------------------------------------------ */
.fx-morph {
  position: relative;
  height: 220vh;
}

@supports (height: 100svh) {
  .fx-morph { height: 220svh; }
}

@media (max-width: 900px) {
  .fx-morph { height: 180vh; }
  @supports (height: 100svh) {
    .fx-morph { height: 180svh; }
  }
}

.fx-morph__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

@supports (height: 100svh) {
  .fx-morph__sticky { height: 100svh; }
}

.fx-morph__card {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 28px;
  transform-origin: center center;
  will-change: transform;
}

.fx-morph__card video,
.fx-morph__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  background: transparent;
}

/* Reduced-motion: no runway — card rendered full-bleed, poster only. */
.fx-morph--static {
  height: auto !important;
}

.fx-morph--static .fx-morph__sticky {
  position: static;
}

.fx-morph--static .fx-morph__card {
  transform: none !important;
  clip-path: none !important;
  border-radius: 0 !important;
  will-change: auto;
}

/* -------------------------------------------------------------------------
 * §5 Arc-flow carousel — CSS-3D rotateY + translateZ arc.
 * ------------------------------------------------------------------------ */
.fx-arc {
  position: relative;
  overflow: hidden;
  background: transparent; /* section owns the bg — alternation lives in momo.css */
}

.fx-arc__stage {
  position: relative;
  width: 100%;
  transform-style: preserve-3d;
  perspective: 1200px;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  cursor: grab;
}

.fx-arc__stage.is-dragging {
  cursor: grabbing;
}

.fx-arc__card {
  position: absolute;
  left: 50%;
  top: 50%;
  margin: 0;
  transform: translate(-50%, -50%);
  transform-style: preserve-3d;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--bg-elevated);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 4px 12px rgba(0, 0, 0, 0.05);
  will-change: transform, opacity;
  backface-visibility: hidden;
}

.fx-arc__card img {
  width: 100%;
  height: 100%;
  /* contain, not cover: our sources are product renders on white (1600×1094
     landscape) — cover would crop to a white margin slice in a 62/100 card.
     Contain + white card = seamless Apple-style product plate. */
  object-fit: contain;
  padding: 10px;
  box-sizing: border-box;
  display: block;
  pointer-events: none;
  -webkit-user-drag: none;
}

/* Captions stay in the DOM for the flat/reduced-motion strip, but the 3D arc
   shows imagery only — the img alt already carries the same text for AT. */
.fx-arc__card figcaption { display: none; }

/* Reduced-motion: flat horizontal scroll-snap strip, no 3D, no auto-rotate. */
.fx-arc--flat .fx-arc__stage {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  perspective: none;
  transform-style: flat;
  touch-action: pan-x pan-y;
  height: auto !important;
  padding: 24px;
  cursor: default;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.fx-arc--flat .fx-arc__card {
  position: static;
  flex: 0 0 auto;
  width: min(42%, 320px) !important;
  height: auto !important;
  aspect-ratio: 62 / 100;
  transform: none !important;
  opacity: 1 !important;
  pointer-events: auto !important;
  scroll-snap-align: center;
  will-change: auto;
}

@media (min-width: 901px) {
  .fx-arc--flat .fx-arc__card { width: min(21%, 320px) !important; }
}

/* -------------------------------------------------------------------------
 * §6 Poem — page closer. 300svh runway, sticky stage, layered floats.
 * ------------------------------------------------------------------------ */
.fx-poem {
  position: relative;
  height: 300vh;
  background: var(--bg-base);
}

@supports (height: 100svh) {
  .fx-poem { height: 300svh; }
}

@media (max-width: 900px) {
  .fx-poem { height: 240vh; }
  @supports (height: 100svh) {
    .fx-poem { height: 240svh; }
  }
}

.fx-poem__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

@supports (height: 100svh) {
  .fx-poem__sticky { height: 100svh; }
}

/* Soft background image, kept faint on white (JS drives scale/opacity). */
.fx-poem__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.35;
  transform: scale(1.15);
  pointer-events: none;
  z-index: 0;
}

/* The poem text. Spans are the purple-highlighted words; JS drives their
 * activation. Word-level spans only — never char-level (Arabic shaping).
 * Typography is single-sourced in momo.css (.fx-poem__text, loaded last). */

/* Depth-layered floating product renders. Default scatter positions here
 * (markup carries no inline offsets); JS drives parallax transforms. */
.fx-poem__float {
  position: absolute;
  z-index: 1;
  width: clamp(120px, 18%, 260px);
  height: auto;
  opacity: 0;
  pointer-events: none;
  will-change: transform;
  filter: drop-shadow(0 12px 32px rgba(29, 29, 31, 0.12));
}

.fx-poem__float[data-depth="0.35"] { inset-inline-start: 12%; inset-block-start: 20%; }
.fx-poem__float[data-depth="0.6"]  { inset-inline-start: 78%; inset-block-start: 45%; }
.fx-poem__float[data-depth="0.85"] { inset-inline-start: 30%; inset-block-start: 68%; }

/* CTA settle — hidden until the armed (dynamic) module drives the fade-up. */
.fx-poem--armed .fx-poem__cta,
.fx-poem--armed .fx-shiny-btn {
  opacity: 0;
}

/* Reduced-motion: full text visible with purple spans, static composition,
 * no runway. */
.fx-poem--static {
  height: auto !important;
}

.fx-poem--static .fx-poem__sticky {
  position: static;
  height: auto;
  min-height: 60vh;
  padding-block: 96px;
}

@supports (height: 100svh) {
  .fx-poem--static .fx-poem__sticky { min-height: 60svh; }
}

.fx-poem--static .fx-poem__bg {
  opacity: 0.15 !important;
  transform: none !important;
}

.fx-poem--static .fx-poem__text span {
  opacity: 1 !important;
  filter: none !important;
  color: var(--m3-accent) !important;
}

.fx-poem--static .fx-poem__float {
  opacity: 1 !important;
  transform: none !important;
  will-change: auto;
}

.fx-poem--static .fx-poem__cta,
.fx-poem--static .fx-shiny-btn {
  opacity: 1 !important;
  transform: none !important;
  pointer-events: auto !important;
}

/* -------------------------------------------------------------------------
 * Global reduced-motion belt-and-braces for fx canvases.
 * (Modules also swap modes live via the core.js MediaQueryList.)
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .fx-arc__stage {
    perspective: none;
  }
  .fx-arc__card {
    will-change: auto;
  }
  .fx-morph__card {
    will-change: auto;
  }
  .fx-poem__float {
    will-change: auto;
  }
}
