---
import type { Content } from "../i18n/content";

interface Props {
  c: Content["pipeline"];
}
const { c } = Astro.props;
---

{/*
  순서를 번호 없이 표현하는 세 층:
  ① 시각  — 스파인 위의 위치가 순서를 만든다 (Beck 튜브맵)
  ② 내용  — 같은 브리프가 단계마다 변해간다 (IKEA 조립 설명서)
  ③ 의미  — <ol role="list"> — 화면에 번호는 없지만 보조기술은 "6개 중 n번째"로 읽는다
             role="list" 는 list-style:none 일 때 Safari/VoiceOver 가 목록 의미를 버리는 것을 되돌린다
*/}
<section id="pipeline" class="section has-bg" aria-labelledby="pipeline-h">
  {/* 히어로에 있던 게이트 씬을 여기로 옮겼다.
      히어로에서는 그 은유를 판독기가 자막으로 설명해야 했다 — 그림이 스스로
      말하지 못한다는 뜻이다. 여기서는 옆에 실제 여섯 단계가 적혀 있고,
      판을 통과할 때 그 단계가 켜진다. 자막이 아니라 콘텐츠가 반응한다. */}
  <div class="gate-bg" aria-hidden="true">
    <div class="gate-fallback"></div>
    <canvas id="pipeline-canvas" class="gate-canvas"></canvas>
  </div>
  {/* 제목·리드·브리프·스파인을 한 컨테이너에 담는다.
      섹션 기본 그리드에 두면 제목은 main 칼럼의 음수 마진으로 x=371,
      리드는 x=435, 스파인은 x=143 에 서서 정렬선이 셋이 된다. */}
  <div class="full spine-wrap">
    <p class="eyebrow reveal">{c.eyebrow}</p>
    <h2 id="pipeline-h" class="reveal">{c.h2}</h2>
    <p class="lead pipeline-lead reveal">{c.lead}</p>

    <div class="brief reveal"><div class="brief-core">
      <span class="brief-label">{c.briefLabel}</span>
      <p class="brief-text">{c.brief}</p>
    </div></div>

    <ol role="list" class="spine">
      {
        c.stages.map((s, i) => (
          <li class="stop reveal" data-stage={i}>
            <div class="stop-head">
              <h3 class="stop-name">{s.name}</h3>
              <p class="stop-does">{s.does}</p>
            </div>
            <p class:list={["stop-becomes", "par-lag", { mono: s.mono }]}>{s.becomes}</p>
          </li>
        ))
      }
    </ol>
    <p class="after muted reveal">{c.after}</p>
  </div>
</section>

<style>
  /* 컨테이너는 넓어도 읽는 것은 measure 안에 둔다 */
  .pipeline-lead {
    margin-bottom: var(--space-5);
    max-width: var(--measure);
  }
  .brief { max-width: var(--measure); }
  #pipeline-h { max-width: 24ch; }

  /* ---- 게이트 배경 ------------------------------------------
     sticky 로 화면에 머물고 음수 마진으로 레이아웃 자리는 차지하지 않는다.
     섹션이 지나가는 동안 카메라가 판 여섯 장을 통과한다. */
  .gate-bg {
    position: sticky;
    top: 0;
    height: 100dvh;
    /* 섹션 패딩(112px) 아래에서 시작하면 히어로 배경이 끝난 자리와
       이 캔버스 사이에 200px 짜리 검은 띠가 생긴다. 패딩만큼 끌어올린다. */
    margin-top: calc(var(--space-7) * -1);
    margin-bottom: -100dvh;
    z-index: -1;
    pointer-events: none;
    grid-column: full;
  }
  .gate-fallback,
  .gate-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
  }
  .gate-fallback {
    background:
      radial-gradient(46% 54% at 28% 34%,
        color-mix(in oklab, var(--accent) 20%, transparent), transparent 70%),
      radial-gradient(42% 50% at 76% 68%,
        color-mix(in oklab, var(--glow-2) 18%, transparent), transparent 68%);
  }
  /* 캔버스가 섹션 패딩 위까지 덮으면 이웃 섹션과 딱 붙어 보인다.
     위아래를 지워 여백을 되돌린다. */
  .gate-bg {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0%, black 6%, black 88%, transparent 100%);
  }
  .gate-canvas { opacity: 0; transition: opacity 1s var(--ease-soft); }
  .gate-canvas.is-live { opacity: 0.7; }
  /* 세로 화면에서는 캔버스가 화면 전체를 덮는다. 같은 밝기면 본문이 묻힌다 */
  @media (max-width: 760px) {
    .gate-canvas.is-live { opacity: 0.45; }
  }

  /* 통과한 단계가 켜진다. 3D 와 텍스트를 잇는 유일한 연결이다 */
  .stop { transition: opacity var(--dur-quick) var(--ease-out); }
  .stop.is-active .stop-name { color: var(--accent); }
  .stop.is-active::before {
    background: var(--accent);
    box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 22%, transparent);
  }

  /* 스파인은 페이지의 본체다. main 칼럼(561px)에 가두면
     페이지에서 가장 긴 섹션이 가장 좁아진다.
     R1(zed.dev) 실측: 컨테이너 1120px 과 전체폭이 교대한다. */
  .spine-wrap {
    max-width: 1180px;
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .brief { margin-bottom: var(--space-2); }
  .brief-core { padding: var(--space-4); }
  .brief-label {
    display: block;
    font-size: var(--step-0);
    letter-spacing: var(--tracking-label);
    color: var(--ink-muted);
    margin-bottom: var(--space-2);
  }
  .brief-text {
    margin: 0;
    font-size: var(--step-1);
  }

  /* ---- 스파인 ---------------------------------------------
     선은 장식이 아니라 순서 그 자체다. 정거장의 위치가 번호를 대신한다.
     ---------------------------------------------------------- */
  .spine {
    /* 정거장의 세로 리듬은 여기 한 곳에서 나온다.
       전에는 패딩이 space-6, 마커 위치가 space-4 로 각자 적혀 있어서
       원이 제목보다 24px 위에 떠 있었다. 값을 각자 쓰면 이렇게 어긋난다. */
    --stop-pad: var(--space-5);
    list-style: none;
    margin: 0;
    padding: 0 0 0 var(--space-5);
    position: relative;
  }
  .spine::before {
    content: "";
    position: absolute;
    left: 5px;
    top: var(--stop-pad);
    bottom: var(--stop-pad);
    width: 1px;
    background: var(--line-strong);
  }

  .stop {
    position: relative;
    /* 정거장 간격이 곧 3D 게이트의 스크롤 구간이다.
       space-4(24px)로는 섹션이 1493px 밖에 안 돼 sticky 이동 거리가 593px 뿐이었다.
       섹션이 화면에 보이는 구간(2393px)의 4분의 1이라, 조금만 스크롤해도
       여섯 게이트를 다 지나쳐 버렸다.

       space-7 까지 올렸더니 이번엔 정거장 사이가 224px 로 벌어져
       목록이 흩어져 보였다 — 여섯 개가 한 목록으로 안 읽힌다.
       읽기 간격과 스크롤 구간은 같은 손잡이가 아니다.
       간격은 읽기에 맞추고, 스크롤 구간은 스파인 뒤 여백(.after)에서 번다. */
    padding-block: var(--stop-pad);
    display: grid;
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--space-5);
    align-items: start;
  }
  .stop-head { min-width: 0; }
  .stop + .stop {
    border-top: 1px solid var(--line);
  }

  /* 정거장 표시 — 채워진 원. 숫자가 아니다 */
  .stop::before {
    content: "";
    position: absolute;
    left: calc(var(--space-5) * -1);
    top: calc(var(--stop-pad) + 0.55em);
    width: 11px;
    height: 11px;
    border-radius: var(--radius-sm);
    background: var(--surface);
    border: 3px solid var(--accent);
    box-sizing: border-box;
  }

  .stop-name {
    font-size: var(--step-1);
    margin-bottom: var(--space-2);
  }
  .stop-does {
    color: var(--ink-muted);
    margin: 0;
    max-width: 34ch;
  }

  /* 브리프가 이 단계에서 무엇이 됐는가 — 변화가 순서를 만든다 */
  .stop-becomes {
    margin: 0;
    padding-left: var(--space-3);
    border-left: 2px solid var(--accent);
  }
  @media (prefers-reduced-motion: no-preference) {
    .stop::before {
      transition: transform var(--dur-quick) var(--ease-out);
    }
    .stop:hover::before { transform: scale(1.5); }
    .stop-becomes { transition: border-color var(--dur-quick) var(--ease-out); }
    .stop:hover .stop-becomes { border-color: var(--ink); }
  }

  .stop-becomes.mono {
    font-family: var(--font-mono);
    font-size: var(--mono-size);
    overflow-wrap: anywhere;
  }

  .after {
    margin-top: var(--space-7);
    max-width: var(--measure);
    /* 여기가 스크롤 구간을 버는 자리다. 정거장 간격을 벌리는 대신
       마지막 정거장 뒤에 여운을 둔다 — 스파인이 끝난 뒤라 빈 공간으로 읽히지 않는다 */
    padding-bottom: 62dvh;
  }

  @media (max-width: 780px) {
    .stop {
      grid-template-columns: 1fr;
      gap: var(--space-3);
    }
  }

  /* 스크롤 진입 페이드를 넣었다가 뺐다.
     `animation-range: entry 10% cover 22%` 로는 페이지 하단 요소가 cover 22% 에
     영영 도달하지 못해, 6개 정거장 중 뒤 3개가 opacity 0 으로 잠겼다.
     이펙트를 끄면 보이지만 켜면 사라지는 것은 폴백이 있는 것과 다르다.
     이 섹션은 페이지의 본체다. 콘텐츠를 걸고 하는 연출은 하지 않는다. */

</style>

<script>
  // 게이트를 통과할 때 해당 단계를 켠다. 값이 바뀔 때만 DOM 을 쓴다.
  import { mountGateScene } from "../lib/gate-scene";

  const canvas = document.getElementById("pipeline-canvas") as HTMLCanvasElement | null;
  const section = document.getElementById("pipeline");
  const stops = [...document.querySelectorAll<HTMLElement>("#pipeline .stop")];

  if (canvas && section && stops.length) {
    let last = -1;
    mountGateScene({
      canvas,
      accentVar: "--accent",
      glowVar: "--glow-2",
      // 캔버스는 sticky 라 rect 가 변하지 않는다. 진행도는 섹션에서 읽는다.
      progressFrom: section,
      onGate(index) {
        if (index === last) return;
        last = index;
        stops.forEach((el, i) => el.classList.toggle("is-active", i === index));
      },
    });
  }
</script>
