/* ============================================================
   MOTION — 스크롤 리빌 시스템
   tokens/base/layout/components 다음에 로드한다.

   원칙
     1. 모션은 화려함이 아니라 신뢰를 만든다. 눈에 띄면 실패다.
     2. transform 과 opacity 만 쓴다. 그래야 컴포지터에서 처리되어 60fps 가 나온다.
     3. 한 번 나타난 요소는 다시 숨기지 않는다. 스크롤을 되돌릴 때 다시
        사라지면 사용자가 자기가 뭘 잘못했나 생각하게 된다.
     4. prefers-reduced-motion 이면 전부 즉시 표시한다.

   ⚠ 디자인 시스템의 "hover 시 transform 금지" 규칙과 충돌하지 않는다.
     그 규칙은 마우스에 반응해 요소가 튀는 것을 막으려는 것이고,
     여기서는 스크롤 진입 시 한 번만 일어나는 등장 연출이다.

   ⚠⚠ 숨김 상태는 전부 .js-reveal 아래에만 둔다.
     이 클래스는 <head> 의 인라인 스크립트가 붙인다.
     JS 가 죽거나, 차단되거나, IntersectionObserver 가 동작하지 않으면
     클래스가 없으므로 콘텐츠는 그냥 보인다.
     연출을 못 하는 것과 내용이 사라지는 것은 전혀 다른 문제다.
   ============================================================ */

:root {
  /* 감속이 길게 끌리는 곡선. 끝맺음이 부드러워 고급스럽게 읽힌다. */
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-reveal: 900ms;
  --dur-reveal-fast: 640ms;
  /* 연속 요소 사이 간격. 너무 짧으면 동시에 뜨고, 길면 굼떠 보인다. */
  --stagger: 80ms;
}

/* ------------------------------------------------------------
   1. 기본 리빌 — 아래에서 올라오며 나타난다
   ------------------------------------------------------------ */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 28px, 0);
  transition: opacity var(--dur-reveal) var(--ease-reveal),
    transform var(--dur-reveal) var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}

.js-reveal [data-reveal].is-revealed {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  /* 연출이 끝나면 합성 레이어를 놓아준다. 계속 쥐고 있으면 메모리만 먹는다. */
  will-change: auto;
}

/* 살짝만 올라오는 변형 — 본문·캡션처럼 가벼운 요소용 */
.js-reveal [data-reveal="soft"] {
  transform: translate3d(0, 14px, 0);
  transition-duration: var(--dur-reveal-fast);
}

/* 페이드만 — 위치가 이미 확정된 큰 블록용 */
.js-reveal [data-reveal="fade"] {
  transform: none;
}

/* ------------------------------------------------------------
   2. 마스크 리빌 — 이미지가 아래에서 위로 드러난다
   clip-path 는 리페인트가 없어 이미지에 쓰기 좋다.
   ------------------------------------------------------------ */
.js-reveal [data-reveal="mask"] {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1100ms var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
}

.js-reveal [data-reveal="mask"].is-revealed {
  clip-path: inset(0 0 0 0);
}

/* 마스크가 열리는 동안 안쪽 이미지는 아주 천천히 제자리를 찾는다.
   두 속도가 겹치면서 깊이가 생긴다. */
.js-reveal [data-reveal="mask"] img,
.js-reveal [data-reveal="mask"] .media__img {
  transform: scale(1.08);
  transition: transform 1600ms var(--ease-reveal);
  transition-delay: var(--reveal-delay, 0ms);
}

.js-reveal [data-reveal="mask"].is-revealed img,
.js-reveal [data-reveal="mask"].is-revealed .media__img {
  transform: scale(1);
}

/* ------------------------------------------------------------
   3. 순차 등장 — 자식들이 차례로 나타난다
   JS 가 --i 를 매기고 여기서 지연으로 환산한다.
   ------------------------------------------------------------ */
.js-reveal [data-reveal-group] > * {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity var(--dur-reveal) var(--ease-reveal),
    transform var(--dur-reveal) var(--ease-reveal);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}

.js-reveal [data-reveal-group].is-revealed > * {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ------------------------------------------------------------
   4. 히어로 진입 — 스크롤이 아니라 로드 직후 한 번
   ------------------------------------------------------------ */
.js-reveal [data-enter] {
  opacity: 0;
  transform: translate3d(0, 24px, 0);
}

.js-reveal.is-loaded [data-enter] {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity 1000ms var(--ease-reveal),
    transform 1000ms var(--ease-reveal);
  transition-delay: calc(var(--i, 0) * 110ms);
}

/* ------------------------------------------------------------
   5. 스크롤 큐 — 히어로 하단의 얇은 선
   ------------------------------------------------------------ */
.scroll-cue {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-en);
  font-size: 0.6875rem;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.scroll-cue__line {
  position: relative;
  display: block;
  width: 52px;
  height: 1px;
  background-color: rgba(255, 255, 255, 0.22);
  overflow: hidden;
}

/* 선 위를 밝은 조각이 천천히 지나간다. 화살표보다 조용하다. */
.scroll-cue__line::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: rgba(255, 255, 255, 0.85);
  transform: translateX(-100%);
  animation: cue-sweep 2600ms var(--ease-reveal) infinite;
}

@keyframes cue-sweep {
  0% {
    transform: translateX(-100%);
  }
  60%,
  100% {
    transform: translateX(100%);
  }
}

/* ------------------------------------------------------------
   6. 섹션 번호 — 페이지의 척추

   원래는 11px 회색 서수를 제목 위에 얹어만 두었다. 그런데 화면에서는
   무엇에도 걸리지 않고 떠 있어서 편집 장치가 아니라 사고처럼 읽혔다.
   Home 은 섹션이 여섯이고 전부 같은 구조(번호 → eyebrow → 제목 → 본문)라
   리듬을 만들 것이 이 번호밖에 없는데, 그게 일을 안 하고 있었다.

   그래서 번호 옆으로 괘선을 끝까지 뽑는다. 편집물의 장 표시처럼
   섹션마다 같은 자리에서 같은 선이 그어지면서, 여섯 번 반복되는 구조가
   "단조로움"이 아니라 "형식"으로 읽힌다.

   선 길이는 번호가 놓인 칸이 정한다 — section-head--split 에서는 좌측
   5fr, sticky-split 에서는 aside 폭. 저절로 섹션마다 길이가 달라져
   같은 장치인데도 인상이 반복되지 않는다.
   ------------------------------------------------------------ */
.section-index {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  font-family: var(--font-en);
  font-size: 0.75rem;
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  color: var(--teal-500);
}

.section-index::after {
  content: "";
  flex: 1;
  height: 1px;
  background-color: var(--color-border);
  /* 선이 왼쪽(번호)에서 자란다. 번호가 선을 끌고 나가는 것처럼 보인다. */
  transform-origin: left center;
}

.section--dark .section-index {
  color: var(--teal-300);
}

.section--dark .section-index::after {
  background-color: var(--color-border-dark);
}

/* 괘선 그리기 — 섹션이 화면에 들어올 때 한 번.
   숨김 상태를 .js-reveal 아래에 두는 것은 여기서도 같다.
   JS 가 죽으면 선은 그냥 처음부터 그어져 있다. */
.js-reveal [data-reveal] .section-index::after {
  transform: scaleX(0);
  transition: transform 1100ms var(--ease-reveal);
  transition-delay: calc(var(--reveal-delay, 0ms) + 180ms);
}

.js-reveal [data-reveal].is-revealed .section-index::after {
  transform: scaleX(1);
}

/* ------------------------------------------------------------
   6-2. 특전 상단선 그리기 — 이 페이지의 유일한 '장면'

   3대 특전은 이 회사가 파는 조건의 실체이고, 화주의 문의 의향이
   가장 높아지는 지점이다. 그런데 화면에서는 서비스 7개 그리드와
   같은 무게로 지나갔다. 여기 한 곳에만 연출을 준다.

   teal 선 세 개가 왼쪽에서 차례로 그어진다. 요소가 튀거나 떠오르지 않고
   선만 자란다 — B2B 화면에서 감당할 수 있는 최대치가 이 정도다.
   0.8초 안에 끝나고 다시는 일어나지 않는다.

   ⚠ border-top 을 그대로 두고 .js-reveal 아래에서만 투명하게 만든다.
     JS 가 죽으면 선은 원래대로 처음부터 그어져 있다.
   ------------------------------------------------------------ */
.step::before {
  content: "";
  position: absolute;
  top: calc(var(--border-w) * -2);
  left: 0;
  width: 100%;
  height: 2px;
  background-color: var(--teal-500);
  transform-origin: left center;
}

/* 데스크톱에서 칸 사이 여백만큼 선을 물려 둔다 —
   .step 이 padding-right 로 간격을 만들기 때문에 선이 칸보다 길어야
   세 개가 같은 굵기의 한 줄처럼 읽힌다. */
@media (min-width: 900px) {
  .step:not(:last-child)::before {
    width: calc(100% + var(--space-3));
  }
}

.js-reveal [data-reveal-group] > .step {
  border-top-color: transparent;
}

.js-reveal [data-reveal-group] > .step::before {
  transform: scaleX(0);
  transition: transform 900ms var(--ease-reveal);
  transition-delay: calc(var(--i, 0) * 140ms + 120ms);
}

.js-reveal [data-reveal-group].is-revealed > .step::before {
  transform: scaleX(1);
}

/* ------------------------------------------------------------
   6-3. 서비스 행 괘선 그리기 — Home 특전과 같은 어법

   Services 는 같은 무게의 행 7개가 이어져 명세서처럼 읽혔다.
   Home 에서 넘어온 사람에게는 리빌이 하나도 없어 화면이 갑자기
   정적으로 바뀌기도 했다.

   행마다 하단 괘선이 왼쪽에서 차례로 그어진다. 특전(3개)보다 항목이
   많으므로 간격을 70ms 로 좁혔다 — 140ms 면 마지막 행까지 1초가 걸려
   기다리는 느낌이 든다.

   ⚠ border-bottom 은 그대로 두고 .js-reveal 아래에서만 투명하게 만든다.
     JS 가 죽으면 괘선이 처음부터 그어져 있다.
   ------------------------------------------------------------ */
.service-row::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: calc(var(--border-w) * -1);
  width: 100%;
  height: var(--border-w);
  background-color: var(--color-border);
  transform-origin: left center;
}

.js-reveal [data-reveal-group] > .service-row {
  border-bottom-color: transparent;
}

.js-reveal [data-reveal-group] > .service-row::after {
  transform: scaleX(0);
  transition: transform 820ms var(--ease-reveal);
  transition-delay: calc(var(--i, 0) * 70ms + 100ms);
}

.js-reveal [data-reveal-group].is-revealed > .service-row::after {
  transform: scaleX(1);
}

/* ------------------------------------------------------------
   7. 모션 최소화 — 전부 즉시 표시
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal-group] > *,
  [data-enter],
  .js-reveal.is-loaded [data-enter] {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }

  .js-reveal [data-reveal="mask"] img,
  .js-reveal [data-reveal="mask"] .media__img {
    transform: none !important;
    transition: none !important;
  }

  .scroll-cue__line::after {
    animation: none;
  }

  /* 괘선·특전 상단선·서비스 행 하단선은 그어진 상태로 둔다 */
  .section-index::after,
  .step::before,
  .service-row::after {
    transform: none !important;
    transition: none !important;
  }
}
