/*
  motion.css — modern motion layer for vibesmusic.app

  Everything here is opt-in via class names + scroll/intersection,
  so it grafts onto any page that loads this file without
  changing existing layout. All animations respect
  prefers-reduced-motion via the media query at the bottom.

  Cascade:
    1. CSS scroll-driven entry fade/rise on .w-section, .stat-strip,
       .team-card, .w-hero-title, .editorial — via animation-timeline: view()
    2. Variable-font opsz "breathing" on .editorial italic spans
    3. Hand-drawn underline wipe on .editorial on hover
    4. Vinyl spin tied to scroll position on .vinyl-echo
    5. Sticky magazine running-head on .w-eyebrow
    6. Hover-tilt on stat tiles + team cards
    7. View Transitions API decl for cross-page nav morph

  Style: short selectors, brand variables only, no JS dependency
  for the CSS-only behaviors.
*/

/* ─────────────────────────────────────────────────────────────
   0. Canonical floating-pill nav — single source of truth for
      the .w-nav structure on every page. Lives here (not in
      wrapped.css) because index.html doesn't load wrapped.css,
      and motion.css is loaded everywhere via the sweep.
   ───────────────────────────────────────────────────────────── */

.w-nav {
  position: fixed;
  top: 24px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px 8px 16px;
  background: rgba(10, 10, 15, 0.55);
  backdrop-filter: blur(28px) saturate(1.4);
  -webkit-backdrop-filter: blur(28px) saturate(1.4);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 999px;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 20px 40px rgba(0, 0, 0, 0.35);
  margin: 0;
  max-width: calc(100vw - 32px);
}
.w-nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px 6px 4px;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  text-decoration: none;
  color: #FFFFFF;
  margin-right: 4px;
}
.w-nav-brand img {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  object-fit: cover;
  box-shadow: 0 6px 18px rgba(165, 107, 255, 0.40);
  flex-shrink: 0;
  display: block;
}
.w-nav-links {
  display: flex;
  gap: 2px;
  align-items: center;
  margin-left: 4px;
}
.w-nav-links a {
  padding: 8px 14px;
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.74);
  text-decoration: none;
  border-radius: 999px;
  transition: color 0.2s ease, background 0.2s ease;
}
.w-nav-links a:hover {
  color: #FFFFFF;
  background: rgba(255, 255, 255, 0.06);
}
.w-nav-cta {
  padding: 9px 16px;
  background: #FFFFFF;
  color: #0A0A0F;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1),
              box-shadow 0.2s cubic-bezier(0.16, 1, 0.3, 1),
              background 0.2s ease;
}
.w-nav-cta:hover {
  transform: translateY(-1px);
  box-shadow: 0 14px 28px rgba(255, 255, 255, 0.15);
}
@media (max-width: 720px) {
  .w-nav { padding: 6px 6px 6px 10px; gap: 4px; }
  .w-nav-brand { font-size: 14px; padding: 4px 6px 4px 2px; }
  .w-nav-brand img { width: 24px; height: 24px; }
  .w-nav-links { display: none; }
  .w-nav-cta { padding: 8px 14px; font-size: 13px; }
}

/* ─────────────────────────────────────────────────────────────
   1. View Transitions API — page-to-page morph
   ───────────────────────────────────────────────────────────── */

@view-transition {
  navigation: auto;
}

/* Apply a transition name to elements we want to morph across pages.
   Only ONE element per page can claim a given name, so we put the
   morph slot on the persistent nav brand. The footer brand and
   hero title still crossfade as part of the root transition. */
.w-nav-brand,
.nav-logo {
  view-transition-name: vibes-brand;
}

/* Default crossfade easing for the transition pseudo-elements */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.32s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
::view-transition-old(vibes-brand),
::view-transition-new(vibes-brand) {
  animation-duration: 0.42s;
}

/* ─────────────────────────────────────────────────────────────
   2. Scroll-driven entry: fade + rise as element enters view
   ───────────────────────────────────────────────────────────── */

@supports (animation-timeline: view()) {
  /* Reveal anything tagged .reveal, plus structural elements
     we want to animate by default. */
  .reveal,
  .w-section > .w-eyebrow,
  .w-section > .w-h2,
  .w-section > .w-p,
  .w-section > .stat-strip,
  .w-section > .team-grid,
  .w-section > .w-rule,
  .post-nav,
  .end-cta {
    animation: motion-rise linear both;
    animation-timeline: view();
    animation-range: entry 5% cover 22%;
  }

  /* A slightly longer settle for stat numerals so they read like
     they're "landing" after the surrounding copy. */
  .stat-num {
    animation: motion-pop linear both;
    animation-timeline: view();
    animation-range: entry 10% cover 30%;
  }

  /* Editorial italic words get a softer, later reveal so they
     read like a pull-quote dropping into place. */
  .editorial {
    animation: motion-italic-rise linear both;
    animation-timeline: view();
    animation-range: entry 20% cover 40%;
  }
}

@keyframes motion-rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes motion-pop {
  0% {
    opacity: 0;
    transform: translateY(18px) scale(0.94);
  }
  60% {
    opacity: 1;
    transform: translateY(0) scale(1.02);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes motion-italic-rise {
  from {
    opacity: 0;
    transform: translateY(6px) skewX(-2deg);
    font-variation-settings: "opsz" 144;
  }
  to {
    opacity: 1;
    transform: translateY(0) skewX(0);
    font-variation-settings: "opsz" 96;
  }
}

/* ─────────────────────────────────────────────────────────────
   3. Editorial italic breathing — Fraunces opsz oscillation
   ───────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: no-preference) {
  .editorial {
    animation: motion-italic-breath 5.2s ease-in-out infinite;
    animation-delay: 0.6s;
  }
}

@keyframes motion-italic-breath {
  0%, 100% { font-variation-settings: "opsz" 96, "wght" 500; }
  50%      { font-variation-settings: "opsz" 120, "wght" 540; }
}

/* When the scroll-driven entry fires, the entry animation wins
   until it completes — then the infinite breath takes over. */
@supports (animation-timeline: view()) {
  .editorial {
    /* Run both animations: entry-driven rise + perpetual breath.
       The named animations from the @supports block above are
       composited with this one. */
  }
}

/* ─────────────────────────────────────────────────────────────
   4. Hand-drawn underline wipe on .editorial on hover
   ───────────────────────────────────────────────────────────── */

.editorial {
  position: relative;
  display: inline-block;
  cursor: default;
}

.editorial::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.05em;
  height: 3px;
  background: linear-gradient(
    90deg,
    var(--neon-dusk-accent, #FF3B7A),
    var(--peak-sun-bg, #FFD93B)
  );
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.42s cubic-bezier(0.4, 0, 0.2, 1);
  pointer-events: none;
  /* Slight rotation so it reads as hand-drawn, not stamped. */
  rotate: -0.6deg;
}

.editorial:hover::after,
.editorial:focus-visible::after {
  transform: scaleX(1);
}

/* ─────────────────────────────────────────────────────────────
   5. Vinyl spin tied to scroll — used as a hero accent
   ───────────────────────────────────────────────────────────── */

.vinyl-echo {
  display: inline-block;
  vertical-align: middle;
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  will-change: transform;
}

@supports (animation-timeline: scroll()) {
  .vinyl-echo {
    animation: motion-vinyl-spin linear;
    animation-timeline: scroll(root block);
    animation-duration: 1ms; /* required but ignored under scroll() */
  }
}

@keyframes motion-vinyl-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(540deg); }
}

/* Fallback: gentle continuous rotation if scroll-driven isn't supported */
@supports not (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .vinyl-echo {
      animation: motion-vinyl-spin 22s linear infinite;
    }
  }
}

/* ─────────────────────────────────────────────────────────────
   6. Sticky magazine running-head — .w-eyebrow inside sections
   ───────────────────────────────────────────────────────────── */

.w-section > .w-eyebrow {
  position: sticky;
  top: 16px;
  z-index: 2;
  background: linear-gradient(
    to bottom,
    var(--page-bg, #0A0A0F) 65%,
    transparent
  );
  padding: 6px 0 10px;
  margin-left: -2px;
  /* Fade in slightly when stuck, so it doesn't feel pasted on */
  transition: opacity 0.2s ease;
}

/* ─────────────────────────────────────────────────────────────
   7. Hover-tilt on stat tiles + team cards
   ───────────────────────────────────────────────────────────── */

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .stat-strip > div,
  .team-card,
  .post-nav-card {
    transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1),
                box-shadow 0.35s ease;
    transform-style: preserve-3d;
    will-change: transform;
  }

  .stat-strip > div:hover,
  .team-card:hover {
    transform: perspective(900px) rotateX(2deg) rotateY(-2deg) translateY(-2px);
    box-shadow: 0 18px 60px -20px rgba(255, 59, 122, 0.18);
  }
}

/* ─────────────────────────────────────────────────────────────
   8. CTA palette-burst container (positioned by JS)
   ───────────────────────────────────────────────────────────── */

.palette-burst {
  position: fixed;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 9999;
}
.palette-burst .pb-dot {
  position: absolute;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  left: -5px;
  top: -5px;
  opacity: 0;
  transform: translate(0, 0);
  animation: motion-palette-burst 0.9s cubic-bezier(0.22, 0.8, 0.32, 1) both;
}
@keyframes motion-palette-burst {
  0% {
    opacity: 1;
    transform: translate(0, 0) scale(0.6);
  }
  60% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(var(--pb-x, 0), var(--pb-y, 0)) scale(1);
  }
}

/* ─────────────────────────────────────────────────────────────
   9. Stat-strip count-up: hide visual digits before JS hydrates
   ───────────────────────────────────────────────────────────── */

.stat-num[data-countup]:not(.counted) {
  /* JS replaces text content; we keep visible to preserve layout */
  visibility: visible;
}

/* ─────────────────────────────────────────────────────────────
   Reduced-motion: kill the kinetic stuff but keep transitions
   short enough to feel responsive.
   ───────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .vinyl-echo,
  .editorial,
  .reveal,
  .w-section > .w-eyebrow,
  .w-section > .w-h2,
  .w-section > .w-p,
  .w-section > .stat-strip,
  .w-section > .team-grid,
  .stat-num,
  .post-nav,
  .end-cta {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
    font-variation-settings: normal !important;
  }
  .stat-strip > div:hover,
  .team-card:hover,
  .post-nav-card:hover {
    transform: none !important;
    box-shadow: none !important;
  }
  ::view-transition-old(root),
  ::view-transition-new(root),
  ::view-transition-old(vibes-brand),
  ::view-transition-new(vibes-brand) {
    animation-duration: 0.01ms !important;
  }
}

/* ─────────────────────────────────────────────────────────────
   THEME TOGGLE — sun/moon button injected into .w-nav by
   motion.js. Default theme is "light" (current site). Dark
   mode dims the aurora + bright surfaces; the already-dark
   sections (manifesto, bento, footer) stay as they are.
   ───────────────────────────────────────────────────────────── */

.w-nav-theme {
  display: inline-grid;
  place-items: center;
  width: 36px;
  height: 36px;
  margin: 0 4px;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.10);
  background: rgba(255, 255, 255, 0.04);
  color: rgba(255, 255, 255, 0.86);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.w-nav-theme:hover {
  background: rgba(255, 255, 255, 0.10);
  color: #FFFFFF;
}
.w-nav-theme svg {
  width: 16px;
  height: 16px;
  display: block;
}
/* Show the right glyph per active theme */
.w-nav-theme .icon-sun  { display: none; }
.w-nav-theme .icon-moon { display: block; }
:root[data-theme="dark"] .w-nav-theme .icon-sun  { display: block; }
:root[data-theme="dark"] .w-nav-theme .icon-moon { display: none; }

/* On narrow viewports the nav is more compact — shrink the toggle too */
@media (max-width: 720px) {
  .w-nav-theme { width: 30px; height: 30px; margin: 0 2px; }
  .w-nav-theme svg { width: 14px; height: 14px; }
}

/* ─── Dark-theme global overrides ────────────────────────────
   Variables flip first so any element that reads them rebalances.
   Then we apply targeted dimming on bright surfaces. */

:root[data-theme="dark"] {
  /* Reduce hairline brightness so dark mode reads more matte */
  --hairline: rgba(255, 255, 255, 0.06);
  --hairline-soft: rgba(255, 255, 255, 0.035);
}

/* Smooth the transition between modes so the toggle feels considered */
html, body,
.hero-aurora, .hero-aurora .aurora-blob,
.hero-meter, .hero-meter-bar,
.home-android-band, .home-postcard, .home-moment {
  transition: opacity 0.4s ease, filter 0.4s ease, background 0.4s ease;
}

/* Hero aurora — the main "bright" element on the homepage.
   In dark mode, dim it to ~22% and reduce saturation. */
:root[data-theme="dark"] .hero-aurora {
  opacity: 0.22;
  filter: saturate(0.7);
}
/* Audio meter under the hero — dimmer + lower opacity bars */
:root[data-theme="dark"] .hero-meter {
  opacity: 0.55;
}
/* Live ticker — already low-contrast; dim a touch further */
:root[data-theme="dark"] .hero-ticker {
  opacity: 0.7;
}

/* The acid-summer Android band, the citrus-glass postcard composer,
   and the peak-sun bento accents are all bright-palette surfaces.
   In dark mode they get a subtle dimming overlay so the whole page
   reads as one quieter object. */
:root[data-theme="dark"] .home-android-band,
:root[data-theme="dark"] .home-postcard {
  filter: brightness(0.78) saturate(0.85);
}

/* The moment picker is already late-drive (dark), so it stays as-is.
   The honest "fine" caption in dark mode just bumps a hair lower. */
:root[data-theme="dark"] .home-moment-fine {
  opacity: 0.7;
}

/* Tour stops — each is its own palette ground, so we don't recolor
   them. We just dim the saturation a touch in dark mode. */
:root[data-theme="dark"] .stop {
  filter: saturate(0.88) brightness(0.95);
}

/* No transition flash on initial paint — only after JS sets the
   data-theme attribute. We add .theme-ready to body after the
   theme is resolved so the first frame isn't a flicker. */
body:not(.theme-ready) html, body:not(.theme-ready) body,
body:not(.theme-ready) .hero-aurora,
body:not(.theme-ready) .home-android-band,
body:not(.theme-ready) .home-postcard {
  transition: none !important;
}

/* Respect reduced motion — instant transitions */
@media (prefers-reduced-motion: reduce) {
  html, body,
  .hero-aurora, .hero-aurora .aurora-blob,
  .hero-meter, .hero-meter-bar,
  .home-android-band, .home-postcard, .home-moment {
    transition: none !important;
  }
}
