/* ============================================================
   Text Animations — すべて CSS で定義
   JS は .char / .word / .line の付与のみ
   ============================================================ */

/* --- 共通変数 --- */
.text-anim {
  --anim-duration: 1.2s;
  --anim-delay: 0s;
  --anim-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --stagger: 0.06s;
}

/* ===================== 要素全体 ===================== */

.text-anim-fade-in {
  animation: anim-fade-in var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.text-anim-slide-up {
  animation: anim-slide-up var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-slide-up {
  from { opacity: 0; transform: translateY(1.2em); }
  to { opacity: 1; transform: translateY(0); }
}

.text-anim-slide-down {
  animation: anim-slide-down var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-slide-down {
  from { opacity: 0; transform: translateY(-1.2em); }
  to { opacity: 1; transform: translateY(0); }
}

.text-anim-slide-left {
  animation: anim-slide-left var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-slide-left {
  from { opacity: 0; transform: translateX(1.5em); }
  to { opacity: 1; transform: translateX(0); }
}

.text-anim-slide-right {
  animation: anim-slide-right var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-slide-right {
  from { opacity: 0; transform: translateX(-1.5em); }
  to { opacity: 1; transform: translateX(0); }
}

.text-anim-bounce-in {
  animation: anim-bounce-in 1.4s cubic-bezier(0.34, 1.56, 0.64, 1) var(--anim-delay) both;
}

@keyframes anim-bounce-in {
  0% { opacity: 0; transform: scale(0.3) translateY(1em); }
  50% { transform: scale(1.05) translateY(-0.2em); }
  70% { transform: scale(0.95) translateY(0.1em); }
  100% { opacity: 1; transform: scale(1) translateY(0); }
}

.text-anim-elastic-in {
  animation: anim-elastic-in 1.6s cubic-bezier(0.68, -0.55, 0.27, 1.55) var(--anim-delay) both;
}

@keyframes anim-elastic-in {
  0% { opacity: 0; transform: scale(0); }
  55% { opacity: 1; transform: scale(1.15); }
  75% { transform: scale(0.92); }
  100% { transform: scale(1); }
}

.text-anim-scale-in {
  animation: anim-scale-in var(--anim-duration) var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-scale-in {
  from { opacity: 0; transform: scale(0.5); }
  to { opacity: 1; transform: scale(1); }
}

.text-anim-scale-pulse {
  animation: anim-scale-pulse 2s ease-in-out infinite;
}

@keyframes anim-scale-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

.text-anim-rotate-in {
  animation: anim-rotate-in 1.2s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-rotate-in {
  from { opacity: 0; transform: rotate(-180deg) scale(0.5); }
  to { opacity: 1; transform: rotate(0) scale(1); }
}

.text-anim-flip-x {
  animation: anim-flip-x 1.2s var(--anim-ease) var(--anim-delay) both;
  transform-style: preserve-3d;
}

@keyframes anim-flip-x {
  from { opacity: 0; transform: perspective(400px) rotateX(90deg); }
  to { opacity: 1; transform: perspective(400px) rotateX(0); }
}

.text-anim-flip-y {
  animation: anim-flip-y 1.2s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-flip-y {
  from { opacity: 0; transform: perspective(400px) rotateY(90deg); }
  to { opacity: 1; transform: perspective(400px) rotateY(0); }
}

.text-anim-shake {
  animation: anim-shake 0.8s ease-in-out infinite;
}

@keyframes anim-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

.text-anim-wobble {
  animation: anim-wobble 1.5s ease-in-out infinite;
}

@keyframes anim-wobble {
  0%, 100% { transform: rotate(0); }
  25% { transform: rotate(-4deg); }
  75% { transform: rotate(4deg); }
}

.text-anim-glow {
  animation: anim-glow 2s ease-in-out infinite;
}

@keyframes anim-glow {
  0%, 100% {
    text-shadow: 0 0 8px rgba(124, 108, 255, 0.4), 0 0 20px rgba(124, 108, 255, 0.2);
  }
  50% {
    text-shadow: 0 0 16px rgba(124, 108, 255, 0.9), 0 0 40px rgba(124, 108, 255, 0.5);
  }
}

.text-anim-neon-flicker {
  animation: anim-neon-flicker 3s linear infinite;
}

@keyframes anim-neon-flicker {
  0%, 18%, 22%, 25%, 53%, 57%, 100% {
    text-shadow:
      0 0 4px #fff,
      0 0 10px #7c6cff,
      0 0 20px #7c6cff,
      0 0 40px #7c6cff;
  }
  20%, 24%, 55% {
    text-shadow: none;
    opacity: 0.85;
  }
}

.text-anim-gradient-shift {
  background: linear-gradient(90deg, #7c6cff, #ff6b9d, #ffd54f, #7c6cff);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: anim-gradient-shift 4s linear infinite;
}

@keyframes anim-gradient-shift {
  0% { background-position: 0% 50%; }
  100% { background-position: 300% 50%; }
}

.text-anim-rainbow {
  animation: anim-rainbow 4s linear infinite;
}

@keyframes anim-rainbow {
  0% { color: #ff6b6b; }
  16% { color: #ffd93d; }
  33% { color: #6bcb77; }
  50% { color: #4d96ff; }
  66% { color: #9b59b6; }
  83% { color: #ff6b9d; }
  100% { color: #ff6b6b; }
}

.text-anim-blur-in {
  animation: anim-blur-in 1.4s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-blur-in {
  from { opacity: 0; filter: blur(12px); }
  to { opacity: 1; filter: blur(0); }
}

.text-anim-skew-in {
  animation: anim-skew-in 1s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-skew-in {
  from { opacity: 0; transform: skewX(-20deg) translateX(-1em); }
  to { opacity: 1; transform: skewX(0) translateX(0); }
}

.text-anim-typewriter {
  overflow: hidden;
  white-space: nowrap;
  border-right: 3px solid var(--accent, #7c6cff);
  width: 0;
  animation:
    anim-typewriter-width 2.5s steps(14, end) forwards,
    anim-typewriter-blink 0.6s step-end infinite;
  animation-delay: var(--anim-delay), calc(var(--anim-delay) + 2.5s);
}

@keyframes anim-typewriter-width {
  to { width: 100%; }
}

@keyframes anim-typewriter-blink {
  50% { border-color: transparent; }
}

.text-anim-underline-grow {
  position: relative;
  display: inline-block;
}

.text-anim-underline-grow::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -0.15em;
  width: 100%;
  height: 3px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  animation: anim-underline-grow 1.2s var(--anim-ease) var(--anim-delay) forwards;
}

@keyframes anim-underline-grow {
  to { transform: scaleX(1); }
}

.text-anim-tracking-expand {
  animation: anim-tracking-expand 1.5s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-tracking-expand {
  from { letter-spacing: -0.2em; opacity: 0; }
  to { letter-spacing: 0.12em; opacity: 1; }
}

.text-anim-text-shadow-dance {
  animation: anim-text-shadow-dance 2s ease-in-out infinite;
}

@keyframes anim-text-shadow-dance {
  0%, 100% { text-shadow: 2px 2px 0 #7c6cff; }
  25% { text-shadow: -2px 2px 0 #ff6b9d; }
  50% { text-shadow: -2px -2px 0 #ffd54f; }
  75% { text-shadow: 2px -2px 0 #6bcb77; }
}

.text-anim-heartbeat {
  animation: anim-heartbeat 1.4s ease-in-out infinite;
}

@keyframes anim-heartbeat {
  0%, 100% { transform: scale(1); }
  14% { transform: scale(1.12); }
  28% { transform: scale(1); }
  42% { transform: scale(1.08); }
  56% { transform: scale(1); }
}

.text-anim-rubber-band {
  animation: anim-rubber-band 1.2s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-rubber-band {
  0% { transform: scale3d(1, 1, 1); }
  30% { transform: scale3d(1.25, 0.75, 1); }
  40% { transform: scale3d(0.75, 1.25, 1); }
  50% { transform: scale3d(1.15, 0.85, 1); }
  65% { transform: scale3d(0.95, 1.05, 1); }
  75% { transform: scale3d(1.05, 0.95, 1); }
  100% { transform: scale3d(1, 1, 1); }
}

.text-anim-swing {
  transform-origin: top center;
  animation: anim-swing 2s ease-in-out infinite;
}

@keyframes anim-swing {
  0%, 100% { transform: rotate(0deg); }
  20% { transform: rotate(12deg); }
  40% { transform: rotate(-8deg); }
  60% { transform: rotate(6deg); }
  80% { transform: rotate(-4deg); }
}

.text-anim-flash {
  animation: anim-flash 2s ease-in-out infinite;
}

@keyframes anim-flash {
  0%, 50%, 100% { opacity: 1; }
  25%, 75% { opacity: 0.2; }
}

.text-anim-glitch {
  position: relative;
  animation: anim-glitch-skew 2s infinite linear alternate-reverse;
}

.text-anim-glitch::before,
.text-anim-glitch::after {
  content: attr(data-text);
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

.text-anim-glitch::before {
  left: 2px;
  text-shadow: -2px 0 #ff6b9d;
  clip-path: inset(0 0 65% 0);
  animation: anim-glitch-top 2s infinite linear alternate-reverse;
}

.text-anim-glitch::after {
  left: -2px;
  text-shadow: 2px 0 #7c6cff;
  clip-path: inset(65% 0 0 0);
  animation: anim-glitch-bottom 2s infinite linear alternate-reverse;
}

@keyframes anim-glitch-skew {
  0%, 100% { transform: skew(0deg); }
  20% { transform: skew(-2deg); }
  40% { transform: skew(2deg); }
}

@keyframes anim-glitch-top {
  0%, 100% { transform: translate(0); }
  20% { transform: translate(-3px, 2px); }
  40% { transform: translate(3px, -2px); }
}

@keyframes anim-glitch-bottom {
  0%, 100% { transform: translate(0); }
  60% { transform: translate(3px, 2px); }
  80% { transform: translate(-3px, -2px); }
}

.text-anim-float {
  animation: anim-float 3s ease-in-out infinite;
}

@keyframes anim-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-0.35em); }
}

.text-anim-spin {
  display: inline-block;
  animation: anim-spin 3s linear infinite;
}

@keyframes anim-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.text-anim-flicker-opacity {
  animation: anim-flicker-opacity 1.5s steps(2, end) infinite;
}

@keyframes anim-flicker-opacity {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

.text-anim-zoom-pulse {
  animation: anim-zoom-pulse 2s ease-in-out infinite;
}

@keyframes anim-zoom-pulse {
  0%, 100% { transform: scale(1); letter-spacing: 0; }
  50% { transform: scale(1.05); letter-spacing: 0.05em; }
}

/* ===================== 文字単位 (.char) ===================== */

.text-anim-stagger-fade-up .char {
  opacity: 0;
  animation: anim-char-fade-up 0.6s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-fade-up {
  from { opacity: 0; transform: translateY(0.8em); }
  to { opacity: 1; transform: translateY(0); }
}

.text-anim-stagger-slide-up .char {
  opacity: 0;
  animation: anim-char-slide-up 0.7s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-slide-up {
  from { opacity: 0; transform: translateY(1.5em); }
  to { opacity: 1; transform: translateY(0); }
}

.text-anim-stagger-bounce .char {
  opacity: 0;
  animation: anim-char-bounce 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-bounce {
  0% { opacity: 0; transform: translateY(2em) scale(0.5); }
  60% { opacity: 1; transform: translateY(-0.3em) scale(1.1); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.text-anim-wave .char {
  animation: anim-char-wave 1.5s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.1s);
}

@keyframes anim-char-wave {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-0.4em); }
}

.text-anim-pop-in .char {
  opacity: 0;
  animation: anim-char-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-pop {
  from { opacity: 0; transform: scale(0); }
  to { opacity: 1; transform: scale(1); }
}

.text-anim-rotate-in-chars .char {
  opacity: 0;
  display: inline-block;
  animation: anim-char-rotate 0.7s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-rotate {
  from { opacity: 0; transform: rotateY(90deg); }
  to { opacity: 1; transform: rotateY(0); }
}

.text-anim-flip-chars .char {
  opacity: 0;
  transform-origin: center bottom;
  animation: anim-char-flip 0.6s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-flip {
  from { opacity: 0; transform: rotateX(90deg); }
  to { opacity: 1; transform: rotateX(0); }
}

.text-anim-blur-reveal-chars .char {
  opacity: 0;
  filter: blur(8px);
  animation: anim-char-blur 0.8s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-blur {
  to { opacity: 1; filter: blur(0); }
}

.text-anim-color-wave .char {
  animation: anim-char-color 2s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.12s);
}

@keyframes anim-char-color {
  0%, 100% { color: #f4f4f6; }
  50% { color: #7c6cff; }
}

.text-anim-drop-in-chars .char {
  opacity: 0;
  animation: anim-char-drop 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-drop {
  from { opacity: 0; transform: translateY(-2em) rotate(-20deg); }
  to { opacity: 1; transform: translateY(0) rotate(0); }
}

.text-anim-stagger-scale .char {
  opacity: 0;
  animation: anim-char-scale 0.6s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-scale {
  from { opacity: 0; transform: scale(0); }
  to { opacity: 1; transform: scale(1); }
}

.text-anim-stagger-skew .char {
  opacity: 0;
  animation: anim-char-skew 0.6s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-skew {
  from { opacity: 0; transform: skewX(-30deg) translateX(-0.5em); }
  to { opacity: 1; transform: skewX(0) translateX(0); }
}

.text-anim-typing-chars .char {
  opacity: 0;
  animation: anim-char-typing 0.1s steps(1) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.08s);
}

@keyframes anim-char-typing {
  to { opacity: 1; }
}

.text-anim-jump .char {
  animation: anim-char-jump 1.2s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.08s);
}

@keyframes anim-char-jump {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-0.5em); }
}

/* ===================== 単語単位 (.word) ===================== */

.text-anim-stagger-words-fade .word {
  opacity: 0;
  animation: anim-word-fade-up 0.7s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.15s);
}

@keyframes anim-word-fade-up {
  from { opacity: 0; transform: translateY(1em); }
  to { opacity: 1; transform: translateY(0); }
}

.text-anim-stagger-words-slide .word {
  opacity: 0;
  animation: anim-word-slide 0.8s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.12s);
}

@keyframes anim-word-slide {
  from { opacity: 0; transform: translateX(-1.5em); }
  to { opacity: 1; transform: translateX(0); }
}

.text-anim-stagger-words-scale .word {
  opacity: 0;
  animation: anim-word-scale 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.12s);
}

@keyframes anim-word-scale {
  from { opacity: 0; transform: scale(0.5); }
  to { opacity: 1; transform: scale(1); }
}

.text-anim-stagger-words-blur .word {
  opacity: 0;
  filter: blur(6px);
  animation: anim-word-blur 0.9s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.14s);
}

@keyframes anim-word-blur {
  to { opacity: 1; filter: blur(0); }
}

.text-anim-stagger-words-rotate .word {
  opacity: 0;
  display: inline-block;
  animation: anim-word-rotate 0.8s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.1s);
}

@keyframes anim-word-rotate {
  from { opacity: 0; transform: rotate(-15deg) translateY(0.5em); }
  to { opacity: 1; transform: rotate(0) translateY(0); }
}

/* ===================== Codrops-inspired (lab-original) ===================== */

.text-anim-clip-wipe {
  animation: anim-clip-wipe 1.1s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-clip-wipe {
  from {
    opacity: 0;
    clip-path: inset(0 100% 0 0);
    transform: translateX(-0.15em);
  }
  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: translateX(0);
  }
}

.text-anim-depth-stack {
  color: #f4f4f6;
  text-shadow:
    1px 1px 0 #7c6cff,
    2px 2px 0 rgba(124, 108, 255, 0.85),
    3px 3px 0 rgba(124, 108, 255, 0.65),
    4px 4px 0 rgba(124, 108, 255, 0.45),
    5px 5px 0 rgba(124, 108, 255, 0.3),
    6px 6px 12px rgba(0, 0, 0, 0.35);
  animation: anim-depth-stack 2.5s ease-in-out infinite;
}

@keyframes anim-depth-stack {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(3px, -3px); }
}

.text-anim-stroke-reveal {
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(244, 244, 246, 0.85);
  animation: anim-stroke-reveal 1.4s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-stroke-reveal {
  0% {
    opacity: 0;
    color: transparent;
    -webkit-text-stroke-color: rgba(124, 108, 255, 0.2);
    letter-spacing: 0.2em;
  }
  55% {
    opacity: 1;
    color: transparent;
    -webkit-text-stroke-color: rgba(244, 244, 246, 0.9);
    letter-spacing: 0.05em;
  }
  100% {
    opacity: 1;
    color: #f4f4f6;
    -webkit-text-stroke-color: transparent;
    letter-spacing: 0;
  }
}

.text-anim-shimmer {
  background: linear-gradient(
    90deg,
    #f4f4f6 0%,
    #f4f4f6 40%,
    #7c6cff 50%,
    #ff6b9d 55%,
    #f4f4f6 60%,
    #f4f4f6 100%
  );
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: anim-shimmer 2.8s linear infinite;
}

@keyframes anim-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: -100% 50%; }
}

.text-anim-perspective-tilt {
  display: inline-block;
  transform-style: preserve-3d;
  animation: anim-perspective-tilt 1.2s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-perspective-tilt {
  from {
    opacity: 0;
    transform: perspective(500px) rotateX(55deg) translateY(1em);
  }
  to {
    opacity: 1;
    transform: perspective(500px) rotateX(0) translateY(0);
  }
}

.text-anim-scramble-in .char {
  opacity: 0;
  display: inline-block;
  filter: blur(6px);
  animation: anim-char-scramble 0.75s steps(6) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.045s);
}

@keyframes anim-char-scramble {
  0% {
    opacity: 0;
    filter: blur(8px);
    transform: translateY(0.6em) rotate(8deg);
  }
  45% {
    opacity: 0.5;
    filter: blur(3px);
    transform: translateY(-0.15em) rotate(-4deg);
  }
  100% {
    opacity: 1;
    filter: blur(0);
    transform: translateY(0) rotate(0);
  }
}

.text-anim-stagger-flip-y .char {
  opacity: 0;
  display: inline-block;
  transform-origin: center bottom;
  animation: anim-char-flip-y 0.75s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-flip-y {
  from {
    opacity: 0;
    transform: perspective(400px) rotateY(90deg);
  }
  to {
    opacity: 1;
    transform: perspective(400px) rotateY(0);
  }
}

.text-anim-stagger-clip .char {
  opacity: 0;
  display: inline-block;
  clip-path: inset(100% 0 0 0);
  animation: anim-char-clip 0.65s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-clip {
  from {
    opacity: 0;
    clip-path: inset(100% 0 0 0);
    transform: translateY(0.5em);
  }
  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: translateY(0);
  }
}

/* ===================== Award-site inspired (lab-original) ===================== */

.text-anim-kinetic-press {
  display: inline-block;
  animation: anim-kinetic-press 1.15s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-kinetic-press {
  0% {
    opacity: 0;
    letter-spacing: 0.35em;
    transform: scaleX(1.35);
  }
  70% {
    opacity: 1;
    letter-spacing: 0.04em;
    transform: scaleX(0.98);
  }
  100% {
    opacity: 1;
    letter-spacing: 0;
    transform: scaleX(1);
  }
}

.text-anim-award-underline {
  display: inline-block;
  position: relative;
  padding-bottom: 0.12em;
  animation: anim-award-underline-fade 0.9s var(--anim-ease) var(--anim-delay) both;
}

.text-anim-award-underline::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: linear-gradient(90deg, #7c6cff, #ff6b9d);
  transform: translateX(-50%) scaleX(0);
  transform-origin: center;
  animation: anim-award-underline-grow 0.85s var(--anim-ease) calc(var(--anim-delay) + 0.2s) both;
}

@keyframes anim-award-underline-fade {
  from { opacity: 0; transform: translateY(0.4em); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes anim-award-underline-grow {
  from { transform: translateX(-50%) scaleX(0); }
  to { transform: translateX(-50%) scaleX(1); }
}

.text-anim-hero-mask-up {
  display: inline-block;
  animation: anim-hero-mask-up 1.2s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-hero-mask-up {
  from {
    opacity: 0;
    clip-path: inset(100% 0 0 0);
    transform: translateY(0.35em);
  }
  to {
    opacity: 1;
    clip-path: inset(0 0 0 0);
    transform: translateY(0);
  }
}

.text-anim-marquee-in {
  display: inline-block;
  white-space: nowrap;
  animation: anim-marquee-in 1.1s var(--anim-ease) var(--anim-delay) both;
}

@keyframes anim-marquee-in {
  from {
    opacity: 0;
    transform: translateX(40%);
    letter-spacing: 0.12em;
  }
  to {
    opacity: 1;
    transform: translateX(0);
    letter-spacing: 0;
  }
}

.text-anim-kinetic-breathe {
  display: inline-block;
  animation: anim-kinetic-breathe 2.4s ease-in-out infinite;
}

@keyframes anim-kinetic-breathe {
  0%, 100% {
    letter-spacing: 0;
    transform: scale(1);
  }
  50% {
    letter-spacing: 0.14em;
    transform: scale(1.03);
  }
}

.text-anim-blend-flash {
  color: #f4f4f6;
  mix-blend-mode: difference;
  animation: anim-blend-flash 2.2s ease-in-out infinite;
}

@keyframes anim-blend-flash {
  0%, 100% { opacity: 1; filter: brightness(1); }
  50% { opacity: 0.75; filter: brightness(1.35); }
}

.text-anim-stagger-lift-3d .char {
  opacity: 0;
  display: inline-block;
  transform-origin: center bottom;
  animation: anim-char-lift-3d 0.8s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * var(--stagger));
}

@keyframes anim-char-lift-3d {
  from {
    opacity: 0;
    transform: perspective(500px) translateY(1.2em) rotateX(55deg) scale(0.85);
  }
  to {
    opacity: 1;
    transform: perspective(500px) translateY(0) rotateX(0) scale(1);
  }
}

.text-anim-stagger-words-rise .word {
  opacity: 0;
  display: inline-block;
  animation: anim-word-rise 0.85s var(--anim-ease) forwards;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.14s);
}

@keyframes anim-word-rise {
  from {
    opacity: 0;
    transform: translateY(1.1em) scale(0.92);
    filter: blur(4px);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
    filter: blur(0);
  }
}

/* --- var-weight ---
   Awwwards editorial trend: variable font weight pulses from ultra-thin
   to heavy, creating a breathing rhythm that feels alive on screen.
   Uses font-variation-settings on DM Sans (wght axis: 100–700).
   Entirely CSS — no JS, no SVG, no extra elements. */
.text-anim-var-weight {
  font-variation-settings: 'wght' 100;
  animation: anim-var-weight 2.4s cubic-bezier(0.37, 0, 0.63, 1) infinite alternate;
  letter-spacing: 0.01em;
}
@keyframes anim-var-weight {
  0%   { font-variation-settings: 'wght' 100; letter-spacing:  0.06em; opacity: 0.55; }
  40%  { font-variation-settings: 'wght' 400; letter-spacing:  0.02em; opacity: 0.85; }
  70%  { font-variation-settings: 'wght' 650; letter-spacing: -0.01em; opacity: 1;    }
  100% { font-variation-settings: 'wght' 700; letter-spacing: -0.02em; opacity: 1;    }
}

/* ============================================================
   NEW TEXT ANIMATIONS — batch 2
   ============================================================ */



/* --- split-flap ---
   Airport departure board: each char flips in with step() timing,
   staggered so it reads left-to-right like a slot machine. */
.text-anim-split-flap .char {
  display: inline-block;
  opacity: 0;
  animation: anim-split-flap 0.5s steps(1, end) forwards;
  animation-delay: calc(var(--anim-delay, 0s) + var(--i, 0) * 0.07s);
}
@keyframes anim-split-flap {
  0%  { opacity: 0; transform: rotateX(-90deg) scaleY(0.4); }
  30% { opacity: 1; transform: rotateX(20deg)  scaleY(1.1); }
  55% { opacity: 1; transform: rotateX(-10deg) scaleY(0.95); }
  75% { opacity: 1; transform: rotateX(5deg)   scaleY(1.02); }
  100%{ opacity: 1; transform: rotateX(0deg)   scaleY(1);    }
}

/* --- cascade-blur ---
   Characters blur in from outside edges toward center.
   Delay is staggered; chars further from center animate first. */
.text-anim-cascade-blur .char {
  display: inline-block;
  opacity: 0;
  filter: blur(20px);
  transform: translateX(calc((var(--i, 0) - 3) * 8px));
  animation: anim-cascade-blur 0.65s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--anim-delay, 0s) + var(--i, 0) * 0.05s);
}
@keyframes anim-cascade-blur {
  from {
    opacity: 0;
    filter: blur(20px);
    transform: translateX(var(--cascade-offset, 12px));
  }
  to {
    opacity: 1;
    filter: blur(0);
    transform: translateX(0);
  }
}
/* --- word-paint ---
   Each word appears with a clip-path paint wipe from left to right,
   creating a brush-stroke reveal effect per word. */
.text-anim-word-paint .word {
  display: inline-block;
  clip-path: inset(0 100% 0 0);
  animation: anim-word-paint 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--anim-delay, 0s) + var(--i, 0) * 0.18s);
  color: var(--accent, #6c63ff);
}
@keyframes anim-word-paint {
  0%   { clip-path: inset(0 100% 0 0); opacity: 1; }
  100% { clip-path: inset(0 0%   0 0); opacity: 1; }
}

/* --- levitate ---
   Text gently levitates: oscillating translateY with a growing shadow.
   Infinite ambient loop - great for hero headlines. */
.text-anim-levitate {
  animation: anim-levitate 3s ease-in-out infinite;
  display: inline-block;
}
@keyframes anim-levitate {
  0%, 100% {
    transform: translateY(0px);
    text-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
  }
  50% {
    transform: translateY(-8px);
    text-shadow: 0 20px 30px rgba(0, 0, 0, 0.35);
  }
}

/* --- liquid-chars ---
   Characters appear to melt/drip down — starts squeezed wide and high,
   then settles into natural shape with a springy overshoot. */
.text-anim-liquid-chars .char {
  display: inline-block;
  opacity: 0;
  transform: scaleY(0) scaleX(1.4) translateY(-0.8em);
  animation: anim-liquid-char 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  animation-delay: calc(var(--anim-delay, 0s) + var(--i, 0) * 0.04s);
}
@keyframes anim-liquid-char {
  from {
    opacity: 0;
    transform: scaleY(0) scaleX(1.4) translateY(-0.8em);
  }
  to {
    opacity: 1;
    transform: scaleY(1) scaleX(1) translateY(0);
  }
}

/* --- highlight-sweep ---
   Fluorescent highlighter sweeps across the text using a ::after pseudo-element. */
.text-anim-highlight-sweep {
  position: relative;
  display: inline-block;
}
.text-anim-highlight-sweep::after {
  content: '';
  position: absolute;
  left: -4%;
  top: 60%;
  width: 108%;
  height: 45%;
  background: rgba(255, 220, 0, 0.4);
  transform: scaleX(0) skewX(-8deg);
  transform-origin: left;
  animation: anim-highlight-sweep 0.6s ease-out forwards;
  animation-delay: var(--anim-delay, 0s);
}
@keyframes anim-highlight-sweep {
  from { transform: scaleX(0) skewX(-8deg); }
  to   { transform: scaleX(1) skewX(-8deg); }
}

/* --- rotate-words-3d ---
   Each word flips in on the Y-axis with a 3D perspective rotation. */
.text-anim-rotate-words-3d .word {
  display: inline-block;
  opacity: 0;
  transform: perspective(500px) rotateY(90deg);
  animation: anim-rotate-word-3d 0.7s ease forwards;
  animation-delay: calc(var(--anim-delay, 0s) + var(--i, 0) * 0.12s);
}
@keyframes anim-rotate-word-3d {
  from {
    opacity: 0;
    transform: perspective(500px) rotateY(90deg);
  }
  to {
    opacity: 1;
    transform: perspective(500px) rotateY(0deg);
  }
}

/* --- stretch-reveal ---
   Text stretches from razor-thin horizontally then snaps to full width. */
.text-anim-stretch-reveal {
  display: inline-block;
  opacity: 0;
  transform: scaleX(0.04);
  animation: anim-stretch-reveal 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: var(--anim-delay, 0s);
}
@keyframes anim-stretch-reveal {
  0%   { opacity: 0; transform: scaleX(0.04); }
  50%  { opacity: 1; transform: scaleX(1.06); }
  100% { opacity: 1; transform: scaleX(1); }
}

/* --- jitter ---
   Characters jitter/vibrate rapidly then settle into place. */
.text-anim-jitter .char {
  display: inline-block;
  animation: anim-jitter 0.6s steps(1) forwards;
  animation-delay: calc(var(--i, 0) * 0.03s);
}
@keyframes anim-jitter {
  0%   { transform: translate(-3px, 2px) rotate(-2deg); opacity: 0; }
  20%  { transform: translate(3px, -1px) rotate(1deg);  opacity: 0; }
  40%  { transform: translate(-2px, 1px);                opacity: 1; }
  60%  { transform: translate(2px, -2px) rotate(-1deg); opacity: 1; }
  80%  { transform: translate(-1px, 1px);                opacity: 1; }
  100% { transform: none;                                opacity: 1; }
}

/* --- neon-outline ---
   Animated neon text stroke — cycles stroke color through purple→cyan→pink. */
.text-anim-neon-outline {
  -webkit-text-stroke: 1.5px #7c6cff;
  color: transparent;
  animation: anim-neon-outline 3s linear infinite;
}
@keyframes anim-neon-outline {
  0%,100% { -webkit-text-stroke-color: #7c6cff; filter: drop-shadow(0 0 4px #7c6cff); }
  33%     { -webkit-text-stroke-color: #06b6d4; filter: drop-shadow(0 0 6px #06b6d4); }
  66%     { -webkit-text-stroke-color: #ec4899; filter: drop-shadow(0 0 4px #ec4899); }
}

/* --- blur-wave-chars ---
   A blur ripple passes through characters in sequence. */
.text-anim-blur-wave-chars .char {
  display: inline-block;
  animation: anim-blur-wave 2.4s ease-in-out infinite;
  animation-delay: calc(var(--i) * 0.08s);
}
@keyframes anim-blur-wave {
  0%,100% { filter: blur(0); opacity: 1; }
  50%     { filter: blur(4px); opacity: 0.6; }
}

/* --- text-3d-extrude ---
   Text with 3D extruded shadow that shortens on entrance. */
.text-anim-text-3d-extrude {
  animation: anim-3d-extrude 1s cubic-bezier(0.22,1,0.36,1) both;
}
@keyframes anim-3d-extrude {
  0%   { opacity:0; text-shadow: 8px 8px 0 #3d35a0,7px 7px 0 #3d35a0,6px 6px 0 #3d35a0,5px 5px 0 #3d35a0,4px 4px 0 #3d35a0,3px 3px 0 #3d35a0; transform: translateX(-8px) translateY(-8px); }
  60%  { opacity:1; text-shadow: 2px 2px 0 #3d35a0,1px 1px 0 #3d35a0; transform: translateX(-2px) translateY(-2px); }
  100% { text-shadow: none; transform: none; }
}

/* --- magnetic-scatter ---
   Characters arrive as if magnetically pulled from scattered positions. */
.text-anim-magnetic-scatter .char {
  display: inline-block;
  animation: anim-mag-scatter 0.6s cubic-bezier(0.22,1,0.36,1) both;
  animation-delay: calc(var(--i) * 0.05s);
}
.text-anim-magnetic-scatter .char:nth-child(odd) {
  --from-y: -2em;
}
.text-anim-magnetic-scatter .char:nth-child(even) {
  --from-y: 2em;
}
@keyframes anim-mag-scatter {
  from { opacity:0; transform: translateY(var(--from-y,-1em)) rotate(calc(var(--i)*8deg - 20deg)) scale(0.5); }
  to   { opacity:1; transform: none; }
}

/* --- word-flip-x ---
   Each word flips in on the X-axis with stagger. */
.text-anim-word-flip-x .word {
  display: inline-block;
  animation: anim-word-flip-x 0.65s cubic-bezier(0.22,1,0.36,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.13s);
}
@keyframes anim-word-flip-x {
  from { opacity:0; transform: perspective(500px) rotateX(-80deg) translateY(0.3em); }
  to   { opacity:1; transform: perspective(500px) rotateX(0) translateY(0); }
}

/* --- char-pendulum ---
   Each character swings like a pendulum from its top pivot (looping). */
.text-anim-char-pendulum .char {
  display: inline-block;
  transform-origin: center top;
  animation: anim-pendulum 1.6s cubic-bezier(0.37,0,0.63,1) infinite alternate;
  animation-delay: calc(var(--i) * 0.12s);
}
@keyframes anim-pendulum {
  from { transform: rotate(-18deg); }
  to   { transform: rotate(18deg); }
}

/* --- bounce-wave-enter ---
   One-shot traveling wave bounce entrance. */
.text-anim-bounce-wave-enter .char {
  display: inline-block;
  animation: anim-bwave-enter 0.5s cubic-bezier(0.34,1.56,0.64,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.06s);
}
@keyframes anim-bwave-enter {
  0%   { opacity:0; transform: translateY(1.2em) scaleY(1.3); }
  60%  { transform: translateY(-0.15em) scaleY(0.9); }
  100% { opacity:1; transform: none; }
}

/* --- perspective-cascade ---
   Characters arrive from deep Z-space perspective. */
.text-anim-perspective-cascade .char {
  display: inline-block;
  animation: anim-persp-casc 0.7s cubic-bezier(0.22,1,0.36,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.04s);
}
@keyframes anim-persp-casc {
  from { opacity:0; transform: perspective(400px) translateZ(-200px) rotateY(-25deg) scale(0.5); }
  to   { opacity:1; transform: perspective(400px) translateZ(0) rotateY(0) scale(1); }
}

/* --- paint-brush ---
   Text appears as if painted by a brush — clip-path sweeps right with blur dissolve. */
.text-anim-paint-brush {
  animation: anim-paint-brush 1.2s cubic-bezier(0.22,1,0.36,1) var(--anim-delay) both;
}
@keyframes anim-paint-brush {
  0%   { opacity:0; clip-path: inset(0 100% 0 0); filter: blur(4px); }
  30%  { opacity:1; filter: blur(1px); }
  100% { clip-path: inset(0 0% 0 0); filter: blur(0); }
}

/* --- split-rise-words ---
   Each word rises from below a virtual overflow mask. */
.text-anim-split-rise-words {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
  overflow: hidden;
}
.text-anim-split-rise-words .word {
  display: inline-block;
  animation: anim-split-rise 0.6s cubic-bezier(0.22,1,0.36,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.1s);
}
@keyframes anim-split-rise {
  from { transform: translateY(1.2em); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}

/* ============================================================
   21st.dev インスパイア テキスト新着 (2026-06)
   ============================================================ */

/* --- gradient-trace ---
   A gradient highlight sweeps L→R continuously across text. */
.text-anim-gradient-trace {
  background: linear-gradient(90deg,
    var(--text, #f4f4f6) 0%, var(--text, #f4f4f6) 30%,
    var(--accent) 50%,
    var(--text, #f4f4f6) 70%, var(--text, #f4f4f6) 100%);
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: anim-gradient-trace 2.4s linear var(--anim-delay) infinite;
}
@keyframes anim-gradient-trace {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* --- word-ticker ---
   Cycling word-slot ticker — rotates through multiple lines. */
.text-anim-word-ticker {
  display: flex;
  overflow: hidden;
  height: 1.2em;
  align-items: flex-start;
}
.text-anim-word-ticker .ticker-track {
  display: flex;
  flex-direction: column;
  animation: anim-word-ticker 2.4s cubic-bezier(0.4,0,0.2,1) var(--anim-delay) infinite;
}
.text-anim-word-ticker .ticker-track span {
  display: block;
  line-height: 1.15em;
  white-space: nowrap;
}
@keyframes anim-word-ticker {
  0%,20%  { transform: translateY(0); }
  25%,45% { transform: translateY(-1.15em); }
  50%,70% { transform: translateY(-2.3em); }
  75%,95% { transform: translateY(-3.45em); }
  100%    { transform: translateY(-4.6em); }
}

/* --- spotlight-hover ---
   Gradient spotlight sweeps continuously across text. */
.text-anim-spotlight-hover {
  display: inline-block;
  background: linear-gradient(90deg,
    var(--fg, #fff) 0%, var(--fg, #fff) 25%,
    rgba(124,108,255,0.9) 50%,
    var(--fg, #fff) 75%, var(--fg, #fff) 100%);
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: anim-spotlight-sweep 2.4s linear var(--anim-delay, 0s) infinite;
}
@keyframes anim-spotlight-sweep {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

/* --- chars-scatter-in ---
   Characters arrive from random scatter positions. */
.text-anim-chars-scatter .char {
  display: inline-block;
  animation: anim-scatter-in 0.6s cubic-bezier(0.34,1.56,0.64,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.05s);
}
@keyframes anim-scatter-in {
  0% {
    opacity: 0;
    transform: translate(
      calc((var(--i, 0) * 17px) - 40px),
      calc((var(--i, 0) * 11px) - 30px)
    ) scale(0.2) rotate(calc(var(--i, 0) * 18deg));
  }
  100% { opacity: 1; transform: none; }
}

/* --- counter-up ---
   Numeric text counts up from 0 via CSS counter animation (appearance of counting). */
.text-anim-counter-up {
  animation: anim-counter-up 2s cubic-bezier(0.4,0,0.2,1) var(--anim-delay) both;
}
@keyframes anim-counter-up {
  0%   { opacity: 0; transform: translateY(0.6em) scale(0.8); filter: blur(4px); }
  50%  { filter: blur(0); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

/* --- chars-orbit ---
   Characters fly in from circular orbit positions. */
.text-anim-chars-orbit .char {
  display: inline-block;
  animation: anim-chars-orbit 0.7s cubic-bezier(0.22,1,0.36,1) both;
  animation-delay: calc(var(--anim-delay) + var(--i) * 0.06s);
}
@keyframes anim-chars-orbit {
  0% {
    opacity: 0;
    transform: rotate(calc(var(--i) * 22deg)) translateX(60px) rotate(calc(var(--i) * -22deg)) scale(0.2);
  }
  100% { opacity: 1; transform: none; }
}
