/* ── kaprera · shared motion layer ───────────────────────────────────────
   Loaded on every page next to motion.js. Everything that hides content
   sits under html.motion, which motion.js adds only when it runs and the
   visitor hasn't asked for reduced motion, so a failed script or a
   reduce preference always leaves a complete, static page.

   Uses the individual `translate` / `rotate` / `scale` properties rather
   than `transform`, so parallax, magnetic pull and tilt compose with the
   hover lifts and keyframe drifts the pages already run on `transform`. */

:root { --m-ease: cubic-bezier(0.22, 1, 0.36, 1); --m-dur: .8s; --m-step: 80ms; }

/* side-entering reveals start outside the column; clip (not hidden) so no
   scroll container appears and sticky children keep working */
html.motion main { overflow-x: clip; }

/* ── scroll reveals ──
   [data-m="up|fade|scale|start|end|blur|mask"] reveals one element;
   [data-m-stagger="<variant>"] reveals its children one after another.
   motion.js adds .m-in on entry and .m-done once settled, which drops
   these rules so the element's own transitions (hover etc.) take over. */
html.motion [data-m]:not(.m-done),
html.motion [data-m-stagger]:not(.m-done) > * {
  transition:
    opacity var(--m-dur) var(--m-ease),
    translate var(--m-dur) var(--m-ease),
    scale var(--m-dur) var(--m-ease),
    filter var(--m-dur) var(--m-ease);
  transition-delay: calc(var(--m-i, 0) * var(--m-step) + var(--m-d, 0ms));
}
html.motion [data-m]:not(.m-in):not(.m-done),
html.motion [data-m-stagger]:not(.m-in):not(.m-done) > * { opacity: 0; translate: 0 26px; }

html.motion [data-m="fade"]:not(.m-in):not(.m-done),
html.motion [data-m-stagger="fade"]:not(.m-in):not(.m-done) > * { translate: none; }

html.motion [data-m="scale"]:not(.m-in):not(.m-done),
html.motion [data-m-stagger="scale"]:not(.m-in):not(.m-done) > * { translate: 0 18px; scale: .94; }

/* start/end follow the reading direction, so Arabic slides from the right */
html.motion [data-m="start"]:not(.m-in):not(.m-done),
html.motion [data-m-stagger="start"]:not(.m-in):not(.m-done) > * { translate: -34px 0; }
html.motion [data-m="end"]:not(.m-in):not(.m-done),
html.motion [data-m-stagger="end"]:not(.m-in):not(.m-done) > * { translate: 34px 0; }
html.motion[dir="rtl"] [data-m="start"]:not(.m-in):not(.m-done),
html.motion[dir="rtl"] [data-m-stagger="start"]:not(.m-in):not(.m-done) > * { translate: 34px 0; }
html.motion[dir="rtl"] [data-m="end"]:not(.m-in):not(.m-done),
html.motion[dir="rtl"] [data-m-stagger="end"]:not(.m-in):not(.m-done) > * { translate: -34px 0; }

html.motion [data-m="blur"]:not(.m-in):not(.m-done),
html.motion [data-m-stagger="blur"]:not(.m-in):not(.m-done) > * { translate: 0 14px; filter: blur(10px); }

/* mask: the block rises out of a soft wipe, like type coming up from a
   baseline. A gradient mask rather than clip-path, because a fully clipped
   element never reports as intersecting and so would never be revealed */
html.motion [data-m="mask"]:not(.m-done) {
  -webkit-mask: linear-gradient(to bottom, #000 45%, transparent 55%) 0 100% / 100% 220% no-repeat;
  mask: linear-gradient(to bottom, #000 45%, transparent 55%) 0 100% / 100% 220% no-repeat;
  transition: opacity var(--m-dur) var(--m-ease), translate var(--m-dur) var(--m-ease),
    -webkit-mask-position 1s var(--m-ease), mask-position 1s var(--m-ease);
  transition-delay: var(--m-d, 0ms);
}
html.motion [data-m="mask"]:not(.m-in):not(.m-done) { opacity: 1; translate: 0 .35em; }
html.motion [data-m="mask"].m-in:not(.m-done) { -webkit-mask-position: 0 0; mask-position: 0 0; }

/* ── reading progress (long pages) ── */
.m-progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; z-index: 1000;
  background: var(--blue, #008cc1);
  transform-origin: 0 50%; scale: var(--m-p, 0) 1;
  pointer-events: none;
}
html[dir="rtl"] .m-progress { transform-origin: 100% 50%; }

/* ── parallax + tilt + magnetic are driven from JS through these props ── */
html.motion [data-m-parallax] { will-change: translate; }

/* ── process rail (home): fills in as the steps scroll past ── */
html.motion .steps .step::before {
  background: linear-gradient(to right, var(--blue) calc(var(--m-rail, 0) * 100%), var(--hair) 0);
}
html.motion[dir="rtl"] .steps .step::before {
  background: linear-gradient(to left, var(--blue) calc(var(--m-rail, 0) * 100%), var(--hair) 0);
}
@media (max-width: 960px) {
  html.motion .steps .step::before,
  html.motion[dir="rtl"] .steps .step::before {
    background: linear-gradient(to bottom, var(--blue) calc(var(--m-rail, 0) * 100%), var(--hair) 0);
  }
}

/* ── FAQ: open/close eases its height where the browser can animate it
      (::details-content + interpolate-size); elsewhere it opens instantly ── */
.faq-item { interpolate-size: allow-keywords; }
.faq-item::details-content {
  block-size: 0; overflow: clip;
  transition: block-size .4s var(--m-ease), content-visibility .4s allow-discrete;
}
.faq-item[open]::details-content { block-size: auto; }
.faq-item .faq-a { transition: opacity .4s var(--m-ease), translate .4s var(--m-ease); }
.faq-item:not([open]) .faq-a { opacity: 0; translate: 0 -6px; }

/* ── links inside running copy: a faint rule is always there (links never
      rely on colour alone), and a full-strength one draws across on hover ── */
.m-link {
  text-decoration: none;
  background:
    linear-gradient(currentColor, currentColor) no-repeat 0 100% / 0 1px,
    linear-gradient(color-mix(in srgb, currentColor 35%, transparent), color-mix(in srgb, currentColor 35%, transparent)) no-repeat 0 100% / 100% 1px;
  transition: background-size .35s var(--m-ease);
}
html[dir="rtl"] .m-link { background-position: 100% 100%, 100% 100%; }
.m-link:hover, .m-link:focus-visible { background-size: 100% 1px, 100% 1px; }

/* the case-study hero shot is oversized so its parallax drift never shows an edge */
html.motion .shot picture { display: block; overflow: hidden; }
html.motion .shot img[data-m-parallax] { scale: 1.08; }

@media (prefers-reduced-motion: reduce) {
  .faq-item::details-content { transition: none; }
  .faq-item .faq-a { transition: none; opacity: 1; translate: none; }
  .m-link { transition: none; }
}
