/* ============================================================
   AVANTCHA — SCROLL REVEAL ANIMATION LIBRARY
   Load this once, globally, in the theme (not per-component).

   USAGE — single element:
     <div data-reveal class="reveal-fade-up">...</div>

   USAGE — staggered group (children animate one by one):
     <div data-reveal-group data-stagger-delay="80">
       <div data-reveal data-reveal-index="0" class="reveal-fade-up">...</div>
       <div data-reveal data-reveal-index="1" class="reveal-fade-up">...</div>
     </div>
   data-stagger-delay is in milliseconds. The trigger script
   (reveal-trigger.js) reads it + each child's data-reveal-index,
   computes index * delay, and writes the result to --reveal-delay
   in seconds. You never need to set --reveal-delay by hand.

   USAGE — letters (per-character reveal on a text element):
     <h2 data-reveal-text class="reveal-letters">Est. Bangkok</h2>

   Pairs with reveal-trigger.js, which adds .is-revealed to
   trigger these once the element scrolls into view.

   TEST BUILD — scroll-scrubbed left/right reveals:
   .reveal-slide-left, .reveal-slide-right, .reveal-wipe-left and
   .reveal-wipe-right now progress WITH scroll position instead of
   firing once, via a @supports(animation-timeline: view()) block
   sitting right under each rule. No class names changed and no
   markup changes needed — same classes, same data-reveal usage.
   0% revealed as the element starts entering the viewport, 100%
   once fully entered, reversible on scroll-up. The keyframes use
   var(--reveal-ease) (not linear) as the animation timing
   function, so motion still decelerates into its landing — most
   of the travel happens early in the scroll range and it settles
   softly rather than tracking scroll 1:1 and stopping dead at the
   end. Browsers without support for the scroll-timeline spec keep
   the original fire-once transition unchanged. Every other class
   in this file (fade-up, veil, curtain, tilt, etc.) is untouched.

   DESIGN PRINCIPLES BEHIND THIS FILE — read this before tuning:
   1. ONE easing curve for everything (--reveal-ease). A system
      with a single, consistent motion signature reads as more
      considered than one with several curves fighting each
      other, even if each looks fine in isolation.
   2. Ease-OUT only, never ease-in-out, for entrances. An
      ease-in-out curve decelerates then re-accelerates — correct
      for something that reverses or loops, but a one-time reveal
      should only ever decelerate, or it reads as a small hesitation
      partway through.
   3. Opacity resolves slightly BEFORE transform/filter finishes,
      everywhere. Content becomes legible while still gently
      settling into place, rather than staying invisible until
      all motion has completely stopped.
   4. Small distances, longer durations. Restraint reads as
      intentional; a bigger movement reads as eager.

   PACING VARIABLES — tune the whole system's feel from here.
   ============================================================ */

:root{
  --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1); /* the ONE curve — smooth, decelerating, no jolt, no hesitation */
  --reveal-duration-fast: 0.95s;    /* opacity, in most rules — resolves first */
  --reveal-duration-base: 1.2s;     /* standard movement */
  --reveal-duration-slow: 1.45s;    /* filter, wipes, secondary movement */
  --reveal-duration-slower: 1.7s;   /* the largest, most deliberate motions */
}

/* Respect reduced-motion first: everything just appears, no motion. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  .reveal-scrub-slide-left,
  .reveal-scrub-slide-right,
  .reveal-scrub-wipe-left,
  .reveal-scrub-wipe-right{
    transition:none !important;
    animation:none !important;
    opacity:1 !important;
    transform:none !important;
    filter:none !important;
    clip-path:none !important;
    box-shadow:none !important;
  }
}

[data-reveal]{
  will-change:opacity, transform, filter;
}

/* ---- 1. Fade Up ---------------------------------------------------
   Quiet, versatile default. Content lifts gently into place —
   a short, restrained rise, not a jump. */
.reveal-fade-up{
  opacity:0;
  transform:translateY(18px);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-fade-up.is-revealed{
  opacity:1;
  transform:none; /* not translateY(0) — avoids creating a new
                      containing block for position:absolute
                      descendants (e.g. dropdown menus) */
}

/* ---- 2. Fade In -----------------------------------------------------
   No motion at all — for dense copy blocks or sections that
   shouldn't compete with something already moving nearby. */
.reveal-fade-in{
  opacity:0;
  transition:opacity var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-fade-in.is-revealed{
  opacity:1;
}

/* ---- 3. Soft Scale ----------------------------------------------------
   Content settles from a whisper of a zoom — barely perceptible
   as scale, reads as a gentle arrival rather than a zoom effect. */
.reveal-soft-scale{
  opacity:0;
  transform:scale(0.97);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-soft-scale.is-revealed{
  opacity:1;
  transform:none;
}

/* ---- 4. Slide From Left ------------------------------------------- */
.reveal-slide-left{
  opacity:0;
  transform:translateX(-32px);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-slide-left.is-revealed{
  opacity:1;
  transform:none;
}
/* Scroll-scrubbed enhancement — same class, no markup change.
   Where the browser supports it, this replaces the fire-once
   transition above with a continuous scroll-linked reveal: 0%
   as the element starts entering the viewport, 100% once it has
   fully entered, reversible on scroll-up. Unsupported browsers
   silently keep the fire-once behavior above (JS still adds
   .is-revealed as usual, no change needed there). */
@supports (animation-timeline: view()){
  .reveal-slide-left{
    animation:reveal-slide-left-scrub-kf var(--reveal-ease) both;
    animation-timeline:view();
    animation-range:entry 0% entry 100%;
  }
  @keyframes reveal-slide-left-scrub-kf{
    from{ opacity:0; transform:translateX(-32px); }
    to{ opacity:1; transform:none; }
  }
}

/* ---- 5. Slide From Right ------------------------------------------- */
.reveal-slide-right{
  opacity:0;
  transform:translateX(32px);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-slide-right.is-revealed{
  opacity:1;
  transform:none;
}
/* Scroll-scrubbed enhancement — see note above .reveal-slide-left. */
@supports (animation-timeline: view()){
  .reveal-slide-right{
    animation:reveal-slide-right-scrub-kf var(--reveal-ease) both;
    animation-timeline:view();
    animation-range:entry 0% entry 100%;
  }
  @keyframes reveal-slide-right-scrub-kf{
    from{ opacity:0; transform:translateX(32px); }
    to{ opacity:1; transform:none; }
  }
}

/* ---- 6. Veil (blur) ------------------------------------------------
   A soft focus pull — content resolves out of a light blur, not
   a heavy one. Elegant for hero imagery and editorial photography. */
.reveal-veil{
  opacity:0;
  filter:blur(9px);
  transform:scale(1.012);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              filter var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-veil.is-revealed{
  opacity:1;
  filter:blur(0);
  transform:none;
}

/* ---- 7. Curtain (clip-path wipe, vertical) ------------------------------
   A curtain lifts from the bottom to reveal the content beneath —
   reserved for section headers and short statements. */
.reveal-curtain{
  opacity:1;
  clip-path:inset(0 0 100% 0);
  transition:clip-path var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-curtain.is-revealed{
  clip-path:inset(0 0 0% 0);
}

/* ---- 8. Tracking In (letterspacing) ---------------------------------
   For short headings / eyebrows only. Letters draw together from
   a wide-but-not-extreme tracking — enough to read as intentional
   without feeling stretched. */
.reveal-tracking-in{
  opacity:0;
  letter-spacing:0.22em;
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              letter-spacing var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-tracking-in.is-revealed{
  opacity:1;
  letter-spacing:normal;
}

/* ---- 9. Rise ----------------------------------------------------------
   A more dramatic sibling of Fade Up — still restrained travel,
   just slower and a touch further. Reserved for large hero
   statements, not small cards. */
.reveal-rise{
  opacity:0;
  transform:translateY(40px);
  transition:opacity var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slower) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-rise.is-revealed{
  opacity:1;
  transform:none;
}

/* ---- 10. Corner Unfold (diagonal clip-path wipe) -----------------------
   The section unfolds diagonally from its bottom-left corner,
   like a page turning open. Spend it on one striking hero image. */
.reveal-corner{
  opacity:1;
  clip-path:polygon(0% 100%, 0% 100%, 0% 100%);
  transition:clip-path var(--reveal-duration-slower) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-corner.is-revealed{
  clip-path:polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}

/* ---- 11. Tilt (subtle 3D settle) --------------------------------------
   Content tilts up out of the page on a shallow perspective axis —
   a light touch, not a dramatic flip. Needs a parent with normal
   overflow (avoid overflow:hidden ancestors clipping the perspective). */
.reveal-tilt{
  opacity:0;
  transform:perspective(1000px) rotateX(-8deg) translateY(14px);
  transform-origin:bottom center;
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-tilt.is-revealed{
  opacity:1;
  transform:none;
}

/* ---- 12. Bloom (desaturate to full color) ------------------------------
   Starts softly muted, not fully grey, then blooms into full
   color and contrast. The color itself carries the reveal. */
.reveal-bloom{
  opacity:0.45;
  filter:grayscale(0.85) contrast(0.94);
  transform:translateY(8px);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              filter var(--reveal-duration-slower) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-bloom.is-revealed{
  opacity:1;
  filter:grayscale(0) contrast(1);
  transform:none;
}

/* ---- 13. Hairline Frame (thin border prints in) -------------------------
   A thin gold frame prints itself around the element as it
   settles in. Needs visible padding/edges — avoid on full-bleed
   images with no breathing room. Replace var(--gold) with your
   theme's accent color variable. */
.reveal-frame{
  opacity:0;
  transform:translateY(10px);
  box-shadow:inset 0 0 0 0px var(--gold);
  transition:opacity var(--reveal-duration-fast) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s),
              box-shadow var(--reveal-duration-fast) var(--reveal-ease) calc(0.35s + var(--reveal-delay, 0s));
}
.reveal-frame.is-revealed{
  opacity:1;
  transform:none;
  box-shadow:inset 0 0 0 1px var(--gold);
}

/* ---- 14. Glow (luxury pulse-in) -----------------------------------------
   A brief, restrained bloom of light accompanies the entrance,
   then settles to rest. Uses a keyframe rather than a transition
   so it can overshoot slightly before settling. Use sparingly —
   one accent element per page, not repeated across a grid. */
.reveal-glow{
  opacity:0;
}
.reveal-glow.is-revealed{
  animation:reveal-glow-kf var(--reveal-duration-slower) var(--reveal-ease) var(--reveal-delay, 0s) forwards;
}
@keyframes reveal-glow-kf{
  0%{
    opacity:0;
    transform:scale(0.97);
    box-shadow:0 0 0 rgba(188,156,106,0);
  }
  55%{
    opacity:1;
    transform:scale(1.008);
    box-shadow:0 0 20px var(--gold-dim, rgba(188,156,106,0.3));
  }
  100%{
    opacity:1;
    transform:none;
    box-shadow:0 0 0 rgba(188,156,106,0);
  }
}

/* ---- 15. Letters (per-character reveal) ---------------------------------
   Splits text into individual characters, each animating in with
   a tight, overlapping stagger and a light blur-resolve — an
   editorial "type-on" feel, restrained rather than mechanical.
   Requires the split logic in reveal-trigger.js (runs
   automatically on any element carrying data-reveal-text).
   Use only on short headings/eyebrows, never body copy — splitting
   a paragraph into hundreds of spans hurts performance and
   accessibility for no visual gain at that length. */
.reveal-letters{
  display:inline-block;
}
.reveal-letters .rl-word{
  display:inline-block;
  white-space:nowrap;
}
.reveal-letters .rl-char{
  display:inline-block;
  opacity:0;
  filter:blur(4px);
  transform:translateY(0.2em);
  transition:opacity var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s),
              transform var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s),
              filter var(--reveal-duration-base) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-letters .rl-char.is-revealed{
  opacity:1;
  filter:blur(0);
  transform:none;
}

/* ---- 16. Wipe From Left (image reveal, horizontal clip-path) -----------
   The image sweeps into view from its left edge outward — starts
   fully hidden, and the clipped portion shrinks away left-to-right.
   Works directly on an <img>, or on a container wrapping one.
   Pairs well with a hero image or a full-width feature photo;
   avoid on text content, since a partially-clipped sentence reads
   as a rendering glitch rather than an intentional reveal. */
.reveal-wipe-left{
  opacity:1;
  clip-path:inset(0 100% 0 0);
  transition:clip-path var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-wipe-left.is-revealed{
  clip-path:inset(0 0% 0 0);
}
/* Scroll-scrubbed enhancement — see note above .reveal-slide-left. */
@supports (animation-timeline: view()){
  .reveal-wipe-left{
    animation:reveal-wipe-left-scrub-kf var(--reveal-ease) both;
    animation-timeline:view();
    animation-range:entry 0% entry 100%;
  }
  @keyframes reveal-wipe-left-scrub-kf{
    from{ clip-path:inset(0 100% 0 0); }
    to{ clip-path:inset(0 0% 0 0); }
  }
}

/* ---- 17. Wipe From Right (image reveal, horizontal clip-path) ----------
   The mirror of the above — the image sweeps into view from its
   right edge outward, revealing right-to-left. */
.reveal-wipe-right{
  opacity:1;
  clip-path:inset(0 0 0 100%);
  transition:clip-path var(--reveal-duration-slow) var(--reveal-ease) var(--reveal-delay, 0s);
}
.reveal-wipe-right.is-revealed{
  clip-path:inset(0 0 0 0%);
}
/* Scroll-scrubbed enhancement — see note above .reveal-slide-left. */
@supports (animation-timeline: view()){
  .reveal-wipe-right{
    animation:reveal-wipe-right-scrub-kf var(--reveal-ease) both;
    animation-timeline:view();
    animation-range:entry 0% entry 100%;
  }
  @keyframes reveal-wipe-right-scrub-kf{
    from{ clip-path:inset(0 0 0 100%); }
    to{ clip-path:inset(0 0 0 0%); }
  }
}

/* ---- Legacy fixed-step delay helpers -----------------------------------
   Optional. Use only for small, fixed groups (max 6 items) where
   you're not using the data-reveal-group + data-stagger-delay
   system — e.g. hand-authored markup, not a Sline block loop.
   For anything driven by a block loop, prefer data-reveal-group
   (see top of file and reveal-trigger.js) — it has no item cap
   and stays correct if items are reordered. */
.reveal-delay-1{ transition-delay: 0.08s; animation-delay: 0.08s; }
.reveal-delay-2{ transition-delay: 0.16s; animation-delay: 0.16s; }
.reveal-delay-3{ transition-delay: 0.24s; animation-delay: 0.24s; }
.reveal-delay-4{ transition-delay: 0.32s; animation-delay: 0.32s; }
.reveal-delay-5{ transition-delay: 0.40s; animation-delay: 0.40s; }
.reveal-delay-6{ transition-delay: 0.48s; animation-delay: 0.48s; }
