/* ===========================================================================
   CoSpark — モーションの共通基盤
   ---------------------------------------------------------------------------
   全ページで同じ「動きの訛り」を持たせるための土台。ここに置くのは
   意味を持った動きだけで、装飾のためだけのアニメーションは入れない。

   使い方はHTML側の属性だけで完結する（motion.js が拾う）：
     data-reveal            … 画面に入ったらふわりと現れる
     data-reveal="left"     … 向きの指定（up / left / right / scale / mask）
     data-reveal-group      … 子要素を少しずつ遅らせて出す
     data-split="char"      … 見出しを1文字ずつ出す（word も可）
     data-count="450"       … 数字を数える
     data-parallax=".12"    … スクロールに対してゆっくり動く
     data-magnetic          … カーソルに少し吸い付くボタン
     data-tilt              … カーソルの位置に合わせてわずかに傾くカード

   JSが動かない環境では .js-motion が付かず、すべて最初から見えたまま。
   演出は「見えている状態」を壊さない範囲でだけ足す。
   =========================================================================== */

:root {
  /* --- イージング ---------------------------------------------------------
     ヒーローの見出しが飛び出して戻る動きで既に使っていた曲線を、サイト全体の
     基準に昇格させた。急に出て、最後の3割をかけて静かに着地する形。
     「勢いはあるが、乱暴ではない」— CoSparkの人柄をそのまま曲線にしたもの。 */
  --ease-out:   cubic-bezier(.16, .84, .34, 1);
  /* 触った瞬間の反応。立ち上がりが速く、ホバーが「重い」と感じさせない */
  --ease-soft:  cubic-bezier(.32, .72, 0, 1);
  /* 行って戻る動き（パネルの浮き沈みなど）用の対称な曲線 */
  --ease-inout: cubic-bezier(.65, .05, .36, 1);

  /* --- 速度 ---------------------------------------------------------------
     移動距離に対応させた3段階だけを使う。中間の速度を作らないことで、
     サイト全体の動きが「同じ人が動かしている」ように揃う。
       fast … 色・不透明度だけが変わる（距離ゼロ）
       base … カードが浮く程度（数px〜十数px）
       slow … セクションが立ち上がる（数十px以上） */
  --t-fast: .22s;
  --t-base: .55s;
  --t-slow: .9s;

  /* 兄弟要素をずらす間隔。40ms未満だと同時に見え、90msを超えると
     「順番に読み込まれている」ように見える。55msがひと続きの所作に読める境目 */
  --stagger: 55ms;

  /* パネルの角丸と、上の面に落ちる影。奥行きはこの2つだけで作る */
  --panel-r: 40px;
  --panel-shadow: 0 -26px 60px -28px rgba(60, 38, 28, .30);
}

/* ===========================================================================
   1. 背景の奥行き
   ---------------------------------------------------------------------------
   単色の面をやめる。ただし「温かい・誠実」を壊さないよう、気づかれない濃さに
   留める。目に見えるグラデーションではなく、面に厚みを与えるだけの量。
   =========================================================================== */

/* 紙の目のようなノイズ。180pxのタイルを1枚だけ描いて敷き詰めるので、
   画面全体にfeTurbulenceをかけるより桁違いに軽い。HTTPリクエストも増えない */
.grain::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 0;
  opacity: .030;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
}
/* 暗い面では黒いノイズが沈んで見えないので、光らせる側に回す */
.panel-ink.grain::after { opacity: .045; mix-blend-mode: overlay; }

/* ===========================================================================
   2. せり上がるパネル（＝セクション同士のつながり）
   ---------------------------------------------------------------------------
   セクションを直線で切り替えると、そこで一度スクロールが「終わって」しまう。
   代わりに、次のセクションが前のセクションの上へ、角の丸い面としてせり上がる
   構造にした。境界は線ではなく、影の落ちた「重なり」になる。

   この重なりはCoSparkが売っているもの（毎日を積み重ねる）そのものなので、
   動きに意味がある。単に境目を隠すための装飾ではない。
   =========================================================================== */
.panel {
  position: relative;
  border-radius: var(--panel-r) var(--panel-r) 0 0;
  /* 前のパネルの下端を隠す。1pxの隙間から下の色が覗くのを防ぐ */
  margin-top: -1px;
  box-shadow: var(--panel-shadow);
  /* 角丸から中身がはみ出さないように。中の光も内側に収まる */
  isolation: isolate;
}
/* 角丸の内側に沿って光の縁を1本入れる。パネルが「面」ではなく
   「厚みのある板」に見えるのはこの1本の効果が大きい */
.panel::before {
  content: '';
  position: absolute; left: 0; right: 0; top: 0; height: var(--panel-r);
  border-radius: inherit;
  pointer-events: none;
  z-index: 2;
  background: linear-gradient(to bottom, rgba(255,255,255,.55), rgba(255,255,255,0));
  -webkit-mask: linear-gradient(to bottom, #000 0 1.5px, transparent 1.5px);
          mask: linear-gradient(to bottom, #000 0 1.5px, transparent 1.5px);
}
.panel-ink::before { background: linear-gradient(to bottom, rgba(255,255,255,.16), rgba(255,255,255,0)); }

/* --- 通り過ぎた板が、奥へ下がる ---
   せり上がる形はもともと作ってあったが、板は最後まで同じ大きさのままだった。
   そのため「次が上に載った」のではなく「次に切り替わった」ようにしか見えず、
   4,400pxのページに章の区切りが生まれていなかった。

   出ていく板を奥へ引くと、上に載る板との間に前後関係ができる。
   下がるのは 2.5% だけ。これ以上引くと板の両脇に地色が見えて、
   重なりではなく「縮んだ四角」になる。

   スクロール駆動なので監視も計算も要らない。未対応の環境と、動きを減らす
   設定の人には何も起きず、これまでどおり板が並ぶ。 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .panel {
      /* 縮む基準は上端。下端を基準にすると、次の板との継ぎ目が動いて
         隙間が開いたり閉じたりして見える */
      transform-origin: 50% 0;
      animation: panelRecede linear both;
      animation-timeline: view();
      /* 板の下端が画面から出ていく区間だけで進める */
      animation-range: exit 0% exit 100%;
      will-change: transform;
    }
    @keyframes panelRecede {
      to { transform: scale(.975); }
    }
    /* フッターは最後の板。奥へ下がる先が無いので動かさない */
    footer.panel { animation: none; transform: none; }
  }
}

/* 暗いパネルの中に、火花の残り火のような温かい光をひとつ置く。
   これがあるので、黒が「冷たい黒」ではなく「火のそばの暗がり」に見える */
.panel-ink { position: relative; overflow: hidden; }
.panel-ink > .wrap { position: relative; z-index: 1; }
.panel-ink::after {
  content: '';
  position: absolute; left: 50%; top: -18%;
  width: min(1100px, 130%); aspect-ratio: 2 / 1;
  transform: translateX(-50%);
  pointer-events: none; z-index: 0;
  background: radial-gradient(ellipse at center,
              rgba(255, 122, 40, .17) 0%,
              rgba(216, 30, 18, .09) 38%,
              rgba(216, 30, 18, 0) 72%);
}

/* ===========================================================================
   3. Reveal — 画面に入ったときの現れ方
   ---------------------------------------------------------------------------
   ぼかしを外しながら現れるのは、「奥から手前にピントが合う」動きに見えるため。
   単なるフェードより奥行きが出る。ただしぼかしの実行コストは高いので、
   狭い画面では距離だけの動きに落とす（下の @media を参照）。
   =========================================================================== */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translate3d(var(--rv-x, 0), var(--rv-y, 24px), 0) scale(var(--rv-s, 1));
  filter: blur(var(--rv-blur, 7px));
  transition:
    opacity   var(--t-slow) var(--ease-out) var(--rv-delay, 0ms),
    transform var(--t-slow) var(--ease-out) var(--rv-delay, 0ms),
    filter    var(--t-slow) var(--ease-out) var(--rv-delay, 0ms);
}
.js-motion [data-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* 向きの指定。横から出すのは「並んだものの1つ」ではなく
   「話の流れが左から来た」と読ませたいときだけに使う */
.js-motion [data-reveal="left"]  { --rv-x: -30px; --rv-y: 0; }
.js-motion [data-reveal="right"] { --rv-x:  30px; --rv-y: 0; }
.js-motion [data-reveal="scale"] { --rv-y: 14px; --rv-s: .965; }
/* 静かに出したいところ用。移動をほぼ無くして、ピントだけ合わせる */
.js-motion [data-reveal="soft"]  { --rv-y: 10px; --rv-blur: 9px; }

/* 動き終わったものからGPUの負担を降ろす。JSが .is-done を付ける */
.js-motion [data-reveal]:not(.is-done) { will-change: transform, opacity, filter; }
.js-motion [data-reveal].is-done { will-change: auto; }

/* ===========================================================================
   4. SplitText — 見出しを1文字ずつ
   ---------------------------------------------------------------------------
   本文まで1文字ずつにすると読めないので、見出しにだけ使う。
   日本語は1文字が1単語に近いので、char単位でも意味が壊れない。
   読み上げソフトには分割前の一文を aria-label で渡している（motion.js側）。
   =========================================================================== */
.js-motion [data-split] .sp-ch,
.js-motion [data-split] .sp-wd {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, .5em, 0);
  transition:
    opacity   .62s var(--ease-out) var(--sp-d, 0ms),
    transform .78s var(--ease-out) var(--sp-d, 0ms);
}
.js-motion [data-split].is-in .sp-ch,
.js-motion [data-split].is-in .sp-wd { opacity: 1; transform: none; }

/* 本文は見出しより一拍おいて出す。見出し→本文の順に目が動くのが自然な読み順で、
   同時に出すと目のやり場が決まらない */
.js-motion [data-reveal][data-after] { --rv-delay: 180ms; }

/* ===========================================================================
   5. カード
   ---------------------------------------------------------------------------
   浮く・締まる・光が横切る、の3つだけ。派手さは足さず、
   「触れられるものだ」と分かる最低限の反応に絞る。
   =========================================================================== */
.card-i {
  position: relative;
  overflow: hidden;
  transition:
    transform    var(--t-base) var(--ease-soft),
    box-shadow   var(--t-base) var(--ease-soft),
    border-color var(--t-fast) var(--ease-soft);
  transform-style: preserve-3d;
}
/* 斜めに横切る細い光。カードの表面が平らな紙ではなく、
   わずかに艶のある面であることを示す */
.card-i::after {
  content: '';
  position: absolute; inset: -60% -10%;
  pointer-events: none;
  background: linear-gradient(105deg,
              rgba(255,255,255,0) 42%,
              rgba(255,255,255,.60) 50%,
              rgba(255,255,255,0) 58%);
  transform: translateX(-115%);
  opacity: 0;
  transition: transform .85s var(--ease-soft), opacity .25s ease;
}
@media (hover: hover) and (pointer: fine) {
  .card-i:hover {
    transform: translateY(-5px);
    border-color: rgba(216, 30, 18, .34);
    box-shadow:
      0 22px 44px -22px rgba(60, 38, 28, .26),
      0 6px 14px -8px rgba(216, 30, 18, .16);
  }
  .card-i:hover::after { transform: translateX(115%); opacity: 1; }
  /* data-tilt が付いたカードだけ、カーソルの位置に合わせて傾く。
     角度の上限は4°。これを超えると「玩具っぽさ」が出て信頼感を損なう */
  .card-i[data-tilt]:hover {
    transform:
      perspective(900px)
      rotateX(var(--tilt-x, 0deg))
      rotateY(var(--tilt-y, 0deg))
      translateY(-5px);
  }
}
/* 暗いパネルの中のカードは、白い光ではなく火の色で反応させる */
.panel-ink .card-i::after {
  background: linear-gradient(105deg,
              rgba(255,170,110,0) 42%,
              rgba(255,170,110,.20) 50%,
              rgba(255,170,110,0) 58%);
}

/* ===========================================================================
   6. ボタン・カーソル
   ---------------------------------------------------------------------------
   カーソル追従は「常に何かが付いてくる」と鬱陶しく、信頼感を下げる。
   押せるものの上に来たときだけ現れる輪にして、装飾ではなく合図にする。
   =========================================================================== */
.js-motion .cursor-ring {
  position: fixed; top: 0; left: 0;
  width: 42px; height: 42px; margin: -21px 0 0 -21px;
  border-radius: 50%;
  border: 1.5px solid rgba(216, 30, 18, .55);
  pointer-events: none; z-index: 9999;
  opacity: 0;
  transform: translate3d(-100px, -100px, 0) scale(.55);
  transition: opacity .28s ease, scale .28s var(--ease-soft);
}
.js-motion .cursor-ring.is-on { opacity: 1; scale: 1; }
@media (hover: none), (pointer: coarse) { .cursor-ring { display: none !important; } }

/* 磁石ボタン。動かすのはボタンの中身ではなく要素そのもので、
   最大でも6px。指の下で「わずかに迎えに来る」程度に留める */
[data-magnetic] {
  transition: transform var(--t-base) var(--ease-soft);
}
[data-magnetic].is-pull { transition: transform .18s var(--ease-soft); }

/* ===========================================================================
   7. ヘッダー
   ---------------------------------------------------------------------------
   最上部では透明のまま（ヒーローの余白を邪魔しない）。
   スクロールが始まったらガラスになり、下へ読み進める間は消える。
   読む行為の邪魔をせず、戻ろうとした瞬間には必ずそこにある、という約束。
   =========================================================================== */
nav {
  transition:
    transform  .45s var(--ease-soft),
    background .35s ease,
    box-shadow .35s ease,
    height     .35s var(--ease-soft);
}
nav.is-stuck {
  background: rgba(250, 248, 244, .72);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  box-shadow: 0 1px 0 rgba(60, 38, 28, .08), 0 8px 24px -18px rgba(60, 38, 28, .5);
}
/* ガラスが敷かれたら、中のチップは背景を持たなくていい（二重の面になるため） */
nav.is-stuck .logo,
nav.is-stuck .nav-login {
  background: transparent;
  border-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
nav.is-hidden { transform: translateY(-100%); }

/* ===========================================================================
   8. フッターを流れる光
   ---------------------------------------------------------------------------
   最後の暗い面を、消灯した画面ではなく「まだ火が残っている」状態にする。
   14秒で1往復。目で追える速さにすると気が散るので、
   視界の端でようやく気づく程度の速度と濃さにしている。
   =========================================================================== */
.footer-sweep {
  position: absolute; inset: 0;
  overflow: hidden; pointer-events: none;
}
.footer-sweep::before {
  content: '';
  position: absolute; top: -60%; bottom: -60%; left: -30%;
  /* 帯を細く濃くすると「線が横切った」と読めてしまう。
     広く薄くして、明るさがゆっくり通り過ぎるだけにする */
  width: 34%;
  background: linear-gradient(to right,
              rgba(255,150,80,0) 0%,
              rgba(255,150,80,.045) 42%,
              rgba(255,196,120,.075) 50%,
              rgba(255,150,80,.045) 58%,
              rgba(255,150,80,0) 100%);
  transform: rotate(14deg) translateX(0);
  animation: footerSweep 14s linear infinite;
}
@keyframes footerSweep {
  0%   { transform: rotate(14deg) translateX(0);     opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: rotate(14deg) translateX(620%);  opacity: 0; }
}

/* ===========================================================================
   9. ページ遷移
   ---------------------------------------------------------------------------
   View Transitions API に任せる。JSゼロ・CSS3行で、対応ブラウザでは
   ページ間が滑らかに繋がり、非対応ブラウザでは今まで通り即座に切り替わる。
   自前でフェードを書くと、戻るボタンや読み込み失敗時の扱いが必ず破綻する。
   =========================================================================== */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vtOut .28s var(--ease-soft) both; }
::view-transition-new(root) { animation: vtIn  .42s var(--ease-out)  both; }
@keyframes vtOut { to   { opacity: 0; transform: translateY(-8px); } }
@keyframes vtIn  { from { opacity: 0; transform: translateY(10px); } }

/* ===========================================================================
   10. オープニング（初回アクセスのみ）
   ---------------------------------------------------------------------------
   幕の色は本文の背景と同じ生成り。真っ白や真っ黒の幕にすると、
   幕が引けた瞬間に画面全体の明るさが変わって「切り替わった」感じが出る。
   同じ色にしておくと、ロゴだけが去っていくように見える。
   =========================================================================== */
.intro {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  transition: opacity .5s var(--ease-soft);
}
.intro.is-gone { opacity: 0; pointer-events: none; }

.intro-logo {
  display: flex; align-items: center; gap: 14px;
  font-size: clamp(30px, 6.4vw, 56px);
  font-weight: 700; letter-spacing: -0.045em;
  /* ヘッダーへ寄せるときの基準点。中心どうしを合わせて縮める */
  transform-origin: 50% 50%;
  transition: transform .62s var(--ease-out);
}
/* マークの絵柄はヘッダーのロゴ（.logo-dot）を借りているが、
   あちらは 20px 固定なので、そのままでは幕の大きな文字の横で豆粒になる。
   .intro を1つ噛ませて詳細度で勝ち、文字サイズに追従させる */
.intro .intro-mark {
  width: 1em; height: 1em; flex-shrink: 0;
  opacity: 0; transform: scale(.4) rotate(-40deg);
  transition: opacity .4s var(--ease-out), transform .6s var(--ease-out);
}
.intro.is-writing .intro-mark { opacity: 1; transform: none; }
/* 幕が出ている間は裏の本文を動かせないようにする。
   スクロールバーの幅ぶん画面が詰まって、幕が引けた瞬間に
   レイアウトが1回横にずれるのを防ぐため、幅は確保したままにする */
.intro-on body { overflow: hidden; }
.intro-on { scrollbar-gutter: stable; }

.intro-ch {
  display: inline-block;
  opacity: 0;
  transform: translateY(.34em);
  transition:
    opacity   .42s var(--ease-out) var(--i, 0ms),
    transform .58s var(--ease-out) var(--i, 0ms);
}
.intro.is-writing .intro-ch { opacity: 1; transform: none; }
.intro-ch.accent { color: var(--red); }

/* Spark の S が置かれた瞬間に散る小さな火花。
   ロゴが完成する前に一度だけ光ることで、名前の意味と絵が結びつく */
.intro-spark {
  position: absolute; left: 50%; top: 50%;
  width: 220px; height: 220px; margin: -110px 0 0 -110px;
  pointer-events: none; opacity: 0;
  background: radial-gradient(circle at center,
              rgba(255,255,255,.95) 0%,
              rgba(255,196,80,.75) 14%,
              rgba(255,124,20,.42) 30%,
              rgba(216, 30, 18,.16) 52%,
              rgba(216, 30, 18,0) 72%);
}
.intro.is-spark .intro-spark { animation: introSpark .72s var(--ease-out) both; }
@keyframes introSpark {
  0%   { opacity: 0; transform: scale(.12); }
  22%  { opacity: 1; transform: scale(.72); }
  100% { opacity: 0; transform: scale(1.5); }
}

/* 幕が去りはじめたら、ロゴだけ先に淡くする。
   位置の移動と濃さの変化を同時に終わらせると、消えた瞬間が目立つ */
.intro.is-landing .intro-logo { opacity: .55; transition: transform .62s var(--ease-out), opacity .5s ease .12s; }

/* ===========================================================================
   11. 環境への配慮
   =========================================================================== */

/* 狭い画面ではぼかしを外す。iPhoneでの blur() 付きトランジションは
   1画面に何枚も重なると目に見えてコマが落ちる。距離だけでも意図は伝わる */
@media (max-width: 700px) {
  .js-motion [data-reveal] { --rv-blur: 0px; --rv-y: 18px; }
  :root { --panel-r: 28px; --t-slow: .78s; }
}

/* 動きを減らす設定の人には、演出そのものを渡さない。
   「弱める」のではなく「無くす」。中身は最初から全部見えている */
@media (prefers-reduced-motion: reduce) {
  .js-motion [data-reveal],
  .js-motion [data-split] .sp-ch,
  .js-motion [data-split] .sp-wd {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
  }
  .card-i, .card-i::after, [data-magnetic], nav { transition: none !important; }
  .card-i:hover { transform: none !important; }
  .footer-sweep::before { animation: none; opacity: .5; }
  .cursor-ring { display: none !important; }
  /* オープニングは「短くする」のではなく、そもそも見せない */
  .intro { display: none !important; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}
