/* 
:root{
  --tr-ease: cubic-bezier(0.65, 0, 0.35, 1);

  --tr-h-x: -34px;
  --tr-h-y: 14px;
  --tr-h-dur: 0.9s;

  --tr-p-x: -16px;
  --tr-p-y: 0px;
  --tr-p-dur: 0.7s;

  --tr-u-x: -10px;
  --tr-u-y: 8px;
  --tr-u-dur: 0.5s;
}


.reveal-tr{
  opacity: 0;
  clip-path: inset(0 100% 0 0);
  -webkit-clip-path: inset(0 100% 0 0);
  transform: translate3d(var(--tr-x, var(--tr-h-x)), var(--tr-y, var(--tr-h-y)), 0);
  transition:
    clip-path var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    -webkit-clip-path var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    opacity var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    transform var(--tr-dur, var(--tr-h-dur)) var(--tr-ease);
  transition-delay: var(--tr-delay, 0s);
  will-change: transform, opacity, clip-path;
}

.reveal-tr.is-revealed{
  opacity: 1;
  clip-path: inset(0 0 0 0);
  -webkit-clip-path: inset(0 0 0 0);
  transform: translate3d(0, 0, 0);
}

.reveal-tr--heading{
  --tr-x: var(--tr-h-x);
  --tr-y: var(--tr-h-y);
  --tr-dur: var(--tr-h-dur);
}

.reveal-tr--para{
  --tr-x: var(--tr-p-x);
  --tr-y: var(--tr-p-y);
  --tr-dur: var(--tr-p-dur);
}


.reveal-fade{
  opacity: 0;
  transform: translate3d(var(--tr-u-x), var(--tr-u-y), 0);
  transition:
    opacity var(--tr-u-dur) var(--tr-ease),
    transform var(--tr-u-dur) var(--tr-ease);
  transition-delay: var(--tr-delay, 0s);
  will-change: transform, opacity;
}

.reveal-fade.is-revealed{
  opacity: 1;
  transform: translate3d(0, 0, 0);
}


[data-swap]{
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}

[data-swap] > .swap-label{
  display: inline-block;
  transition: transform 0.45s var(--tr-ease), opacity 0.45s var(--tr-ease);
}

[data-swap]::after{
  content: attr(data-swap);
  position: absolute;
  left: 0;
  top: 100%;
  display: inline-block;
  transition: transform 0.45s var(--tr-ease);
  pointer-events: none;
  white-space: nowrap;
}

[data-swap]:hover > .swap-label,
[data-swap]:focus-visible > .swap-label{
  transform: translateY(-100%);
  opacity: 0;
}

[data-swap]:hover::after,
[data-swap]:focus-visible::after{
  transform: translateY(-100%);
}


@media (max-width: 768px){
  :root{
    --tr-h-x: -18px;
    --tr-h-y: 8px;
    --tr-h-dur: 0.7s;

    --tr-p-x: -10px;
    --tr-p-dur: 0.55s;

    --tr-u-x: -6px;
    --tr-u-y: 5px;
    --tr-u-dur: 0.4s;
  }
}


@media (prefers-reduced-motion: reduce){
  .reveal-tr,
  .reveal-fade{
    opacity: 1 !important;
    clip-path: inset(0 0 0 0) !important;
    -webkit-clip-path: inset(0 0 0 0) !important;
    transform: none !important;
    transition: none !important;
  }
  [data-swap] > .swap-label,
  [data-swap]::after{
    transition: none !important;
  }
} */

/* ==========================================================================
   GrowStation — Premium Text Reveal System
   Cinematic left→right mask reveal for headings/paragraphs,
   plus a quieter fade-slide for small UI text (nav, buttons, labels, stats).

   Everything here is additive: it only touches elements that carry the
   .reveal-tr / .reveal-fade classes, which reveal.js applies at runtime.
   Nothing in this file overrides existing layout, color or type rules.

   FIX (added): a CSS-only fallback. If the JavaScript that adds
   .is-revealed never fires in time (or fires on an element that later
   gets replaced/rebuilt by the i18n script), the element still becomes
   visible on its own after a short delay instead of staying hidden
   forever until the page is refreshed.
   ========================================================================== */

:root{
  --tr-ease: cubic-bezier(0.65, 0, 0.35, 1);

  /* heading (dramatic) — negative x = starts to the LEFT, slides right into place */
  --tr-h-x: -34px;
  --tr-h-y: 14px;
  --tr-h-dur: 0.9s;

  /* paragraph (subtle) */
  --tr-p-x: -16px;
  --tr-p-y: 0px;
  --tr-p-dur: 0.7s;

  /* small UI text — fade/slide only, no mask */
  --tr-u-x: -10px;
  --tr-u-y: 8px;
  --tr-u-dur: 0.5s;

  /* fallback timing — must be longer than any normal reveal delay you use */
  --tr-fallback-delay: 1.4s;
}

/* ---------- Mask reveal (headings / paragraphs / quotes) ---------- */

.reveal-tr{
  opacity: 0;
  clip-path: inset(0 100% 0 0);
  -webkit-clip-path: inset(0 100% 0 0);
  transform: translate3d(var(--tr-x, var(--tr-h-x)), var(--tr-y, var(--tr-h-y)), 0);
  transition:
    clip-path var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    -webkit-clip-path var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    opacity var(--tr-dur, var(--tr-h-dur)) var(--tr-ease),
    transform var(--tr-dur, var(--tr-h-dur)) var(--tr-ease);
  transition-delay: var(--tr-delay, 0s);
  will-change: transform, opacity, clip-path;

  /* FIX: safety-net animation. Runs once, does nothing until
     --tr-fallback-delay has passed, then forces the element visible.
     If .is-revealed is added by JS before this fires, that class's
     transition takes over visually and this has nothing left to do. */
  animation: reveal-fallback 0s linear var(--tr-fallback-delay) forwards;
}

.reveal-tr.is-revealed{
  opacity: 1;
  clip-path: inset(0 0 0 0);
  -webkit-clip-path: inset(0 0 0 0);
  transform: translate3d(0, 0, 0);
}

/* heading variant — bigger travel, bit more drama */
.reveal-tr--heading{
  --tr-x: var(--tr-h-x);
  --tr-y: var(--tr-h-y);
  --tr-dur: var(--tr-h-dur);
}

/* paragraph / quote variant — quieter */
.reveal-tr--para{
  --tr-x: var(--tr-p-x);
  --tr-y: var(--tr-p-y);
  --tr-dur: var(--tr-p-dur);
}

/* ---------- Fade-slide (nav, buttons, labels, stats, footer links) ------ */
/* Transform + opacity only — never touches layout, so it's safe on inline
   elements, flex children, sticky headers, etc. */

.reveal-fade{
  opacity: 0;
  transform: translate3d(var(--tr-u-x), var(--tr-u-y), 0);
  transition:
    opacity var(--tr-u-dur) var(--tr-ease),
    transform var(--tr-u-dur) var(--tr-ease);
  transition-delay: var(--tr-delay, 0s);
  will-change: transform, opacity;

  /* FIX: same safety net as above, for the fade/slide elements */
  animation: reveal-fallback 0s linear var(--tr-fallback-delay) forwards;
}

.reveal-fade.is-revealed{
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* FIX: the actual fallback keyframe. Forces every hidden state
   (opacity, clip-path, transform) back to "shown" regardless of
   which variant the element used. */
@keyframes reveal-fallback{
  to{
    opacity: 1;
    clip-path: inset(0 0 0 0);
    -webkit-clip-path: inset(0 0 0 0);
    transform: translate3d(0, 0, 0);
  }
}

/* ---------- Premium button/link hover text-swap ---------- */
/* Applied only to elements marked [data-swap]. The visible label slides up
   and out while a duplicate slides up from below, via a generated ::after
   layer — the original text node (and any i18n binding on it) is untouched. */

[data-swap]{
  position: relative;
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
}

[data-swap] > .swap-label{
  display: inline-block;
  transition: transform 0.45s var(--tr-ease), opacity 0.45s var(--tr-ease);
}

[data-swap]::after{
  content: attr(data-swap);
  position: absolute;
  left: 0;
  top: 100%;
  display: inline-block;
  transition: transform 0.45s var(--tr-ease);
  pointer-events: none;
  white-space: nowrap;
}

[data-swap]:hover > .swap-label,
[data-swap]:focus-visible > .swap-label{
  transform: translateY(-100%);
  opacity: 0;
}

[data-swap]:hover::after,
[data-swap]:focus-visible::after{
  transform: translateY(-100%);
}

/* ---------- Mobile: shorter travel, snappier timing ---------- */

@media (max-width: 768px){
  :root{
    --tr-h-x: -18px;
    --tr-h-y: 8px;
    --tr-h-dur: 0.7s;

    --tr-p-x: -10px;
    --tr-p-dur: 0.55s;

    --tr-u-x: -6px;
    --tr-u-y: 5px;
    --tr-u-dur: 0.4s;
  }
}

/* ---------- Accessibility ---------- */

@media (prefers-reduced-motion: reduce){
  .reveal-tr,
  .reveal-fade{
    opacity: 1 !important;
    clip-path: inset(0 0 0 0) !important;
    -webkit-clip-path: inset(0 0 0 0) !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  [data-swap] > .swap-label,
  [data-swap]::after{
    transition: none !important;
  }
}