@charset "UTF-8";
/*!
 * heading-anim.css - 見出し画像アニメーションプラグイン
 * 斜めに配置された手書き見出し画像に対応
 * 依存なし / MIT License
 */

/* ============================================================
   1. 既定値(すべて CSS 変数。JS から上書きされる)
   ============================================================ */
:root {
  --ha-duration: 800ms;
  --ha-delay: 0ms;
  --ha-stagger: 0ms;
  --ha-easing: cubic-bezier(.22,.61,.36,1);
  --ha-i: 0;                        /* 分割系:ブロック番号 */
  --ha-angle: 0deg;                 /* 見出しの傾き(自動検出) */
  --ha-skew: 0%;                    /* 縦境界線の傾き量(幅に対する%) */
  --ha-over: 10%;                   /* 横方向のはみ出し余白 */
  --ha-skewy: 0%;                   /* 横境界線の傾き量(高さに対する%) */
  --ha-overy: 10%;                  /* 縦方向のはみ出し余白 */
  --ha-cx: 50%;                     /* 分割ブロックの中心X(変形の基点) */
  --ha-color: #ffe45e;              /* underline / marker の色 */
  --ha-glow: rgba(255,255,255,.92); /* glow の色 */
}

/* ============================================================
   2. FOUC 対策
   JS 読み込み時に <html class="ha-js"> が付く。
   JS 無効時はこのルール自体が効かないので画像はそのまま表示される
   ============================================================ */
.ha-js [data-ha]:not(.ha-bound) { opacity: 0 !important; }

/* ============================================================
   3. ステージ / レイヤーの基本構造
   ============================================================ */
.ha { position: relative; }
.ha--wrap { display: inline-block; vertical-align: top; line-height: 0; max-width: 100%; }
.ha--wrap .ha__layer { display: block; max-width: 100%; height: auto; }

/* clip-path はこの内側の要素に当てる。
   IntersectionObserver は監視対象自身の clip-path も可視判定に含めるため、
   ステージ(監視対象)を直接クリップすると永久に発火しなくなる */
.ha__clip { display: block; }
.ha--bg > .ha__clip { position: absolute; inset: 0; }

.ha--bg .ha__layer,
.ha__slice,
.ha__gloss {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  pointer-events: none;
}
.ha__slice, .ha__gloss { z-index: 1; }
.ha__layer { backface-visibility: hidden; transform-origin: 50% 50%; }

/* 分割モード:スライス生成が完了するまで元画像を隠す */
.ha--sliced .ha__layer { opacity: 0; }
/* 再生完了後は元画像に戻す(スライス境界のジャギー回避) */
.ha--swapped .ha__layer { opacity: 1; }
.ha--swapped .ha__slice { display: none; }

/* ============================================================
   4. アニメーション共通ルール
   animation-name を --ha-name から供給しているため、
   バリエーション追加は @keyframes を足すだけで済む
   ============================================================ */
.ha-anim {
  animation-name: var(--ha-name, none);
  animation-duration: var(--ha-duration);
  animation-timing-function: var(--ha-easing);
  animation-delay: calc(var(--ha-delay) + var(--ha-stagger) * var(--ha-i));
  animation-fill-mode: both;
  animation-play-state: paused;   /* 発火前は from の状態で停止させておく */
}
.ha.ha-in .ha-anim,
.ha.ha-in.ha-anim { animation-play-state: running; }
.ha.ha-playing .ha-anim { will-change: opacity, transform, clip-path, filter; }

/* ============================================================
   5. アニメーション定義
   ============================================================ */

/* --- 基本 ------------------------------------------------- */
@keyframes ha-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes ha-fade-up {
  from { opacity: 0; transform: translate3d(0, 24%, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes ha-zoom {
  from { opacity: 0; transform: scale(.86); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes ha-blur {
  from { opacity: 0; filter: blur(14px); }
  to   { opacity: 1; filter: blur(0); }
}
@keyframes ha-pop {
  0%   { opacity: 0; transform: scale(.7); }
  60%  { opacity: 1; transform: scale(1.06); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes ha-ink {
  0%   { opacity: 0; filter: blur(12px) contrast(.5); transform: scale(1.05); }
  55%  { opacity: 1; }
  100% { opacity: 1; filter: blur(0) contrast(1); transform: scale(1); }
}

/* 傾きを利用:大きく傾いた状態から本来の角度に収まる */
.ha--tilt .ha__layer { transform-origin: 0% 100%; }
@keyframes ha-tilt {
  from { opacity: 0; transform: rotate(calc(var(--ha-angle) * -4)) translate3d(0, 12%, 0); }
  to   { opacity: 1; transform: rotate(0deg) translate3d(0, 0, 0); }
}

/* --- ワイプ系(境界線が見出しの傾きに沿う) ----------------- */
@keyframes ha-wipe {
  from {
    clip-path: polygon(
      calc(-2 * var(--ha-over)) 0%,
      calc(0% - var(--ha-over) + var(--ha-skew)) 0%,
      calc(0% - var(--ha-over) - var(--ha-skew)) 100%,
      calc(-2 * var(--ha-over)) 100%);
  }
  to {
    clip-path: polygon(
      calc(-2 * var(--ha-over)) 0%,
      calc(100% + var(--ha-over) + var(--ha-skew)) 0%,
      calc(100% + var(--ha-over) - var(--ha-skew)) 100%,
      calc(-2 * var(--ha-over)) 100%);
  }
}
@keyframes ha-curtain {
  from {
    clip-path: polygon(
      calc(50% + var(--ha-skew)) 0%,
      calc(50% + var(--ha-skew)) 0%,
      calc(50% - var(--ha-skew)) 100%,
      calc(50% - var(--ha-skew)) 100%);
  }
  to {
    clip-path: polygon(
      calc(0% - var(--ha-over) + var(--ha-skew)) 0%,
      calc(100% + var(--ha-over) + var(--ha-skew)) 0%,
      calc(100% + var(--ha-over) - var(--ha-skew)) 100%,
      calc(0% - var(--ha-over) - var(--ha-skew)) 100%);
  }
}

/* せり上がり:境界線を傾けつつ中身も translate させる */
@keyframes ha-reveal-clip {
  from {
    clip-path: polygon(
      0%   calc(100% + var(--ha-overy) - var(--ha-skewy)),
      100% calc(100% + var(--ha-overy) + var(--ha-skewy)),
      100% 200%, 0% 200%);
  }
  to {
    clip-path: polygon(
      0%   calc(0% - var(--ha-overy) - var(--ha-skewy)),
      100% calc(0% - var(--ha-overy) + var(--ha-skewy)),
      100% 200%, 0% 200%);
  }
}
@keyframes ha-reveal-move {
  from { transform: translate3d(0, 32%, 0); }
  to   { transform: translate3d(0, 0, 0); }
}

/* --- 刷毛でひと掃き(先端をぼかしたマスクが傾きに沿って走る) --- */
.ha--brush .ha__layer {
  -webkit-mask-image: linear-gradient(calc(90deg + var(--ha-angle)),
    #000 0%, #000 40%, rgba(0,0,0,0) 44%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(calc(90deg + var(--ha-angle)),
    #000 0%, #000 40%, rgba(0,0,0,0) 44%, rgba(0,0,0,0) 100%);
  -webkit-mask-size: 400% 100%;   mask-size: 400% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
@keyframes ha-brush {
  from { -webkit-mask-position: 100% 50%; mask-position: 100% 50%; }
  to   { -webkit-mask-position: 0% 50%;   mask-position: 0% 50%; }
}

/* --- 手書き:文字ブロックを1つずつ、左から右へ書き進める ------
   進捗 --ha-p を @property で数値として登録し、
   各ブロックの左端 --ha-a から右端 --ha-b まで clip-path を伸ばす */
@property --ha-p { syntax: "<number>"; inherits: false; initial-value: 0; }

.ha--handwrite .ha__slice {
  clip-path: polygon(
    calc(var(--ha-a) + var(--ha-skew)) 0%,
    calc(var(--ha-a) + (var(--ha-b) - var(--ha-a)) * var(--ha-p) + var(--ha-skew)) 0%,
    calc(var(--ha-a) + (var(--ha-b) - var(--ha-a)) * var(--ha-p) - var(--ha-skew)) 100%,
    calc(var(--ha-a) - var(--ha-skew)) 100%);
}
@keyframes ha-handwrite {
  from { --ha-p: 0; }
  to   { --ha-p: 1; }
}

/* --- 分割系(1文字ずつ) ------------------------------------ */
.ha__slice { transform-origin: var(--ha-cx) 70%; }
@keyframes ha-char-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes ha-char-up {
  from { opacity: 0; transform: translate3d(0, 26%, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes ha-wave {
  0%   { opacity: 0; transform: translate3d(0, -34%, 0); }
  55%  { opacity: 1; transform: translate3d(0, 7%, 0); }
  100% { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes ha-random {
  from { opacity: 0; transform: scale(.55) rotate(-8deg); }
  to   { opacity: 1; transform: scale(1) rotate(0deg); }
}

/* --- 装飾系 ------------------------------------------------ */
.ha--underline .ha__clip,
.ha--marker .ha__clip { position: relative; z-index: 2; }
.ha--marker.ha--bg > .ha__clip,
.ha--underline.ha--bg > .ha__clip { position: absolute; }

.ha--underline::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 4%;
  height: var(--ha-line, 5%);
  min-height: 3px;
  background: var(--ha-color);
  border-radius: 999px;
  transform-origin: 0% 50%;
  z-index: 1;
  animation: ha-line var(--ha-duration) var(--ha-easing) var(--ha-delay) both paused;
}
.ha--marker::before {
  content: "";
  position: absolute;
  left: -1%; right: -1%; bottom: 10%;
  height: 48%;
  background: var(--ha-color);
  border-radius: 3px;
  transform-origin: 0% 50%;
  z-index: 1;
  animation: ha-line var(--ha-duration) var(--ha-easing) var(--ha-delay) both paused;
}
.ha--underline.ha-in::after,
.ha--marker.ha-in::before { animation-play-state: running; }

@keyframes ha-line {
  from { transform: rotate(var(--ha-angle)) scaleX(0); }
  to   { transform: rotate(var(--ha-angle)) scaleX(1); }
}

/* きらめきが文字の上を走る(mask は JS が画像で設定する) */
.ha--glow > .ha__gloss {
  background-image: linear-gradient(calc(90deg + var(--ha-angle)),
    rgba(255,255,255,0) 38%, var(--ha-glow) 50%, rgba(255,255,255,0) 62%);
  background-size: 300% 100%;
  -webkit-mask-size: 100% 100%;   mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  z-index: 3;
  animation: ha-gloss var(--ha-duration) linear
             calc(var(--ha-delay) + var(--ha-duration) * .55) both paused;
}
.ha--glow.ha-in > .ha__gloss { animation-play-state: running; }
@keyframes ha-gloss {
  from { background-position: 200% 0; }
  to   { background-position: -110% 0; }
}

/* ============================================================
   6. アクセシビリティ:動きを減らす設定を尊重する
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .ha-anim,
  .ha--underline::after,
  .ha--marker::before,
  .ha--glow > .ha__gloss { animation: none !important; }
  .ha__layer { opacity: 1 !important; filter: none !important; transform: none !important; }
  .ha, .ha__clip, .ha__slice { clip-path: none !important; }
  .ha__slice, .ha__gloss { display: none !important; }
}
