/* ==========================================================================
   Drift — animation system

   Two reveal vocabularies:
   1. Block reveals (.fade-up / .scale-in / .slide-*) for general elements.
   2. Masked line reveals (.mask-line) where type rises from behind a mask —
      the editorial signature.

   Everything transform-based is gated behind `prefers-reduced-motion:
   no-preference`. Motion-sensitive visitors get the full layout and type
   with zero movement — never a half-revealed, opacity:0 element.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /* ---- Block reveals ---------------------------------------------------- */
  .fade-up, .fade-up-slow, .fade-in, .scale-in, .slide-left, .slide-right {
    opacity: 0;
    transition-property: opacity, transform;
    transition-timing-function: var(--ease-out);
    will-change: opacity, transform;
  }
  .fade-up      { transform: translateY(22px); transition-duration: 0.95s; }
  .fade-up-slow { transform: translateY(26px); transition-duration: 1.25s; }
  .fade-in      { transition-duration: 0.85s; }
  .scale-in     { transform: scale(1.05);     transition-duration: 1.1s; }
  .slide-left   { transform: translateX(24px);  transition-duration: 0.95s; }
  .slide-right  { transform: translateX(-24px); transition-duration: 0.95s; }

  .fade-up.in-view, .fade-up-slow.in-view, .fade-in.in-view,
  .scale-in.in-view, .slide-left.in-view, .slide-right.in-view {
    opacity: 1; transform: none;
  }

  /* ---- Masked line reveals --------------------------------------------- */
  /* The mask clips; the inner span starts pushed below and slides up. The
     vertical padding gives Fraunces' italic ascenders/descenders room so the
     clip never shaves a letterform; the negative margin cancels the layout
     cost so spacing stays tight. */
  .mask-line {
    display: block;
    overflow: hidden;
    padding: 0.12em 0.02em 0.18em;
    margin: -0.12em -0.02em -0.18em;
  }
  .mask-line > span {
    display: block;
    transform: translateY(118%);
    transition: transform 1.05s var(--ease-out);
    transition-delay: calc(var(--i, 0) * 0.11s);
    will-change: transform;
  }
  .in-view .mask-line > span,
  .mask-line.in-view > span,
  body.loaded .hero .mask-line > span {
    transform: translateY(0);
  }

  /* Generic reveal children driven by a parent .in-view (used for fades that
     should wait for their section, with per-item --i stagger). */
  .stagger > * {
    opacity: 0; transform: translateY(18px);
    transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out);
    transition-delay: calc(var(--i, 0) * 0.1s);
    will-change: opacity, transform;
  }
  .stagger.in-view > * { opacity: 1; transform: none; }

  /* ---- Clip-path image wipe -------------------------------------------- */
  .img-reveal {
    clip-path: inset(0 0 100% 0);
    transform: scale(1.07);
    transition: clip-path 1.4s var(--ease-out), transform 1.6s var(--ease-out);
    will-change: clip-path, transform;
  }
  /* Reveals whether the wipe target carries .in-view itself (full-bleed
     image) or sits inside a revealed wrapper (the alternating feel rows). */
  .img-reveal.in-view,
  .in-view .img-reveal { clip-path: inset(0 0 0 0); transform: scale(1); }

  /* ---- Stagger delay utilities ----------------------------------------- */
  .delay-100 { transition-delay: 0.1s; }
  .delay-200 { transition-delay: 0.2s; }
  .delay-300 { transition-delay: 0.3s; }
  .delay-400 { transition-delay: 0.4s; }
  .delay-500 { transition-delay: 0.5s; }
  .delay-600 { transition-delay: 0.6s; }

  /* ---- Hero load stagger (non-masked elements) ------------------------- */
  .hero-stagger > * {
    opacity: 0; transform: translateY(16px);
    transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out);
  }
  body.loaded .hero-stagger > * { opacity: 1; transform: none; }
  body.loaded .hero-stagger > *:nth-child(1) { transition-delay: 0.10s; }
  body.loaded .hero-stagger > *:nth-child(2) { transition-delay: 0.30s; }
  body.loaded .hero-stagger > *:nth-child(3) { transition-delay: 0.55s; }
  body.loaded .hero-stagger > *:nth-child(4) { transition-delay: 0.80s; }
  body.loaded .hero-stagger > *:nth-child(5) { transition-delay: 1.0s; }
  body.loaded .hero-stagger > *:nth-child(6) { transition-delay: 1.2s; }

  /* Per-element load reveal (pairs with .delay-* for staggering). Used for
     hero copy that sits between masked headline lines, where a single
     nth-child stagger wrapper would be awkward. */
  .load {
    opacity: 0; transform: translateY(16px);
    transition: opacity 0.95s var(--ease-out), transform 0.95s var(--ease-out);
    will-change: opacity, transform;
  }
  body.loaded .load { opacity: 1; transform: none; }

  /* nav + breath guide fade in on load */
  .nav__fade, .hero__visual {
    opacity: 0; transform: translateY(-6px);
    transition: opacity 0.9s ease, transform 0.9s ease;
  }
  .hero__visual { transform: scale(0.96); transition: opacity 1.4s var(--ease-out), transform 1.4s var(--ease-out); transition-delay: 0.4s; }
  body.loaded .nav__fade { opacity: 1; transform: none; }
  body.loaded .hero__visual { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Breathing — the brand-defining motion.
   Small amplitude, long period; fast/large breathing reads as a pulse, which
   is the opposite of calm.
   -------------------------------------------------------------------------- */

@keyframes breathe      { 0%,100% { transform: scale(1); } 50% { transform: scale(1.02); } }
@keyframes breathe-mesh { from { transform: scale(1); } to { transform: scale(1.06); } }
@keyframes drift-across { 0% { transform: translateX(-25%); } 100% { transform: translateX(125%); } }
@keyframes marquee      { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes cue-fall     { 0% { transform: translateY(-100%); } 100% { transform: translateY(100%); } }
@keyframes dot-breathe  { 0%,100% { opacity: 0.4; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1.3); } }

@media (prefers-reduced-motion: no-preference) {
  .breathe      { animation: breathe 6s ease-in-out infinite; will-change: transform; }
  .breathe-slow { animation: breathe 10s ease-in-out infinite; will-change: transform; }
  .breathe-mesh { animation: breathe-mesh 8s ease-in-out infinite alternate; }
  .drift-mesh   { animation: drift-across 60s linear infinite; }

  .marquee__track { animation: marquee 48s linear infinite; }
  .marquee:hover .marquee__track { animation-play-state: paused; }

  /* a sage glint that falls down the hero scroll cue */
  .scroll-cue i::after {
    content: ""; position: absolute; inset: 0;
    background: var(--sage);
    animation: cue-fall 2.6s var(--ease-soft) infinite;
  }
  .nav__logo i { animation: dot-breathe 6s ease-in-out infinite; }
}

/* --------------------------------------------------------------------------
   Reduced motion: show everything, move nothing.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .fade-up, .fade-up-slow, .fade-in, .scale-in, .slide-left, .slide-right,
  .hero-stagger > *, .nav__fade, .hero__visual, .stagger > *, .img-reveal, .load {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
  .mask-line { overflow: visible; }
  .mask-line > span { transform: none !important; }
}
