/* #!
Textillin CSS/JS (text animation):
Author: sarap422
Description: Intersection Observer based text animation. anime.js splitText for splitting, CSS transition for motion (no @keyframes). Vanilla JS / jQuery不要
Version: 3.0.0
License: Released under the MIT license.
License URI: https://opensource.org/licenses/MIT
Credits: splitText — anime.js v4 (Julian Garnier, MIT)
*/

/*
 * 設計メモ
 * ── クラスは `textroll-*`（旧 `tlt-*`）。1要素1トリガークラスに統一。
 * ── 分割は anime.js splitText。各文字は <span data-char="i"> で生成される。
 * ── モーションは transition のみ（@keyframes ゼロ ＝ FireShot/印刷エンジンで初期フレーム固定される問題が起きない）。
 * ── 発火は Scrollin と同じ `is-scloaded`（必ず textroll- と同一要素で複合すること。単独子孫セレクタ禁止）。
 * ── stagger は各 char のインライン変数 `--textroll-i`（JSが付与）で表現。shuffle はその並びを入れ替えるだけ。
 * ── 修飾は Scrollin 共有の delayed-* / slowed-* ＋ 文字間隔用の staggered-*。
 */

/* エフェクトクラスの明示列挙（scrollin.scss と同方式）。
 * ※ `[class*="textroll-"]` の部分一致は禁止：.textroll-armed（<html>）まで
 *   マッチしてページ全文が対象化する事故の原因になる。新エフェクト追加時は
 *   このリストと textillin.js の EFFECT_TYPES の両方に登録すること。 */
$tr: '.textroll-fadeIn, .textroll-fadeInBottom, .textroll-fadeInTop, .textroll-fadeInLeft, .textroll-fadeInRight, .textroll-bounceIn, .textroll-bounceInBottom, .textroll-bounceInTop, .textroll-bounceInLeft, .textroll-bounceInRight, .textroll-flipInX, .textroll-flipInY, .textroll-clipInBottom, .textroll-clipInTop, .textroll-clipInLeft, .textroll-clipInRight';


/* §FOUCガード（分割完了までコンテナを隠す）
 * ── textillin.js が1行目で <html> に .textroll-armed を付与（＝JSが動く時だけ隠す。JS不在なら素のテキストが見える）。
 * ── 分割完了で各コンテナに .is-split を付与 → 可視に戻す。以降は各 char 自身の opacity:0 が隠す役目を引き継ぐ。
 * ── a11yクローン（clip-path隠しの裸 span）はコンテナが可視に戻ることで a11y ツリーに正しく残る。
────────────── ༚༶⊰⟡⊱༶༚༺*/
:where(.textroll-armed) :not([untextillin]) :is(#{$tr}):not(.is-split) {
	visibility: hidden;
}


/* §char 共通（transition の下地）
────────────── ༚༶⊰⟡⊱༶༚༺*/
:not([untextillin]) :is(#{$tr}) [data-char] {
	--textroll-distance: 1em;
	--textroll-lag: calc(var(--textroll-i, 0) * var(--textroll-stagger, 15ms) + var(--textroll-delay, 0s));
	--textroll-tf: var(--textroll-ease, var(--ease-out-cubic, cubic-bezier(0.215, 0.61, 0.355, 1)));

	opacity: 0;
	transform: var(--textroll-from, none);
	clip-path: var(--textroll-clip-from, none);
	transition:
		opacity   var(--textroll-duration, 1s) var(--textroll-tf) var(--textroll-lag),
		transform var(--textroll-duration, 1s) var(--textroll-tf) var(--textroll-lag),
		clip-path var(--textroll-duration, 1s) var(--textroll-tf) var(--textroll-lag);
}


/* §エフェクト（from / to のペアを宣言するだけ）
────────────── ༚༶⊰⟡⊱༶༚༺*/
/* フェード系（ease-out-cubic） */
:not([untextillin]) .textroll-fadeIn        [data-char] { --textroll-from: none;                                    --textroll-to: none; }
:not([untextillin]) .textroll-fadeInBottom  [data-char] { --textroll-from: translateY(var(--textroll-distance));    --textroll-to: translateY(0); }
:not([untextillin]) .textroll-fadeInTop     [data-char] { --textroll-from: translateY(calc(var(--textroll-distance) * -1)); --textroll-to: translateY(0); }
:not([untextillin]) .textroll-fadeInLeft    [data-char] { --textroll-from: translateX(calc(var(--textroll-distance) * -1)); --textroll-to: translateX(0); }
:not([untextillin]) .textroll-fadeInRight   [data-char] { --textroll-from: translateX(var(--textroll-distance));    --textroll-to: translateX(0); }

/* バウンス／フリップ系（1回オーバーシュートの ease-out-back） */
:not([untextillin]) :is(
	.textroll-bounceIn, .textroll-bounceInBottom, .textroll-bounceInTop,
	.textroll-bounceInLeft, .textroll-bounceInRight,
	.textroll-flipInX, .textroll-flipInY
) [data-char] {
	--textroll-ease: var(--ease-out-back, cubic-bezier(0.175, 0.885, 0.32, 1.275));
}

:not([untextillin]) .textroll-bounceIn        [data-char] { --textroll-from: scale(.3);                                     --textroll-to: scale(1); }
:not([untextillin]) .textroll-bounceInBottom  [data-char] { --textroll-from: translateY(var(--textroll-distance)) scale(.3); --textroll-to: translateY(0) scale(1); }
:not([untextillin]) .textroll-bounceInTop     [data-char] { --textroll-from: translateY(calc(var(--textroll-distance) * -1)) scale(.3); --textroll-to: translateY(0) scale(1); }
:not([untextillin]) .textroll-bounceInLeft    [data-char] { --textroll-from: translateX(calc(var(--textroll-distance) * -1)) scale(.3); --textroll-to: translateX(0) scale(1); }
:not([untextillin]) .textroll-bounceInRight   [data-char] { --textroll-from: translateX(var(--textroll-distance)) scale(.3); --textroll-to: translateX(0) scale(1); }

:not([untextillin]) .textroll-flipInX  [data-char] { --textroll-from: perspective(800px) rotateX(90deg); --textroll-to: perspective(800px) rotateX(0deg); }
:not([untextillin]) .textroll-flipInY  [data-char] { --textroll-from: perspective(800px) rotateY(90deg); --textroll-to: perspective(800px) rotateY(0deg); }

/* クリップ系（scrollin の clipIn を文字単位に移植。移動もフェードもせず、マスクが開いて現れる）
 * ── 隠す役目は clip-path が担うため opacity は常に 1（フェードさせない）
 * ── 方向名は scrollin と同義（clipInBottom ＝ 下から上へ開く） */
:not([untextillin]) :is(
	.textroll-clipInBottom, .textroll-clipInTop,
	.textroll-clipInLeft, .textroll-clipInRight
) [data-char] {
	--textroll-from: none;
	--textroll-to: none;
	--textroll-clip-to: inset(0 0 0 0);
	opacity: 1;
}

:not([untextillin]) .textroll-clipInBottom [data-char] { --textroll-clip-from: inset(100% 0 0 0); }  /* 下から上 */
:not([untextillin]) .textroll-clipInTop    [data-char] { --textroll-clip-from: inset(0 0 100% 0); }  /* 上から下 */
:not([untextillin]) .textroll-clipInLeft   [data-char] { --textroll-clip-from: inset(0 100% 0 0); }  /* 左から右 */
:not([untextillin]) .textroll-clipInRight  [data-char] { --textroll-clip-from: inset(0 0 0 100%); }  /* 右から左 */


/* §発火（is-scloaded ＝ ビューポート到達。必ず textroll- と同一要素で複合）
────────────── ༚༶⊰⟡⊱༶༚༺*/
:not([untextillin]) :is(#{$tr}):where(.is-scloaded) [data-char] {
	opacity: 1;
	transform: var(--textroll-to, none);
	clip-path: var(--textroll-clip-to, none);
}


/* §修飾（Scrollin 共有の delayed-* / slowed-* と同語彙・同スケール。定義は textroll- にスコープ）
 * ── delayed-*   : 開始遅延（--textroll-delay）
 * ── slowed-*    : 再生時間の延長（--textroll-duration）
 * ── staggered-* : 文字間隔（--textroll-stagger）※旧 data-in-delay
────────────── ༚༶⊰⟡⊱༶༚༺*/
/* delayed（開始遅延） */
.delayed-0:is(#{$tr}) { --textroll-delay: 0s; }
.delayed-1:is(#{$tr}) { --textroll-delay: 0.25s; }
.delayed-2:is(#{$tr}) { --textroll-delay: 0.5s; }
.delayed-3:is(#{$tr}) { --textroll-delay: 0.75s; }
.delayed-4:is(#{$tr}) { --textroll-delay: 1s; }
.delayed-5:is(#{$tr}) { --textroll-delay: 1.25s; }
.delayed-6:is(#{$tr}) { --textroll-delay: 1.5s; }

/* slowed（再生時間の延長） */
.slowed-1:is(#{$tr})   { --textroll-duration: 1.5s; }
.slowed-2:is(#{$tr})   { --textroll-duration: 2s; }
.slowed-3:is(#{$tr})   { --textroll-duration: 3s; }
.slowed-4:is(#{$tr})   { --textroll-duration: 4s; }
.slowed-5:is(#{$tr})   { --textroll-duration: 5s; }
.slowed-10:is(#{$tr})  { --textroll-duration: 10s; }
.slowed-20:is(#{$tr})  { --textroll-duration: 20s; }
.slowed-30:is(#{$tr})  { --textroll-duration: 30s; }
.slowed-60:is(#{$tr})  { --textroll-duration: 60s; }

/* fasted（再生時間の短縮） */
.fasted-1:is(#{$tr})   { --textroll-duration: 0.75s; }
.fasted-2:is(#{$tr})   { --textroll-duration: 0.5s; }
.fasted-3:is(#{$tr})   { --textroll-duration: 0.25s; }

/* staggered（文字間隔） */
.staggered-0:is(#{$tr})    { --textroll-stagger: 0ms; }
.staggered-10:is(#{$tr})   { --textroll-stagger: 10ms; }
.staggered-30:is(#{$tr})   { --textroll-stagger: 30ms; }
.staggered-60:is(#{$tr})   { --textroll-stagger: 60ms; }
.staggered-90:is(#{$tr})   { --textroll-stagger: 90ms; }
.staggered-120:is(#{$tr})  { --textroll-stagger: 120ms; }

/* synced（全文字同時＝stagger を殺す） */
.is-synced:is(#{$tr})  { --textroll-stagger: 0ms; }


/* §印刷時（Scrollin と同じ思想。ただし対象は container ではなく [data-char]。a11yクローンを露出させないよう限定）
────────────── ༚༶⊰⟡⊱༶༚༺*/
@media print {
	:is(#{$tr}) {
		visibility: visible !important;
	}

	:is(#{$tr}) [data-char] {
		animation: none !important;
		transition: none !important;
		opacity: 1 !important;
		transform: none !important;
		clip-path: none !important;
	}
}
