/* ═══════════════════════════════════════════════════════════════════════
   스크롤 효과 — ContentsDoc/03-스크롤-효과.md 구현

   세 겹으로 되어 있고, 아래로 갈수록 안전한 쪽이다.

     1. CSS 스크롤 연동 (animation-timeline)  — 가장 부드럽다. JS 0줄
     2. IntersectionObserver                  — 1이 안 되는 브라우저에서 (Firefox 등)
     3. 아무것도 안 함                         — JS 가 안 돌면 내용이 그냥 다 보인다

   🔴 지켜야 할 것
     · 움직임은 transform 과 opacity 로만 만든다.
       top·width·height 를 건드리면 브라우저가 화면을 다시 계산해 끊긴다.
     · prefers-reduced-motion 이면 전부 끄고 최종 상태로 즉시 보여 준다.
       멀미·전정기관 질환이 있는 분에게는 실제 피해가 간다.
     · 스크롤을 가로채지 않는다. 뒤로가기·페이지 내 검색이 망가진다.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── 기본값 ───────────────────────────────────────────────────────────
   숫자를 여기 모아 둔다. 효과의 세기를 바꿀 때 이 값만 고치면 된다. */
:root{
  --gl-rise: 24px;                              /* 아래에서 올라오는 거리 */
  --gl-slide: 40px;                             /* 옆에서 들어오는 거리 */
  --gl-zoom: 0.94;                              /* 커지며 나타날 때 시작 배율 */
  --gl-dur: 0.7s;                               /* 지속 시간 */
  --gl-ease: cubic-bezier(0.16, 1, 0.3, 1);     /* 감속 곡선 — 현 사이트와 같은 값 */
}

/* ── 등장 효과 ────────────────────────────────────────────────────────
   .js 가 붙었을 때만 숨긴다. 자바스크립트가 안 돌면 처음부터 다 보인다 —
   화면이 비어 버리는 것이 애니메이션이 없는 것보다 훨씬 나쁘다. */
.js [data-gl]{
  opacity: 0;
  transition: opacity var(--gl-dur) var(--gl-ease),
              transform var(--gl-dur) var(--gl-ease);
  transition-delay: var(--gl-delay, 0s);
  will-change: opacity, transform;
}

.js [data-gl="rise"]  { transform: translateY(var(--gl-rise)); }
.js [data-gl="fall"]  { transform: translateY(calc(var(--gl-rise) * -1)); }
.js [data-gl="left"]  { transform: translateX(calc(var(--gl-slide) * -1)); }
.js [data-gl="right"] { transform: translateX(var(--gl-slide)); }
.js [data-gl="zoom"]  { transform: scale(var(--gl-zoom)); }
.js [data-gl="fade"]  { transform: none; }

/* 글줄 올림(mask) — 글을 줄로 나눈 뒤(gl-scroll.js splitLines)에만 숨기고, 나누지 못한 것은 «올라오며 나타나기» 와 같다.
   줄 박스(overflow: hidden)가 마스크다 — 글자 아래쪽(g · j · 받침)이 잘리지 않게 아래 여백을 얹었다가 같은 만큼 뺀다. */
.js [data-gl="mask"] { transform: translateY(var(--gl-rise)); }
.js [data-gl="mask"].gl-mask-ready { opacity: 1; transform: none; transition: none; will-change: auto; }
.gl-line { display: block; overflow: hidden; padding-bottom: .16em; margin-bottom: -.16em; }
.gl-line-in { display: block; }
.js .gl-mask-ready .gl-line-in {
  transform: translateY(112%);
  transition: transform .95s var(--gl-ease) calc(var(--i, 0) * 110ms + var(--gl-delay, 0s));
  will-change: transform;
}
.js .gl-mask-ready.gl-in .gl-line-in { transform: none; will-change: auto; }

/* 떨어지기(drop) — 위에서 기울어 내려와 자리를 잡는다. 홀수 · 짝수 칸이 반대로 기울어 «흩어져 떨어지는» 느낌이 난다.
   거리는 영상(300~500px)을 웹 화면 크기에 맞춰 줄였다. 느리게 내려와 끝에서 감속(1.4s). */
:root { --gl-drop: 150px; --gl-drop-rot: 10deg; }
.js [data-gl="drop"] {
  transform: translateY(calc(var(--gl-drop) * -1)) rotate(calc(var(--gl-drop-rot) * -1));
  transition-duration: 1.4s;
  transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
}
.js [data-gl="drop"]:nth-child(even) { transform: translateY(calc(var(--gl-drop) * -1)) rotate(calc(var(--gl-drop-rot) * .8)); }
.js [data-gl="drop"].gl-in,
.js [data-gl="drop"].gl-in:nth-child(even) { transform: none; }

/* 흐리게 돌며 나타나기(spin) — 제이터 Blurry Text Spin 의 수치(ContentsDoc/35 9단계).
   글자: −45° · 3배 · 흐림 · 투명 → 제자리, 웹에서는 글자마다 1.1s 감속, 글자 사이 --gl-spin-gap(≈52ms · gl-scroll.js 가 글 길이로 정한다).
   글 전체: 살짝 기운 채(−5° · 1.1배) 2.8s 동안 천천히 바로 선다(원본은 끝까지 도는 선형 흔들림 — 웹에서는 제자리에 멈춘다).
   흐림은 글자 크기에 비례(em) — 원본 200px / 글자 170px 은 웹에서 글자가 아예 안 보여 .5em 으로 줄였다.
   나누기 전(스크립트 전)에는 섹션 전체가 «밝아지며» 나타나고, 나눈 뒤에는 섹션은 그대로 두고 글자만 움직인다. */
:root { --gl-spin-rot: -45deg; --gl-spin-scale: 3; --gl-spin-blur: .5em; }
.js [data-gl="spin"] { transform: none; }
.js [data-gl="spin"].gl-spin-ready { opacity: 1; transition: none; will-change: auto; }
.gl-spin-w { display: inline-block; white-space: nowrap; }
.gl-spin-c { display: inline-block; }
.js .gl-spin-ready .gl-spin-t { transform: rotate(-5deg) scale(1.1); transition: transform 2.8s cubic-bezier(.2, .35, .3, 1) var(--gl-delay, 0s); }
.js .gl-spin-ready .gl-spin-c {
  opacity: 0;
  transform: rotate(var(--gl-spin-rot)) scale(var(--gl-spin-scale));
  filter: blur(var(--gl-spin-blur));
  transition: opacity 1.1s cubic-bezier(.16, .65, .3, 1), transform 1.1s cubic-bezier(.16, .65, .3, 1), filter 1.1s cubic-bezier(.16, .65, .3, 1);
  transition-delay: calc(var(--c, 0) * var(--gl-spin-gap, 52ms) + var(--gl-delay, 0s));
  will-change: opacity, transform, filter;
}
.js .gl-spin-ready.gl-in .gl-spin-t { transform: none; }
.js .gl-spin-ready.gl-in .gl-spin-c { opacity: 1; transform: none; filter: none; }
.js .gl-spin-ready:not(.gl-spin-settled) .gl-spin-t { will-change: transform; }
.js .gl-spin-ready.gl-spin-settled .gl-spin-c { will-change: auto; }

/* 화면에 들어오면 제자리로 */
.js [data-gl].gl-in{
  opacity: 1;
  transform: none;
  /* 다 끝난 요소까지 계속 준비 상태로 두면 메모리를 붙잡는다 */
  will-change: auto;
}

/* ── 패럴랙스 ─────────────────────────────────────────────────────────
   스크롤보다 느리게 따라오는 요소. 세기는 data-gl-parallax 로 준다. */
.js [data-gl-parallax]{
  will-change: transform;
  transform: translate3d(0, var(--gl-shift, 0px), 0);
}

/* ── 읽는 진행바 ──────────────────────────────────────────────────── */
.gl-progress{
  position: fixed; top: 0; left: 0; right: 0; height: 3px;
  background: transparent; z-index: 200; pointer-events: none;
}
.gl-progress > i{
  display: block; height: 100%; width: 0;
  background: var(--accent, #ff5a1f);
  transform-origin: 0 50%;
}

/* ── 숫자 세기 ────────────────────────────────────────────────────────
   자리수가 바뀔 때 글자가 들썩이지 않게 폭을 고정한다. */
.gl-count{ font-variant-numeric: tabular-nums; }

/* ═══ CSS 스크롤 연동이 되는 브라우저 ═══════════════════════════════
   Chrome/Edge 115+ · Safari 26+ 에서 동작한다. Firefox 는 아직 플래그 뒤라
   위의 IntersectionObserver 경로로 내려간다 (2026-09 기준).

   여기서는 "화면에 들어오는 동안" 진행되는 애니메이션을 브라우저가
   직접 돌린다. 스크롤 이벤트를 받지 않으므로 훨씬 부드럽다. */
@supports (animation-timeline: view()) {
  .js.gl-css-timeline [data-gl]:not([data-gl="mask"]):not([data-gl="drop"]):not([data-gl="spin"]){
    /* JS 가 켜 준 클래스가 붙었을 때만 넘어간다 —
       두 경로가 같은 요소에 동시에 걸리면 서로 값을 덮어쓴다. */
    opacity: 1;
    transform: none;
    transition: none;
    animation: gl-appear linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 55%;
  }
  .js.gl-css-timeline [data-gl="rise"] { --gl-from-y: var(--gl-rise); }
  .js.gl-css-timeline [data-gl="fall"] { --gl-from-y: calc(var(--gl-rise) * -1); }
  .js.gl-css-timeline [data-gl="left"] { --gl-from-x: calc(var(--gl-slide) * -1); }
  .js.gl-css-timeline [data-gl="right"]{ --gl-from-x: var(--gl-slide); }
  .js.gl-css-timeline [data-gl="zoom"] { --gl-from-s: var(--gl-zoom); }

  @keyframes gl-appear{
    from{
      opacity: 0;
      transform: translate3d(var(--gl-from-x, 0), var(--gl-from-y, 0), 0)
                 scale(var(--gl-from-s, 1));
    }
    to{ opacity: 1; transform: none; }
  }
}

/* ═══ 움직임을 원하지 않는 분 ═══════════════════════════════════════
   가장 마지막에 두어 위의 모든 규칙을 덮는다. */
@media (prefers-reduced-motion: reduce){
  .js [data-gl],
  .js.gl-css-timeline [data-gl]{
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    animation: none !important;
  }
  .js [data-gl-parallax]{ transform: none !important; }
  .js .gl-mask-ready .gl-line-in{ transform: none !important; transition: none !important; }
  .js .gl-spin-ready .gl-spin-c,
  .js .gl-spin-ready .gl-spin-t{ opacity: 1 !important; transform: none !important; filter: none !important; transition: none !important; }
  .gl-progress{ display: none; }
  /* 사이트 전체의 나머지 움직임도 함께 줄인다 */
  *, *::before, *::after{
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
