/* ============================================================================
   ENOK Transitions Kit — copy-paste motion for real apps.
   Zero dependencies. CSP-safe. No build step.

   Use the classes on their own, or pair with transitions.js for scroll reveals
   and modal wiring. Every recipe respects prefers-reduced-motion.

   Free to use, copy, and ship in your projects — see LICENSE.txt.
   ENOK · the open media company · enok.com
   ============================================================================ */

:root {
  /* ---- Named easing curves (reuse anywhere) ---- */
  --tx-ease-soft:   cubic-bezier(.22, .61, .36, 1);   /* gentle ease-out       */
  --tx-ease-quint:  cubic-bezier(.22, 1, .36, 1);     /* long-tail ease-out    */
  --tx-ease-spring: cubic-bezier(.34, 1.56, .64, 1);  /* overshoot spring      */
  --tx-ease-inout:  cubic-bezier(.65, 0, .35, 1);     /* symmetric in-out      */

  /* ---- Durations ---- */
  --tx-fast: 180ms;
  --tx-base: 320ms;
  --tx-slow: 520ms;

  /* ---- Travel + stagger ---- */
  --tx-rise: 14px;   /* how far reveals translate before settling */
  --tx-step: 60ms;   /* delay added per staggered item (--tx-i)   */
}

/* ----------------------------------------------------------------------------
   1 · PAGE ENTER
   Fade + rise the whole view on load. Put .tx-page on a wrapper.
   Also opts into the native View Transitions API where supported (progressive).
   ---------------------------------------------------------------------------- */
.tx-page {
  animation: tx-page-in var(--tx-slow) var(--tx-ease-quint) both;
}
@keyframes tx-page-in {
  from { opacity: 0; transform: translateY(var(--tx-rise)); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

/* ----------------------------------------------------------------------------
   2 · REVEAL ON SCROLL
   Elements start hidden; add .is-in (transitions.js does this via
   IntersectionObserver) to reveal. Direction modifiers + per-item stagger.
   ---------------------------------------------------------------------------- */
.tx-reveal {
  opacity: 0;
  transform: translateY(var(--tx-rise));
  transition:
    opacity   var(--tx-base) var(--tx-ease-quint),
    transform var(--tx-base) var(--tx-ease-quint);
  transition-delay: calc(var(--tx-i, 0) * var(--tx-step));
  will-change: opacity, transform;
}
.tx-reveal.tx-left  { transform: translateX(calc(-1 * var(--tx-rise))); }
.tx-reveal.tx-right { transform: translateX(var(--tx-rise)); }
.tx-reveal.tx-scale { transform: scale(.94); }
.tx-reveal.is-in    { opacity: 1; transform: none; }

/* ----------------------------------------------------------------------------
   3 · LIST SPRING
   Items pop in with a little overshoot, staggered by --tx-i.
   ---------------------------------------------------------------------------- */
.tx-spring {
  opacity: 0;
  transform: translateY(10px) scale(.98);
  transition:
    opacity   var(--tx-base) var(--tx-ease-spring),
    transform var(--tx-base) var(--tx-ease-spring);
  transition-delay: calc(var(--tx-i, 0) * var(--tx-step));
  will-change: opacity, transform;
}
.tx-spring.is-in { opacity: 1; transform: none; }

/* ----------------------------------------------------------------------------
   4 · MODAL PHYSICS
   Backdrop fades; panel springs up + scales in. Toggle .is-open on .tx-modal.
   Structure:  .tx-modal > .tx-modal__scrim + .tx-modal__panel
   ---------------------------------------------------------------------------- */
.tx-modal {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center; padding: 20px;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition:
    opacity    var(--tx-base) var(--tx-ease-soft),
    visibility 0s linear var(--tx-base);
}
.tx-modal__scrim {
  position: absolute; inset: 0;
  background: rgba(24, 20, 16, .42);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.tx-modal__panel {
  position: relative;
  transform: translateY(12px) scale(.96);
  transition: transform var(--tx-base) var(--tx-ease-spring);
  will-change: transform;
}
.tx-modal.is-open {
  opacity: 1; visibility: visible; pointer-events: auto;
  transition-delay: 0s;
}
.tx-modal.is-open .tx-modal__panel { transform: none; }

/* ----------------------------------------------------------------------------
   5 · ONE-SHOT HELPERS
   Fire once on insert. Add the class; remove/re-add to replay.
   ---------------------------------------------------------------------------- */
.tx-fade { animation: tx-fade var(--tx-base) var(--tx-ease-soft) both; }
.tx-pop  { animation: tx-pop  var(--tx-base) var(--tx-ease-spring) both; }
@keyframes tx-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes tx-pop  {
  from { opacity: 0; transform: scale(.9); }
  to   { opacity: 1; transform: none; }
}

/* ----------------------------------------------------------------------------
   ACCESSIBILITY — honour reduced-motion. Content still shows; motion is cut.
   ---------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .tx-page,
  .tx-reveal,
  .tx-spring,
  .tx-modal,
  .tx-modal__panel,
  .tx-fade,
  .tx-pop {
    animation: none !important;
    transition-duration: 1ms !important;
    transition-delay: 0s !important;
  }
  .tx-reveal,
  .tx-spring { opacity: 1 !important; transform: none !important; }
}

/* ============================================================================
   ENOK Transitions Kit · v0.2 — COMPONENT MOTION
   Six owned recipes for the moments a real app actually animates:
     6 · Toast        rise + fade + scale
     7 · Tabs         sliding pill indicator
     8 · Accordion    height + chevron morph
     9 · Tooltip      delay in, instant out
    10 · Error shake  damped, on its own cubic-bezier
    11 · Skeleton     ghost → content crossfade
   Same laws as v0.1: zero dependencies, CSP-safe, every recipe collapses under
   prefers-reduced-motion — and MODULARITY: components sit on tangible shadows,
   never a global dim overlay.
   ============================================================================ */

:root {
  --tx-toast-rise: 16px;                            /* how far a toast travels in     */
  --tx-tip-delay:  380ms;                           /* hover dwell before a tooltip   */
  --tx-ease-exit:  cubic-bezier(.4, 0, 1, 1);       /* quick accelerating exit        */
  --tx-ease-shake: cubic-bezier(.36, .07, .19, .97);/* the error-shake curve          */
  --tx-skel-base:  #E9E2D3;                          /* skeleton ghost tint (override) */
}

/* ----------------------------------------------------------------------------
   6 · TOAST — rise + fade + scale
   Tangible cards that stack in a corner region and ride their own shadow —
   no backdrop, no dim (MODULARITY). Driven by Transitions.toast(); the default
   skin here makes them usable out of the box and is easy to override.
   Structure:  .tx-toast-region.tx-toast-region--br > .tx-toast (.is-in / .is-out)
   ---------------------------------------------------------------------------- */
.tx-toast-region {
  position: fixed; z-index: 1100; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 10px;
  pointer-events: none;
}
.tx-toast-region--br { right: 18px; bottom: 18px; align-items: flex-end; }
.tx-toast-region--bl { left: 18px;  bottom: 18px; align-items: flex-start; }
.tx-toast-region--tr { right: 18px; top: 18px;    align-items: flex-end; }
.tx-toast-region--tc { left: 50%;   top: 18px;    align-items: center; transform: translateX(-50%); }

.tx-toast {
  pointer-events: auto;
  min-width: 200px; max-width: 340px;
  display: flex; align-items: center; gap: 12px;
  padding: 12px 15px;
  font-size: 13.5px; line-height: 1.45; color: #2E2A24;
  background: #FBF8F1; border: 1px solid #E5DDCC; border-radius: 12px;
  box-shadow: 0 14px 34px rgba(46, 42, 36, .20);   /* tangible shadow, no scrim */
  opacity: 0;
  transform: translateY(var(--tx-toast-rise)) scale(.96);
  transition:
    opacity   var(--tx-base) var(--tx-ease-quint),
    transform var(--tx-base) var(--tx-ease-spring);
  will-change: opacity, transform;
}
/* toasts in top regions rise DOWN from above their edge */
.tx-toast-region--tr .tx-toast,
.tx-toast-region--tc .tx-toast {
  transform: translateY(calc(-1 * var(--tx-toast-rise))) scale(.96);
}
.tx-toast.is-in  { opacity: 1; transform: none; }
.tx-toast.is-out {
  opacity: 0; transform: translateY(6px) scale(.98);
  transition:
    opacity   var(--tx-fast) var(--tx-ease-exit),
    transform var(--tx-fast) var(--tx-ease-exit);
}
.tx-toast[data-tone="success"] { border-left: 3px solid #77754B; }
.tx-toast[data-tone="error"]   { border-left: 3px solid #B4654A; }
.tx-toast[data-tone="info"]    { border-left: 3px solid #6E6558; }
.tx-toast__action {
  margin-left: auto; flex: 0 0 auto;
  font: inherit; font-size: 12.5px; font-weight: 600;
  color: #B4654A; background: none; border: 0; padding: 2px 4px;
  border-radius: 6px; cursor: pointer;
}
.tx-toast__action:hover { text-decoration: underline; }

/* ----------------------------------------------------------------------------
   7 · TABS — sliding pill indicator
   A pill glides under the active tab, resizing to fit it. transitions.js
   measures the active tab and sets --tx-pill-x / --tx-pill-w; the pill only
   starts animating once it's .tx-pill-armed (no slide-in from 0,0 on load).
   Structure:  [data-tx-tabs] > .tx-tabs__list > [data-tx-tab]… + .tx-tabs__pill
   ---------------------------------------------------------------------------- */
.tx-tabs__list { position: relative; display: inline-flex; }
.tx-tabs__pill {
  position: absolute; top: 0; left: 0; height: 100%;
  width: var(--tx-pill-w, 0);
  transform: translateX(var(--tx-pill-x, 0));
  transition: none;
  will-change: transform, width; pointer-events: none;
}
.tx-tabs__pill.tx-pill-armed {
  transition:
    transform var(--tx-base) var(--tx-ease-quint),
    width     var(--tx-base) var(--tx-ease-quint);
}
[data-tx-tab] { position: relative; z-index: 1; }
[data-tx-panel] { display: none; }
[data-tx-panel].is-active {
  display: block;
  animation: tx-fade var(--tx-base) var(--tx-ease-soft) both;
}

/* ----------------------------------------------------------------------------
   8 · ACCORDION — height + chevron morph
   Panels animate their real height (0 ⇄ content) then settle to auto so they
   stay responsive; the chevron rotates in step. transitions.js does the height
   math (CSS cannot animate to auto).
   Structure:  [data-tx-accordion] > .tx-acc__item > [data-tx-acc-trigger] + .tx-acc__panel
   ---------------------------------------------------------------------------- */
.tx-acc__panel {
  height: 0; overflow: hidden;
  transition: height var(--tx-base) var(--tx-ease-quint);
  will-change: height;
}
.tx-acc__chevron {
  display: inline-block; flex: 0 0 auto;
  transition: transform var(--tx-base) var(--tx-ease-spring);
}
.tx-acc__item.is-open > [data-tx-acc-trigger] .tx-acc__chevron { transform: rotate(90deg); }

/* ----------------------------------------------------------------------------
   9 · TOOLTIP — delay in, instant out
   Appears after a short hover/focus dwell; vanishes the instant you leave.
   One shared floating bubble on a real shadow — never a dim. Declarative:
   any [data-tx-tooltip="…"] element is wired automatically.
   ---------------------------------------------------------------------------- */
.tx-tip {
  position: fixed; z-index: 1200; pointer-events: none;
  max-width: 240px; padding: 7px 10px;
  font-size: 12.5px; line-height: 1.4; color: #FBF8F1;
  background: #2E2A24; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(46, 42, 36, .28);
  opacity: 0; transform: translateY(4px) scale(.96);
  transition:
    opacity   var(--tx-fast) var(--tx-ease-soft),
    transform var(--tx-fast) var(--tx-ease-soft);
  will-change: opacity, transform;
}
.tx-tip.is-in { opacity: 1; transform: none; }
.tx-tip.tx-tip-instant { transition: none; }   /* instant-out: cut the fade */

/* ----------------------------------------------------------------------------
   10 · ERROR SHAKE
   A short, damped horizontal shake on its own cubic-bezier — for rejected
   inputs and failed submits. One-shot: Transitions.shake(el) adds the class and
   strips it on animationend so it can fire again. Or add .tx-shake yourself.
   ---------------------------------------------------------------------------- */
.tx-shake { animation: tx-shake var(--tx-slow) var(--tx-ease-shake) both; }
@keyframes tx-shake {
  10%, 90%       { transform: translateX(-1px); }
  20%, 80%       { transform: translateX(2px); }
  30%, 50%, 70%  { transform: translateX(-6px); }
  40%, 60%       { transform: translateX(6px); }
}

/* ----------------------------------------------------------------------------
   11 · SKELETON → CONTENT CROSSFADE  (bonus)
   A shimmering placeholder that dissolves into the real content when it lands.
   Add .is-ready (or call Transitions.ready(el)): the ghost fades out as the
   content fades in. Shimmer holds still under reduced-motion.
   Structure:  .tx-skel > .tx-skel__ghost + .tx-skel__real
   ---------------------------------------------------------------------------- */
.tx-skel { position: relative; }
.tx-skel__ghost {
  position: absolute; inset: 0; border-radius: inherit;
  background:
    linear-gradient(100deg, rgba(255,255,255,0) 32%, rgba(255,255,255,.55) 50%, rgba(255,255,255,0) 68%),
    var(--tx-skel-base, #E9E2D3);
  background-size: 220% 100%, 100% 100%;
  background-repeat: no-repeat;
  animation: tx-shimmer 1.4s linear infinite;
  transition: opacity var(--tx-base) var(--tx-ease-soft);
}
.tx-skel__real {
  opacity: 0;
  transition: opacity var(--tx-slow) var(--tx-ease-soft);
}
.tx-skel.is-ready .tx-skel__ghost { opacity: 0; }
.tx-skel.is-ready .tx-skel__real  { opacity: 1; }
@keyframes tx-shimmer {
  from { background-position: 120% 0, 0 0; }
  to   { background-position: -120% 0, 0 0; }
}

/* ----------------------------------------------------------------------------
   ACCESSIBILITY — v0.2 recipes honour reduced-motion. Content still shows;
   motion is cut. Toasts still appear, panels still open, tooltips still show —
   just without the travel.
   ---------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .tx-toast, .tx-toast.is-in, .tx-toast.is-out,
  .tx-tabs__pill, .tx-tabs__pill.tx-pill-armed,
  .tx-acc__panel, .tx-acc__chevron,
  .tx-tip, .tx-shake,
  [data-tx-panel].is-active,
  .tx-skel__ghost, .tx-skel__real {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    transition-delay: 0s !important;
  }
  .tx-toast { opacity: 1 !important; transform: none !important; }
  .tx-skel__ghost { animation: none !important; }
}
