/* ─────────────────────────────────────────────────────────────────────────
   Guardian Motion System — production easing + scroll-reveal + GPU hints
   Layered ON TOP OF page-transitions.css. Loads on every page.
   Designed to feel like Framer Motion's spring/transition system, but in
   pure CSS — no JS dependency for the reveal/hover/tap effects.
   ───────────────────────────────────────────────────────────────────────── */

/* Easing tokens — name them by feel, not by curve. Use these in any
   `transition: ... var(--ease-*)` to keep motion consistent everywhere. */
:root {
  --ease-spring:   cubic-bezier(0.34, 1.56, 0.64, 1);   /* overshoot pop */
  --ease-spring-soft: cubic-bezier(0.22, 1, 0.36, 1);   /* gentle settle */
  --ease-swift:    cubic-bezier(0.16, 1, 0.3, 1);       /* expo-out */
  --ease-quint:    cubic-bezier(0.25, 1, 0.5, 1);       /* quint-out */
  --ease-out:      cubic-bezier(0.32, 0.72, 0, 1);      /* iOS-style */
  --ease-in:       cubic-bezier(0.7, 0, 0.84, 0);
  --ease-in-out:   cubic-bezier(0.65, 0, 0.35, 1);

  --dur-fast:   0.18s;
  --dur-base:   0.28s;
  --dur-slow:   0.42s;
  --dur-page:   0.5s;
}

/* Honour the user's accessibility preference. The transitions still happen
   so layout changes don't snap, but they're snapped to ~1ms which is
   imperceptible. We never set `display: none` to "fix" reduced motion —
   that would break interactions that depend on transitionend events. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* GPU-promote anything that animates transform/opacity, but only when
   actually animating — leaving will-change on permanently is itself a
   memory cost. We use it on a class that's added by motion.js when an
   element comes into the viewport, then removed after the animation. */
.g-will-animate {
  will-change: transform, opacity;
}

/* Smooth scroll for in-page anchors only — disabled if reduce-motion. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Scroll-driven reveal classes. Add `.g-reveal` (or one of the directional
   variants) to any element. motion.js uses IntersectionObserver to add
   `.is-visible` when the element enters the viewport, which kicks off the
   transition. Defaults to fade-up.
   ───────────────────────────────────────────────────────────────────────── */
.g-reveal,
.g-reveal-up,
.g-reveal-down,
.g-reveal-left,
.g-reveal-right,
.g-reveal-scale {
  opacity: 0;
  transition:
    opacity   var(--dur-slow) var(--ease-swift),
    transform var(--dur-slow) var(--ease-swift);
  will-change: transform, opacity;
}

.g-reveal,
.g-reveal-up    { transform: translate3d(0, 16px, 0); }
.g-reveal-down  { transform: translate3d(0, -16px, 0); }
.g-reveal-left  { transform: translate3d(16px, 0, 0); }
.g-reveal-right { transform: translate3d(-16px, 0, 0); }
.g-reveal-scale { transform: scale(0.96); }

.g-reveal.is-visible,
.g-reveal-up.is-visible,
.g-reveal-down.is-visible,
.g-reveal-left.is-visible,
.g-reveal-right.is-visible,
.g-reveal-scale.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
}

/* Stagger helpers — a parent with `.g-stagger-children` will apply
   incremental delays to its direct children, just like framer-motion's
   variants={staggerChildren}. Caps at 12 children to keep the cascade
   short. */
.g-stagger-children > .g-reveal,
.g-stagger-children > .g-reveal-up,
.g-stagger-children > .g-reveal-down,
.g-stagger-children > .g-reveal-left,
.g-stagger-children > .g-reveal-right,
.g-stagger-children > .g-reveal-scale {
  transition-delay: 0s;
}
.g-stagger-children > *:nth-child(1).is-visible  { transition-delay: 0.00s; }
.g-stagger-children > *:nth-child(2).is-visible  { transition-delay: 0.05s; }
.g-stagger-children > *:nth-child(3).is-visible  { transition-delay: 0.10s; }
.g-stagger-children > *:nth-child(4).is-visible  { transition-delay: 0.15s; }
.g-stagger-children > *:nth-child(5).is-visible  { transition-delay: 0.20s; }
.g-stagger-children > *:nth-child(6).is-visible  { transition-delay: 0.25s; }
.g-stagger-children > *:nth-child(7).is-visible  { transition-delay: 0.30s; }
.g-stagger-children > *:nth-child(8).is-visible  { transition-delay: 0.35s; }
.g-stagger-children > *:nth-child(9).is-visible  { transition-delay: 0.40s; }
.g-stagger-children > *:nth-child(10).is-visible { transition-delay: 0.45s; }
.g-stagger-children > *:nth-child(11).is-visible { transition-delay: 0.50s; }
.g-stagger-children > *:nth-child(12).is-visible { transition-delay: 0.55s; }

/* ─────────────────────────────────────────────────────────────────────────
   Spring-style hover/tap micro-interactions. Apply to any interactive
   element — wraps the existing button styles without overriding their
   colour/border behaviour.
   ───────────────────────────────────────────────────────────────────────── */
.g-press {
  transition:
    transform var(--dur-base) var(--ease-spring),
    box-shadow var(--dur-base) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.g-press:hover  { transform: translateY(-2px) scale(1.015); }
.g-press:active { transform: translateY(0)    scale(0.985); transition-duration: 80ms; }

/* Subtle lift for cards (less aggressive than .g-press) */
.g-lift {
  transition:
    transform   var(--dur-base) var(--ease-swift),
    box-shadow  var(--dur-base) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}
.g-lift:hover { transform: translateY(-3px); }

/* ─────────────────────────────────────────────────────────────────────────
   Polished focus ring — keyboard-only via :focus-visible, avoids the
   ugly default ring on click. Works alongside the existing accent token.
   ───────────────────────────────────────────────────────────────────────── */
*:focus { outline: none; }
*:focus-visible {
  outline: 2px solid var(--accent, #6f92ff);
  outline-offset: 2px;
  transition: outline-offset var(--dur-fast) var(--ease-out);
}

/* ─────────────────────────────────────────────────────────────────────────
   Skeleton shimmer (extends the existing .g-skeleton with a richer mask
   that respects the angled clip-paths in the design system).
   ───────────────────────────────────────────────────────────────────────── */
@keyframes g-shimmer-rich {
  0%   { background-position: -1200px 0; }
  100% { background-position:  1200px 0; }
}
.g-skeleton-rich {
  position: relative;
  background:
    linear-gradient(90deg,
      rgba(255, 255, 255, 0.025) 0%,
      rgba(255, 255, 255, 0.07)  50%,
      rgba(255, 255, 255, 0.025) 100%);
  background-size: 1600px 100%;
  animation: g-shimmer-rich 2.2s linear infinite;
}

/* ─────────────────────────────────────────────────────────────────────────
   Number-counter target — motion.js animates the textContent for any
   element with `data-g-count="<final value>"`. The CSS just stops it
   from looking jittery during the count.
   ───────────────────────────────────────────────────────────────────────── */
[data-g-count] {
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────────────────────────────────────
   Tooltip-style hover preview (used by sidebars + chips). Avoids
   layout-thrashing by living in a transformed container.
   ───────────────────────────────────────────────────────────────────────── */
.g-tip-host { position: relative; }
.g-tip {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition:
    opacity var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}
.g-tip-host:hover > .g-tip,
.g-tip-host:focus-within > .g-tip {
  opacity: 1;
  transform: translateY(0);
}

/* ─────────────────────────────────────────────────────────────────────────
   Containment hints — declares to the browser that side panels can be
   painted independently. Cuts paint area on scroll, esp. with backdrop
   filters in the nav rail.
   ───────────────────────────────────────────────────────────────────────── */
.left-sidebar,
.right-sidebar,
.mobile-panel,
.g-nav,
#g-nav-rail {
  contain: layout style paint;
}

/* The globe is its own paint context — keep it isolated so right-side
   activity feed updates don't repaint it. */
.globe-wrapper,
#globe-container {
  contain: strict;
}

/* ─────────────────────────────────────────────────────────────────────────
   Tap ripple — subtle and works without JS. Uses a pseudo-element so it
   doesn't fight buttons that already have an ::after.
   ───────────────────────────────────────────────────────────────────────── */
.g-ripple {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.g-ripple::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(circle at var(--g-ripple-x, 50%) var(--g-ripple-y, 50%),
              rgba(255, 255, 255, 0.18) 0%,
              transparent 60%);
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity var(--dur-base) var(--ease-out),
    transform var(--dur-slow) var(--ease-spring-soft);
  pointer-events: none;
  z-index: 0;
}
.g-ripple:active::after {
  opacity: 1;
  transform: scale(1.2);
  transition-duration: 0ms, 0ms;
}
