/**
 * Text Effects System
 *
 * Self-contained CSS class library for text visual effects:
 * gradients, glow, stroke, shimmer, reveal, and hover effects.
 *
 * CSS variable namespace: --text-fx-*
 * Class namespace: .text-fx-*
 *
 * Uses DaisyUI OKLCH theme variables for automatic theme adaptation.
 */

/* ==========================================================================
   @property Declarations
   ========================================================================== */

@property --text-fx-angle {
    syntax: "<angle>";
    initial-value: 0deg;
    inherits: false;
}

/* ==========================================================================
   Base Variables
   ========================================================================== */

:root {
    --text-fx-color-1: var(--color-primary);
    --text-fx-color-2: var(--color-secondary);
    --text-fx-color-3: var(--color-accent);
    --text-fx-angle: 90deg;
    --text-fx-duration: 3s;
    --text-fx-glow-color: var(--color-primary);
    --text-fx-glow-size: 20px;
}

/* ==========================================================================
   Static Effects (8) — No Animation
   ========================================================================== */

/* Gradient — static two-color linear gradient */
.text-fx-gradient {
    background: linear-gradient(
        var(--text-fx-angle),
        var(--text-fx-color-1),
        var(--text-fx-color-2)
    );
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* Gradient Radial — radial gradient from center */
.text-fx-gradient-radial {
    background: radial-gradient(
        circle,
        var(--text-fx-color-1),
        var(--text-fx-color-2)
    );
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* Glow — soft luminous glow (3-layer text-shadow) */
.text-fx-glow {
    text-shadow:
        0 0 7px color-mix(in oklch, var(--color-primary) 50%, transparent),
        0 0 14px color-mix(in oklch, var(--color-primary) 30%, transparent),
        0 0 21px color-mix(in oklch, var(--color-primary) 15%, transparent);
}

/* Glow Intense — strong neon glow (5-layer text-shadow) */
.text-fx-glow-intense {
    text-shadow:
        0 0 4px color-mix(in oklch, var(--color-primary) 80%, transparent),
        0 0 11px color-mix(in oklch, var(--color-primary) 50%, transparent),
        0 0 19px color-mix(in oklch, var(--color-primary) 30%, transparent),
        0 0 40px color-mix(in oklch, var(--color-primary) 20%, transparent),
        0 0 80px color-mix(in oklch, var(--color-primary) 10%, transparent);
}

/* Shadow 3D — stacked offset shadows for depth */
.text-fx-shadow-3d {
    text-shadow:
        1px 1px 0 color-mix(in oklch, var(--color-base-content) 15%, transparent),
        2px 2px 0 color-mix(in oklch, var(--color-base-content) 12%, transparent),
        3px 3px 0 color-mix(in oklch, var(--color-base-content) 9%, transparent),
        4px 4px 0 color-mix(in oklch, var(--color-base-content) 6%, transparent),
        5px 5px 10px color-mix(in oklch, var(--color-base-content) 10%, transparent);
}

/* Shadow Retro — anaglyph red/cyan 3D offset */
.text-fx-shadow-retro {
    /* Tokenised 2026-09-14 (step 8). These were two hard-coded oklch() values —
       authored colour, the fourth instance this month — so the effect looked
       the same in all eighteen families and swapped with none of them. The look
       is a chromatic-aberration offset: two complementary role colours pulling
       apart, which is exactly what error and info are. */
    text-shadow:
        -2px 0 var(--color-error),
        2px 0 var(--color-info);
}

/* Outline — hollow/outlined text */
.text-fx-outline {
    -webkit-text-stroke: 1px currentColor;
    -webkit-text-fill-color: transparent;
    paint-order: stroke fill;
}

/* Outline Thick — bold outlined text */
.text-fx-outline-thick {
    -webkit-text-stroke: 3px currentColor;
    -webkit-text-fill-color: transparent;
    paint-order: stroke fill;
}

/* ==========================================================================
   Animated Effects (8) — CSS @keyframes
   ========================================================================== */

/* Gradient Flow — flowing gradient across text */
@keyframes text-fx-flow {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

.text-fx-gradient-flow {
    background: linear-gradient(
        var(--text-fx-angle),
        var(--text-fx-color-1),
        var(--text-fx-color-2),
        var(--text-fx-color-3),
        var(--text-fx-color-1)
    );
    background-size: 300% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: text-fx-flow var(--text-fx-duration) ease infinite;
}

/* Gradient Rotate — rotating gradient angle via @property */
@keyframes text-fx-rotate {
    from { --text-fx-angle: 0deg; }
    to { --text-fx-angle: 360deg; }
}

.text-fx-gradient-rotate {
    background: linear-gradient(
        var(--text-fx-angle),
        var(--text-fx-color-1),
        var(--text-fx-color-2)
    );
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: text-fx-rotate var(--text-fx-duration) linear infinite;
}

/* Shimmer — shine sweep across text */
@keyframes text-fx-shimmer {
    0% { background-position: -200% 0; }
    100% { background-position: 200% 0; }
}

.text-fx-shimmer {
    background: linear-gradient(
        90deg,
        currentColor 0%,
        currentColor 40%,
        rgba(255, 255, 255, 0.8) 50%,
        currentColor 60%,
        currentColor 100%
    );
    background-size: 200% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: text-fx-shimmer var(--text-fx-duration) ease-in-out infinite;
}

/* Glow Pulse — pulsing glow intensity */
@keyframes text-fx-glow-pulse {
    0%, 100% {
        text-shadow:
            0 0 7px color-mix(in oklch, var(--color-primary) 30%, transparent),
            0 0 14px color-mix(in oklch, var(--color-primary) 15%, transparent);
    }
    50% {
        text-shadow:
            0 0 14px color-mix(in oklch, var(--color-primary) 60%, transparent),
            0 0 28px color-mix(in oklch, var(--color-primary) 30%, transparent),
            0 0 42px color-mix(in oklch, var(--color-primary) 15%, transparent);
    }
}

.text-fx-glow-pulse {
    animation: text-fx-glow-pulse var(--text-fx-duration) ease-in-out infinite;
}

/* Color Cycle — rainbow hue rotation */
@keyframes text-fx-color-cycle {
    from { filter: hue-rotate(0deg); }
    to { filter: hue-rotate(360deg); }
}

.text-fx-color-cycle {
    animation: text-fx-color-cycle var(--text-fx-duration) linear infinite;
}

/* Blur In — focus-in from blur (plays once) */
@keyframes text-fx-blur-in {
    from {
        filter: blur(12px);
        opacity: 0;
    }
    to {
        filter: blur(0);
        opacity: 1;
    }
}

.text-fx-blur-in {
    animation: text-fx-blur-in 1s ease-out forwards;
}

/* Reveal Right — clip-path wipe from left to right (plays once) */
@keyframes text-fx-reveal-right {
    from { clip-path: inset(0 100% 0 0); }
    to { clip-path: inset(0 0 0 0); }
}

.text-fx-reveal-right {
    animation: text-fx-reveal-right 0.8s ease-out forwards;
}

/* Reveal Up — clip-path wipe from bottom to top (plays once) */
@keyframes text-fx-reveal-up {
    from { clip-path: inset(100% 0 0 0); }
    to { clip-path: inset(0 0 0 0); }
}

.text-fx-reveal-up {
    animation: text-fx-reveal-up 0.8s ease-out forwards;
}

/* ==========================================================================
   Hover Effects (4)
   ========================================================================== */

/* Hover Glow — glow appears on hover */
.text-fx-hover-glow {
    transition: text-shadow 0.3s ease;
}
@media (hover: hover) {
    .text-fx-hover-glow:hover {
        text-shadow:
            0 0 7px color-mix(in oklch, var(--color-primary) 50%, transparent),
            0 0 14px color-mix(in oklch, var(--color-primary) 30%, transparent),
            0 0 21px color-mix(in oklch, var(--color-primary) 15%, transparent);
    }
}

/* Hover Gradient — gradient shifts on hover */
/* The gradient arrives ON HOVER. It used to be declared at rest — the glyph was
   gradient-filled the whole time and hovering only slid background-position,
   which over a base effect read as nothing happening at all. A hover effect
   whose resting state is already the effect has nothing left to do. */
.text-fx-hover-gradient {
    transition: background-position 0.4s ease;
}
@media (hover: hover) {
    .text-fx-hover-gradient:hover {
        background: linear-gradient(
            var(--text-fx-angle),
            currentColor,
            var(--text-fx-color-1),
            var(--text-fx-color-2)
        );
        background-size: 300% 100%;
        background-position: 100% 50%;
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
    }
}

/* Hover Outline — outline appears on hover */
.text-fx-hover-outline {
    -webkit-text-stroke: 0px currentColor;
    paint-order: stroke fill;
    transition: -webkit-text-stroke 0.3s ease;
}
@media (hover: hover) {
    .text-fx-hover-outline:hover {
        -webkit-text-stroke: 1px currentColor;
        -webkit-text-fill-color: transparent;
    }
}

/* Hover Color — color change to primary on hover */
.text-fx-hover-color {
    transition: color 0.3s ease;
}
@media (hover: hover) {
    .text-fx-hover-color:hover {
        color: var(--color-primary);
    }
}


/* ==========================================================================
   Base + hover combinations that could not work
   ==========================================================================

   Rick, 2026-09-15, listing five of eight pairs on the mo-text-hover page as
   not working. They were not near-misses; each was structurally incapable of
   showing anything, for one of two reasons.

   THE GLYPH FILL IS CLAIMED. `gradient-flow` sets `-webkit-text-fill-color:
   transparent` and paints the glyph with a clipped background. Any hover that
   works by setting `color` is then writing to a property that no longer draws
   anything, and any hover that works by setting `background-position` is
   writing to a property an infinite animation already owns — a running
   animation beats a normal declaration, so the hover value never applies.

   THE PAIR IS AN IDENTITY. `.text-fx-glow` and `.text-fx-hover-glow:hover`
   carried byte-identical three-layer text-shadows. Hovering swapped a value for
   itself. That one is not a CSS bug so much as a catalogue that never asked
   what a hover over its own effect should mean.

   Each rule below is scoped to the exact pair, so combinations that already
   work are untouched. */

@media (hover: hover) {

    /* color over gradient-flow — `color` cannot reach a transparent fill, so
       the hover drops the gradient entirely and lands on a solid primary. The
       animation has to stop with it or it keeps repainting the background. */
    .text-fx-gradient-flow.text-fx-hover-color:hover {
        background: none;
        -webkit-text-fill-color: var(--color-primary);
        color: var(--color-primary);
        animation: none;
    }

    /* glow over gradient-flow — text-shadow is unreliable behind a
       background-clipped glyph. drop-shadow() filters the rendered element, so
       it reads against the gradient instead of behind it. */
    .text-fx-gradient-flow.text-fx-hover-glow:hover {
        text-shadow: none;
        filter:
            drop-shadow(0 0 6px color-mix(in oklch, var(--color-primary) 60%, transparent))
            drop-shadow(0 0 14px color-mix(in oklch, var(--color-primary) 35%, transparent));
    }

    /* glow over glow — the hover must exceed the resting state to be a hover.
       Wider radii and more opacity than the base's 7/14/21 at 50/30/15. */
    .text-fx-glow.text-fx-hover-glow:hover {
        text-shadow:
            0 0 10px color-mix(in oklch, var(--color-primary) 80%, transparent),
            0 0 22px color-mix(in oklch, var(--color-primary) 55%, transparent),
            0 0 38px color-mix(in oklch, var(--color-primary) 30%, transparent);
    }

    /* gradient over gradient-flow — the flow animation owns background-position
       and outranks the hover declaration. Pausing it hands the property back,
       and the paused gradient then slides to the hover position. */
    .text-fx-gradient-flow.text-fx-hover-gradient:hover {
        animation-play-state: paused;
        background-position: 100% 50%;
    }
}

/* ==========================================================================
   Accessibility — prefers-reduced-motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .text-fx-gradient-flow,
    .text-fx-gradient-rotate,
    .text-fx-shimmer,
    .text-fx-glow-pulse,
    .text-fx-color-cycle,
    .text-fx-blur-in,
    .text-fx-reveal-right,
    .text-fx-reveal-up {
        animation: none;
    }

    /* Ensure reveal effects are visible when animation is disabled */
    .text-fx-blur-in {
        filter: none;
        opacity: 1;
    }

    .text-fx-reveal-right,
    .text-fx-reveal-up {
        clip-path: none;
    }
}

/* ==========================================================================
   SplitType Compatibility
   When text_animation splits text into .line/.word/.char children,
   background-clip: text on the parent no longer works because the parent
   has no direct text nodes. Propagate gradient styles to child elements.
   ========================================================================== */

[class*="text-fx-gradient"] > .line,
[class*="text-fx-gradient"] > .word,
[class*="text-fx-gradient"] > .char,
.text-fx-shimmer > .line,
.text-fx-shimmer > .word,
.text-fx-shimmer > .char {
    background: inherit;
    background-size: inherit;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: inherit;
}

/* ==========================================================================
   Accessibility — prefers-contrast
   ========================================================================== */

@media (prefers-contrast: more) {
    [class*="text-fx-"] {
        -webkit-text-fill-color: unset;
        background-clip: unset;
        text-shadow: none !important;
        -webkit-text-stroke: 0 !important;
        filter: none !important;
    }
}

/* ==========================================================================
   Print — disable all effects
   ========================================================================== */

@media print {
    [class*="text-fx-"] {
        -webkit-text-fill-color: unset;
        background-clip: unset;
        text-shadow: none !important;
        -webkit-text-stroke: 0 !important;
        filter: none !important;
        animation: none !important;
        clip-path: none !important;
    }
}
