/* ============================================================================
 * motion.css — drop-in motion layer for the `motion` skill
 * Zero-dependency · token-aware · reduced-motion-safe · mobile-first · GPU-only
 *
 * Pairs with motion.js (the engine that adds `.mo-in` and drives behaviours).
 * Works standalone (sensible fallbacks) but inherits OSMO design tokens when
 * present: --ease-spring, --ease-smooth, --surface, --glass-bg, --bauhaus-*.
 *
 * ── ATTRIBUTE CONTRACT (must stay in sync with motion.js + docs) ────────────
 * SCROLL REVEAL (JS toggles `.mo-in` when the element enters the viewport):
 *   data-mo="fade|fade-up|fade-down|fade-left|fade-right|scale|scale-up|
 *            blur|clip-up|rise|rotate"
 *   data-mo-delay="<ms>"   data-mo-dur="<ms>"
 *   data-mo-ease="smooth|spring|out|in-out|linear"
 *   data-mo-once="true|false"            (default true)
 *   data-mo-stagger="<ms>"  → put on a PARENT; its revealed children cascade
 *   data-mo-split="chars|words"          → split text, each unit reveals
 * BEHAVIOURS (JS, init on scan / MO.refresh()):
 *   data-mo-count="<number>" [count-dur/decimals/prefix/suffix/sep]
 *   data-mo-magnetic="<0..1>"            (fine-pointer only)
 *   data-mo-tilt="<deg>"                 (fine-pointer only)
 *   data-mo-parallax="<factor>"          (e.g. 0.15; negative = opposite)
 *   data-mo-marquee [speed/reverse/gap]
 * CSS-ONLY UTILITIES (no JS):
 *   .mo-lift .mo-press .mo-underline .mo-shine .mo-glow-hover
 *   .mo-float .mo-bob .mo-pulse-soft .mo-spin .mo-skeleton
 *   .mo-gradient-mesh .mo-ken-burns .mo-marquee(+.mo-marquee__track)
 * ========================================================================== */

:root {
  /* Easing — inherit OSMO tokens if defined, else best-practice fallbacks.   */
  --mo-ease-out:    var(--ease-smooth, cubic-bezier(0.16, 1, 0.3, 1));   /* enter */
  --mo-ease-in:     cubic-bezier(0.7, 0, 0.84, 0);                       /* exit  */
  --mo-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);                      /* move  */
  --mo-ease-spring: var(--ease-spring, cubic-bezier(0.34, 1.56, 0.64, 1)); /* playful */
  /* Duration scale — travel-distance should scale these up.                 */
  --mo-dur-micro: 140ms;   /* hover / tap feedback            */
  --mo-dur-fast:  240ms;   /* small element enter             */
  --mo-dur:       560ms;   /* default reveal                  */
  --mo-dur-slow:  820ms;   /* large hero / long-travel reveal */
  /* Reveal travel distances (kept small — taste, not theatre).              */
  --mo-rise: 28px;
  --mo-slide: 40px;
}

/* ── SCROLL REVEAL ───────────────────────────────────────────────────────── */
[data-mo] {
  opacity: 0;
  transition:
    opacity var(--_mo-dur, var(--mo-dur)) var(--_mo-ease, var(--mo-ease-out)) var(--_mo-delay, 0ms),
    transform var(--_mo-dur, var(--mo-dur)) var(--_mo-ease, var(--mo-ease-out)) var(--_mo-delay, 0ms),
    filter var(--_mo-dur, var(--mo-dur)) var(--_mo-ease, var(--mo-ease-out)) var(--_mo-delay, 0ms),
    clip-path var(--_mo-dur, var(--mo-dur)) var(--_mo-ease, var(--mo-ease-out)) var(--_mo-delay, 0ms);
  will-change: opacity, transform;
}
[data-mo].mo-in { opacity: 1; transform: none; filter: none; clip-path: inset(0 0 0 0); }

/* per-effect starting state */
[data-mo="fade"]        { }
[data-mo="fade-up"]     { transform: translate3d(0, var(--mo-rise), 0); }
[data-mo="rise"]        { transform: translate3d(0, calc(var(--mo-rise) * 1.6), 0); }
[data-mo="fade-down"]   { transform: translate3d(0, calc(var(--mo-rise) * -1), 0); }
[data-mo="fade-left"]   { transform: translate3d(var(--mo-slide), 0, 0); }
[data-mo="fade-right"]  { transform: translate3d(calc(var(--mo-slide) * -1), 0, 0); }
[data-mo="scale"]       { transform: scale(0.92); }
[data-mo="scale-up"]    { transform: scale(0.8); }
[data-mo="rotate"]      { transform: rotate(-4deg) scale(0.96); transform-origin: 50% 100%; }
[data-mo="blur"]        { filter: blur(14px); opacity: 0; }
[data-mo="clip-up"]     { clip-path: inset(100% 0 0 0); transform: translate3d(0, var(--mo-rise), 0); }

/* easing modifiers map to tokens (JS may also set --_mo-ease inline) */
[data-mo-ease="spring"] { --_mo-ease: var(--mo-ease-spring); }
[data-mo-ease="out"]    { --_mo-ease: var(--mo-ease-out); }
[data-mo-ease="in-out"] { --_mo-ease: var(--mo-ease-in-out); }
[data-mo-ease="linear"] { --_mo-ease: linear; }

/* split-text units (motion.js wraps each char/word in .mo-unit) */
.mo-split { display: inline-block; }
.mo-unit  {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.7em, 0);
  transition: opacity var(--mo-dur-fast) var(--mo-ease-out) var(--_mo-d, 0ms),
              transform var(--mo-dur) var(--mo-ease-spring) var(--_mo-d, 0ms);
  will-change: transform, opacity;
}
.mo-unit.mo-in { opacity: 1; transform: none; }
.mo-unit--space { white-space: pre; }

/* ── HOVER / PRESS MICRO-INTERACTIONS (CSS only) ─────────────────────────── */
.mo-lift {
  transition: transform var(--mo-dur-micro) var(--mo-ease-out),
              box-shadow var(--mo-dur-micro) var(--mo-ease-out);
}
.mo-lift:hover  { transform: translateY(-2px); }
.mo-lift:active { transform: translateY(0); transition-duration: 80ms; }

.mo-press { transition: transform var(--mo-dur-micro) var(--mo-ease-out); }
.mo-press:active { transform: scale(0.96); }

/* growing underline — great for nav/links */
.mo-underline { position: relative; }
.mo-underline::after {
  content: ""; position: absolute; left: 0; bottom: -2px; height: 1.5px; width: 100%;
  background: currentColor; transform: scaleX(0); transform-origin: right;
  transition: transform var(--mo-dur-fast) var(--mo-ease-out);
}
.mo-underline:hover::after { transform: scaleX(1); transform-origin: left; }

/* sheen sweep on hover (place on a button/card; needs overflow:hidden) */
.mo-shine { position: relative; overflow: hidden; isolation: isolate; }
.mo-shine::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(115deg, transparent 30%,
    rgba(255,255,255,0.35) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform 700ms var(--mo-ease-out);
}
.mo-shine:hover::before { transform: translateX(120%); }

.mo-glow-hover {
  transition: box-shadow var(--mo-dur-fast) var(--mo-ease-out),
              transform var(--mo-dur-fast) var(--mo-ease-out);
}
.mo-glow-hover:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 30px color-mix(in srgb, var(--bauhaus-blue, #3F5C7C) 28%, transparent);
}

/* ── AMBIENT / LOOPING (CSS only) ────────────────────────────────────────── */
.mo-float    { animation: mo-float 6s var(--mo-ease-in-out) infinite; }
.mo-bob      { animation: mo-bob 2.4s var(--mo-ease-in-out) infinite; }
.mo-pulse-soft { animation: mo-pulse 2.6s var(--mo-ease-in-out) infinite; }
.mo-spin     { animation: mo-spin 1s linear infinite; }

@keyframes mo-float  { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
@keyframes mo-bob    { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes mo-pulse  { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
@keyframes mo-spin   { to { transform: rotate(360deg); } }

/* shimmer skeleton — replaces "Loading..." text (OSMO rule) */
.mo-skeleton {
  position: relative; overflow: hidden;
  background: var(--surface-2, #e5dfd6);
  border-radius: var(--radius-card, 16px);
}
.mo-skeleton::after {
  content: ""; position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--surface, #ECE9E4) 70%, #fff) 45%, transparent);
  animation: mo-shimmer 1.6s linear infinite;
}
@keyframes mo-shimmer { to { transform: translateX(100%); } }

/* animated gradient-mesh background (atmosphere behind glass panels) */
.mo-gradient-mesh {
  background:
    radial-gradient(60% 60% at 20% 20%, color-mix(in srgb, var(--bauhaus-blue,#3F5C7C) 22%, transparent), transparent 70%),
    radial-gradient(55% 55% at 80% 30%, color-mix(in srgb, var(--bauhaus-yellow,#D4A82E) 18%, transparent), transparent 70%),
    radial-gradient(60% 60% at 50% 85%, color-mix(in srgb, var(--bauhaus-green,#5A8262) 18%, transparent), transparent 70%),
    var(--surface, #ECE9E4);
  background-size: 200% 200%, 200% 200%, 200% 200%, 100% 100%;
  animation: mo-gradient-drift 18s var(--mo-ease-in-out) infinite;
}
@keyframes mo-gradient-drift {
  0%,100% { background-position: 0% 0%, 100% 0%, 50% 100%, 0 0; }
  50%     { background-position: 100% 50%, 0% 60%, 60% 0%, 0 0; }
}

/* slow zoom for hero imagery */
.mo-ken-burns { overflow: hidden; }
.mo-ken-burns > img, .mo-ken-burns > .mo-ken-burns__img {
  animation: mo-ken-burns 14s var(--mo-ease-in-out) infinite alternate;
  transform-origin: center;
}
@keyframes mo-ken-burns { from { transform: scale(1); } to { transform: scale(1.12) translate(2%, -1%); } }

/* ── MARQUEE (CSS only; motion.js can make it velocity-reactive) ─────────── */
.mo-marquee { overflow: hidden; display: flex; user-select: none; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.mo-marquee__track {
  display: flex; flex: none; gap: var(--mo-marquee-gap, 2rem);
  padding-right: var(--mo-marquee-gap, 2rem);
  animation: mo-marquee var(--mo-marquee-dur, 24s) linear infinite;
}
.mo-marquee:hover .mo-marquee__track { animation-play-state: paused; }
.mo-marquee[data-reverse] .mo-marquee__track { animation-direction: reverse; }
@keyframes mo-marquee { to { transform: translateX(-50%); } }

/* ── PAGE / SECTION ENTER (CSS only — use on #appContent un-hide) ─────────── */
.mo-page-in { animation: mo-page-in var(--mo-dur) var(--mo-ease-out) both; }
@keyframes mo-page-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* scroll-progress bar (motion.js sets --mo-progress 0..1) */
.mo-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 9999; transform-origin: 0 50%; transform: scaleX(var(--mo-progress, 0)); background: var(--bauhaus-blue, #3F5C7C); }

/* tilt helper: parent gets perspective so children rotateX/Y read as depth */
[data-mo-tilt] { transform-style: preserve-3d; will-change: transform; transition: transform var(--mo-dur-fast) var(--mo-ease-out); }

/* ── ACCORDION (honest auto-height via grid-template-rows 0fr→1fr) ───────── */
.mo-acc { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--mo-dur-fast) var(--mo-ease-out); }
.mo-acc.mo-open, .mo-acc[open] { grid-template-rows: 1fr; }
.mo-acc > .mo-acc__body { overflow: hidden; min-height: 0; }

/* ── IMAGE WIPE (clip-path reveal; add .mo-in via the reveal engine) ─────── */
.mo-img-reveal { clip-path: inset(0 100% 0 0); transition: clip-path var(--mo-dur-slow) var(--mo-ease-out); overflow: hidden; }
.mo-img-reveal.mo-in { clip-path: inset(0 0 0 0); }
.mo-img-reveal > img { transform: scale(1.12); transition: transform calc(var(--mo-dur-slow) + 200ms) var(--mo-ease-out); }
.mo-img-reveal.mo-in > img { transform: none; }

/* ── STICKY HEADER SHRINK (engine toggles .mo-stuck via an IO sentinel) ──── */
/* consumer styles .mo-stuck however they like; this is just a smooth base */
[data-mo-shrink] { transition: padding var(--mo-dur-fast) var(--mo-ease-out), box-shadow var(--mo-dur-fast) var(--mo-ease-out), background var(--mo-dur-fast) var(--mo-ease-out); }

/* ── NATIVE CSS SCROLL-DRIVEN ANIMATION (progressive enhancement) ─────────
 * Where supported (Chromium 115+, June 2026), these run on the compositor
 * with ZERO JS. motion.js is the fallback for Safari/Firefox until parity.   */
@supports (animation-timeline: view()) {
  .mo-sd-reveal {
    opacity: 0;
    animation: mo-sd-reveal linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 60%;
  }
  @keyframes mo-sd-reveal { to { opacity: 1; transform: none; } }
  .mo-sd-reveal { transform: translateY(var(--mo-rise)); }
  /* parallax with zero JS */
  .mo-sd-parallax { animation: mo-sd-parallax linear both; animation-timeline: view(); }
  @keyframes mo-sd-parallax { from { transform: translateY(calc(var(--mo-parallax, 0.15) * -80px)); } to { transform: translateY(calc(var(--mo-parallax, 0.15) * 80px)); } }
}

/* ── REDUCED MOTION — *reduce, don't remove* (vestibular safety) ──────────
 * The lazy nuke `*{animation:none;transition:none}` strips ALL feedback,
 * which itself harms usability. Instead we: (a) stop auto/looping/ambient
 * motion, (b) land scroll-reveals on their final state INSTANTLY (no slide),
 * but (c) KEEP small user-initiated transitions (hover lift, modal opacity
 * fade) — those aren't vestibular triggers and carry useful feedback.        */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto !important; }
  /* auto / looping ambient motion → off */
  .mo-float, .mo-bob, .mo-pulse-soft, .mo-spin,
  .mo-ken-burns > img, .mo-ken-burns > .mo-ken-burns__img,
  .mo-gradient-mesh, .mo-marquee__track { animation: none !important; }
  .mo-skeleton::after { animation: none !important; }
  /* scroll reveals (incl. native scroll-driven + split + img wipe) → instant final state */
  [data-mo], .mo-unit, .mo-sd-reveal, .mo-sd-parallax, .mo-img-reveal {
    opacity: 1 !important; transform: none !important; filter: none !important;
    clip-path: none !important; transition: none !important; animation: none !important;
  }
  .mo-img-reveal > img { transform: none !important; }
}
