/**
 * Text Effects 26 — frontend styles for [effect:…] placeholders.
 *
 * Markup contract (produced by TextEffectTransformationEventListener,
 * animated by text-effects.js):
 *   <span class="text-effect text-{type}" data-effect="{type}" data-*>
 *     <span class="text-effect__item">…</span> …
 *   </span>
 *
 * State classes added by the engine:
 *   .te-ready    — JS initialized the element
 *   .te-static   — edit context / no animation (show plain content)
 *   .is-active   — current segment (swap)
 *   .is-visible  — entered viewport (reveal / highlight)
 *
 * Timing defaults are CSS custom properties so they can be themed globally;
 * inline data-options override them per instance via the engine.
 */

.text-effect {
    --te-speed: 400ms;       /* swap transition duration  */
    --te-duration: 600ms;    /* reveal / highlight duration */
    --te-delay: 0ms;
    --te-highlight-color: rgba(255, 214, 0, 0.45);
}

/* ---------------------------------------------------------------- swap */

.text-swap {
    display: inline-grid;
    vertical-align: bottom;
}

.text-swap .text-effect__item {
    grid-area: 1 / 1;
    opacity: 0;
    transition: opacity var(--te-speed) ease, transform var(--te-speed) ease;
}

.text-swap .text-effect__item.is-active {
    opacity: 1;
}

/* Show the first segment before JS runs, in edit context, or as no-JS fallback. */
.text-swap:not(.te-ready) .text-effect__item:first-child,
.text-swap.te-static .text-effect__item:first-child {
    opacity: 1;
}

/* slide variant — classic rotator: outgoing word slides up & out, incoming
   word slides in from below. Needs directional keyframes (a single static
   transform can't be both "enters from below" and "leaves to top"). */
.text-swap[data-transition="slide"] {
    overflow: hidden;
}
.text-swap[data-transition="slide"] .text-effect__item {
    opacity: 0;
}
/* Before the first swap (.te-playing absent) the active segment is just static —
   the first segment must not slide in on load. */
.text-swap[data-transition="slide"] .text-effect__item.is-active {
    opacity: 1;
}
/* Once playing, the entering/leaving segments use the directional keyframes. */
.text-swap[data-transition="slide"].te-playing .text-effect__item.is-active {
    animation: te-swap-in var(--te-speed) ease both;
}
.text-swap[data-transition="slide"].te-playing .text-effect__item.is-leaving {
    animation: te-swap-out var(--te-speed) ease both;
}

@keyframes te-swap-in {
    from { opacity: 0; transform: translateY(100%); }
    to   { opacity: 1; transform: translateY(0); }
}
@keyframes te-swap-out {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-100%); }
}

/* none variant: instant swap */
.text-swap[data-transition="none"] .text-effect__item {
    transition: none;
}

/* ----------------------------------------------------------- typewriter */

.text-typewriter .text-effect__item {
    display: none;
}

/* Show the first segment before JS runs, in edit context, or as no-JS fallback. */
.text-typewriter:not(.te-ready) .text-effect__item:first-child,
.text-typewriter.te-static .text-effect__item:first-child {
    display: inline;
}

.text-effect__cursor {
    display: inline-block;
    width: 0.08em;
    min-width: 1px;
    height: 1em;
    margin-left: 0.06em;
    background: currentColor;
    vertical-align: text-bottom;
    animation: te-blink 1s steps(1) infinite;
}

@keyframes te-blink {
    50% { opacity: 0; }
}

/* --------------------------------------------------------------- reveal */

.text-reveal {
    display: inline-block;
    opacity: 0;
    transition:
        opacity var(--te-duration) ease var(--te-delay),
        transform var(--te-duration) ease var(--te-delay);
    will-change: opacity, transform;
}

.text-reveal[data-animation="up"]    { transform: translateY(1em); }
.text-reveal[data-animation="left"]  { transform: translateX(-1em); }
.text-reveal[data-animation="right"] { transform: translateX(1em); }
.text-reveal[data-animation="fade"]  { transform: none; }

.text-reveal.is-visible {
    opacity: 1;
    transform: none;
}

/* No-JS / edit fallback: keep content visible. */
.text-reveal:not(.te-ready),
.text-reveal.te-static {
    opacity: 1;
    transform: none;
}

/* word stagger: animate the words, not the container */
.text-reveal[data-stagger="word"] {
    opacity: 1;
    transform: none;
}
.text-reveal[data-stagger="word"] .text-effect__word {
    display: inline-block;
    opacity: 0;
    transform: translateY(0.6em);
    transition:
        opacity var(--te-duration) ease var(--te-word-delay, 0ms),
        transform var(--te-duration) ease var(--te-word-delay, 0ms);
}
.text-reveal[data-stagger="word"].is-visible .text-effect__word {
    opacity: 1;
    transform: none;
}

/* ------------------------------------------------------------ highlight */

.text-highlight {
    background-image: linear-gradient(var(--te-highlight-color), var(--te-highlight-color));
    background-repeat: no-repeat;
    background-position: 0 88%;
    background-size: 0% 0.35em;
    transition: background-size var(--te-duration) ease var(--te-delay);
}

.text-highlight.is-visible {
    background-size: 100% 0.35em;
}

/* underline variant */
.text-highlight[data-style="underline"] {
    background-position: 0 100%;
    background-size: 0% 0.12em;
}
.text-highlight[data-style="underline"].is-visible {
    background-size: 100% 0.12em;
}

/* strike variant */
.text-highlight[data-style="strike"] {
    background-position: 0 50%;
    background-size: 0% 0.14em;
}
.text-highlight[data-style="strike"].is-visible {
    background-size: 100% 0.14em;
}

/* No-JS / edit fallback: show the highlight fully. */
.text-highlight:not(.te-ready),
.text-highlight.te-static {
    background-size: 100% 0.35em;
}
.text-highlight[data-style="underline"]:not(.te-ready),
.text-highlight[data-style="underline"].te-static {
    background-size: 100% 0.12em;
}
.text-highlight[data-style="strike"]:not(.te-ready),
.text-highlight[data-style="strike"].te-static {
    background-size: 100% 0.14em;
}

/* --------------------------------------------------------------- wavy */

.text-wavy .text-effect__char {
    display: inline-block;
    animation: te-wave var(--te-duration, 1200ms) ease-in-out infinite;
    animation-delay: calc(var(--te-char-index, 0) * var(--te-step, 60ms));
    will-change: transform;
}

@keyframes te-wave {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(calc(var(--te-amplitude, 8px) * -1)); }
}

/* --------------------------------------------------------------- split */

.text-split {
    overflow: hidden;
}
.text-split .text-effect__item {
    display: inline-block;
    opacity: 0;
    transition:
        transform var(--te-duration, 600ms) ease var(--te-delay, 0ms),
        opacity var(--te-duration, 600ms) ease var(--te-delay, 0ms);
    will-change: transform, opacity;
}
/* odd parts enter from the left, even parts from the right — they meet in the middle */
.text-split .text-effect__item:nth-child(odd)  { transform: translateX(calc(var(--te-distance, 60px) * -1)); }
.text-split .text-effect__item:nth-child(even) { transform: translateX(var(--te-distance, 60px)); }
.text-split.is-visible .text-effect__item {
    opacity: 1;
    transform: none;
}
/* No-JS / edit fallback: show the joined text. */
.text-split:not(.te-ready) .text-effect__item,
.text-split.te-static .text-effect__item {
    opacity: 1;
    transform: none;
}

/* --------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
    .text-effect,
    .text-effect * {
        transition: none !important;
        animation: none !important;
    }
    .text-swap .text-effect__item.is-active,
    .text-reveal,
    .text-reveal .text-effect__word,
    .text-split .text-effect__item {
        opacity: 1 !important;
        transform: none !important;
    }
    .text-effect__cursor { display: none; }
}
