/* ============================================================
   designpaca 소개 페이지 — 기반 스타일
   토큰은 tokens.css 에만 있다. 이 파일에 새 값을 만들지 않는다.
   ============================================================ */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--surface);
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: var(--leading-normal);
  letter-spacing: var(--tracking-body);
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

[lang="ko"] body,
[lang="ko"] p,
[lang="ko"] li,
[lang="ko"] dt,
[lang="ko"] dd,
[lang="ko"] h1,
[lang="ko"] h2,
[lang="ko"] h3 {
  word-break: keep-all;
  overflow-wrap: break-word;
}

/* ---- 대기광 -------------------------------------------------
   유리는 뒤에 무언가 있어야 유리로 보인다. 페이지 전체에 깔리는
   고정 광원 두 개가 그 역할을 한다. fixed 라 스크롤 리페인트가 없다. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(58vw 46vw at 14% -6%,
      color-mix(in oklab, var(--accent) 26%, transparent), transparent 68%),
    radial-gradient(52vw 44vw at 92% 8%,
      color-mix(in oklab, var(--glow-2) 20%, transparent), transparent 66%),
    radial-gradient(70vw 60vw at 62% 108%,
      color-mix(in oklab, var(--accent) 14%, transparent), transparent 70%);
}

/* ---- 필름 그레인 ------------------------------------------- */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay;
}

/* ---- 스크롤 ------------------------------------------------
   기본 스크롤바는 OS 색이라 어두운 페이지 옆에 밝은 막대가 선다.
   표준 속성으로 먼저 칠하고, 아직 지원이 없는 곳은 -webkit- 으로 받는다. */
html {
  scrollbar-color: var(--line-strong) transparent;
  scrollbar-width: thin;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--radius-sm);
  border: 2px solid var(--surface);
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-muted); }

/* 고정 헤더 높이만큼 앵커 목적지를 내린다. 없으면 점프한 섹션의
   제목이 헤더 뒤로 들어간다. */
:target,
section[id] { scroll-margin-top: calc(var(--header-h) + var(--space-4)); }

/* ---- 컨트롤 ------------------------------------------------
   버튼처럼 생긴 것은 전부 .btn 을 쓴다. 높이·패딩·웨이트를
   컴포넌트마다 다시 정하면 그때부터 SSOT 가 아니다. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  padding-inline: var(--control-pad-x);
  border-radius: var(--radius-sm);
  font-size: var(--step-0);
  font-weight: var(--weight-medium);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid transparent;
  transition:
    background-color var(--dur-instant) var(--ease-out),
    color var(--dur-instant) var(--ease-out),
    transform var(--dur-instant) var(--ease-out);
}
.btn:active { transform: translateY(1px); }
.btn-sm { min-height: var(--control-h-sm); }

/* 채운 버튼 — 페이지에 하나뿐인 주 행동에만 */
.btn-solid {
  background: var(--accent);
  color: var(--accent-ink);
}
.btn-solid:hover { background: color-mix(in oklab, var(--accent) 86%, var(--ink)); color: var(--accent-ink); }

/* 테두리 버튼 — 나머지 전부 */
.btn-quiet {
  background: var(--glass-tint-2);
  border-color: var(--glass-edge);
  color: var(--ink);
}
.btn-quiet:hover { background: var(--glass-tint); color: var(--ink); }

/* ---- 그리드 ------------------------------------------------ */
.page {
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter), 1fr)
    [wide-start] minmax(0, var(--wide-pad))
    [main-start] minmax(0, var(--measure)) [main-end]
    minmax(0, var(--wide-pad)) [wide-end]
    minmax(var(--gutter), 1fr) [full-end];
}
.page > * { grid-column: main; }
.page > .wide { grid-column: wide; }
.page > .full { grid-column: full; }

.section {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: full;
  padding-block: var(--space-7);
  position: relative;
}
.section > * { grid-column: main; }
.section > .wide { grid-column: wide; }
.section > .full { grid-column: full; }

.page > *,
.section > * { min-width: 0; }

/* ---- 타이포 ------------------------------------------------ */
h1, h2, h3 {
  margin: 0;
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  font-weight: var(--weight-strong);
  text-wrap: balance;
}
h1 { font-size: var(--display); }
h2 { font-size: var(--step-3); }

/* 섹션 제목만 본문 정렬선 왼쪽으로 나간다. 같은 양을 페이지 전체에서 반복한다 */
@media (min-width: 1100px) {
  .section > h2,
  .section > .eyebrow,
  .section > .head { margin-left: calc(var(--space-6) * -1); }
}
h3 { font-size: var(--step-1); }


p { margin: 0 0 var(--space-3); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

/* 제목 다음에 오는 것은 반드시 떨어진다.
   h2 는 margin: 0 이고 p 도 margin-top: 0 이라 제목과 본문이 그대로 붙었다.
   섹션마다 0 이거나 24px 로 갈려 있었다 — 값을 각자 붙이면 이렇게 된다.

   `:where()` 로 감싸면 특이도가 0 이라 위의 `p` 규칙에 진다. 실제로 한 번 그렇게
   써서 아무 효과가 없었다. 특이도를 갖추고, `p` 뒤에 둔다.

   제목을 `.head` 로 감싼 섹션에서는 인접 형제가 끊긴다 — 수치 섹션이 그래서
   혼자 0px 이었다. 제목 묶음도 같은 규칙에 넣는다. */
h1 + *,
h2 + *,
h3 + *,
.head + * { margin-top: var(--space-4); }

.lead { font-size: var(--step-1); color: var(--ink-muted); }

.eyebrow {
  font-size: var(--step-0);
  letter-spacing: var(--tracking-label);
  color: var(--accent);
  font-weight: var(--weight-medium);
  margin: 0 0 var(--space-3);
}

code, kbd, samp, pre {
  font-family: var(--font-mono);
  font-size: var(--mono-size);
}

a {
  color: inherit;
  text-underline-offset: 0.2em;
  text-decoration-thickness: from-font;
  transition: color var(--dur-instant) var(--ease-out);
}
a:hover { color: var(--accent); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* ============================================================
   유리
   backdrop-filter 만 쓰면 흐린 유리에서 끝난다. 두께와 굴절이
   있어야 유리로 읽힌다. 그래서 세 겹을 쌓는다.
     1) 배경 흐림 + 채도    (backdrop-filter)
     2) 가장자리 빛         (border + inset shadow)
     3) 위쪽 반사           (::before 그라디언트)
   굴절은 지원되는 브라우저에서만 backdrop-filter 에 SVG 를 얹는다.
   ============================================================ */
.glass {
  position: relative;
  isolation: isolate;
  background: var(--glass-tint);
  border: 1px solid var(--glass-edge);
  border-radius: var(--radius-frame);
  box-shadow: var(--glass-hi), var(--glass-bloom), var(--shadow-2);
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-sat));
}
/* 위쪽 모서리에 닿는 빛. 유리판의 두께가 여기서 보인다 */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: var(--glass-sheen);
}
/* backdrop-filter 에 SVG 굴절을 얹는 것은 Chrome 에서 실제로 동작한다.
   한때 "Chrome 이 선언을 버린다" 고 적어뒀지만 재측정에서 틀린 것으로 드러났다 —
   computed 값은 url(#id) 로 유지되고 배경도 굴절된다.
   여기에 아직 얹지 않은 이유는 다른 것이다: Chrome 전용이고, feImage 크기를
   요소 크기에 맞춰 리사이즈마다 다시 만들어야 한다. 헤더처럼 크기가 변하는
   요소에는 비용이 맞지 않는다. 자세한 기법은 references/svg-filters.md. */

/* ---- 테두리를 도는 빛 --------------------------------------
   `conic-gradient` 의 각도를 `@property` 로 애니메이션하면 매 프레임 배경을
   다시 칠한다 — 컴포지터에서 못 돌고 메인 스레드가 페인트한다(하드 게이트 #8).

   대신 **정사각형 원뿔 레이어를 마스크 안에서 회전**시킨다. 움직이는 것은
   `rotate` 뿐이라 GPU 에서 돈다. 정사각형이라 회전해도 밝기가 균일하고,
   요소보다 크게(200%) 잡아 납작한 헤더에서도 모서리까지 닿는다. */
.rim {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
  pointer-events: none;
  padding: 1px;
  -webkit-mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  mask-composite: exclude;
}
.rim::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  aspect-ratio: 1;
  translate: -50% -50%;
  background: conic-gradient(
    from 0deg,
    transparent 0 58%,
    color-mix(in oklab, var(--accent) 70%, white) 74%,
    transparent 86%
  );
  opacity: 0.55;
}
@media (prefers-reduced-motion: no-preference) {
  .rim::before {
    animation: rim-spin 11s linear infinite;
  }
}
@keyframes rim-spin {
  to { rotate: 360deg; }
}

/* 가장자리를 도는 빛. 유리판 테두리의 굴절을 흉내낸다 */
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  padding: 1px;
  background: var(--glass-rim);
  /* 아래 black 은 색이 아니라 마스크의 알파다. 팔레트와 무관하다 */
  -webkit-mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(black 0 0) content-box,
    linear-gradient(black 0 0);
  mask-composite: exclude;
}

/* 투명도를 줄여달라는 요청에 불투명한 판을 깔면 그냥 상자가 된다.
   배경에 가까운 톤으로 낮추고 형태는 테두리와 그림자가 유지한다. */
@media (prefers-reduced-transparency: reduce) {
  .glass {
    background: color-mix(in oklab, var(--surface) 84%, var(--ink));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* 떠 있는 헤더는 배경과 더 가깝게. 여기 판이 깔리면 즉시 눈에 띈다 */
  .site-header .glass {
    background: color-mix(in oklab, var(--surface) 93%, var(--ink));
  }
}
@supports not (backdrop-filter: blur(1px)) {
  .glass { background: var(--surface-raised); }
}

/* 유리 위에 얹는 두 번째 판. 안쪽으로 갈수록 밝아진다 */
.glass-2 { background: var(--glass-tint-2); }

/* 액자. 이중 베젤을 버렸다 — 껍데기와 속의 radius 가 서로 달라
   그것만으로 형태 어휘가 두 종 늘어난다(하드 게이트 #3).
   그리고 안이 불투명한 이미지면 backdrop-filter 는 아무것도 하지 않는다.
   유리로 읽히게 하는 것은 테두리 광과 위쪽 반사지 흐림이 아니다. */
.frame {
  position: relative;
  background: var(--surface-sunken);
  border: 1px solid var(--glass-edge);
  border-radius: var(--radius-frame);
  box-shadow: var(--glass-hi), var(--core-hi), var(--shadow-2);
  overflow: clip;
}
/* 이미지를 담는 액자만. 인라인 여백을 지운다 */
.frame > :is(img, picture, canvas) { display: block; width: 100%; height: auto; }

/* ---- 진입 연출 ----------------------------------------------
   opacity 로 콘텐츠를 숨기지 않는다.

   이 페이지에서 두 번 데였다. 처음엔 scroll-driven animation 의
   범위 끝에 하단 요소가 도달하지 못해 6단계 중 3개가 영영 숨었고,
   두 번째는 IntersectionObserver 가 부드러운 스크롤 중간 지점을
   건너뛰어 50개 중 49개가 숨은 채로 남았다.

   그래서 규칙을 바꿨다. 움직이는 것은 위치뿐이다. 최악의 경우에도
   요소가 14px 아래에 있을 뿐이고 글자는 항상 읽힌다. */
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    transform: translateY(14px);
    transition: transform var(--dur-slow) var(--ease-soft);
    transition-delay: var(--reveal-delay, 0ms);
  }
  .reveal.is-in { transform: none; }
}

/* ---- 상호작용 모션 ------------------------------------------
   모션은 이유가 있어야 붙인다. 여기 있는 것은 전부 피드백이다.
   "지금 이 카드를 보고 있다", "이 버튼은 눌린다"를 말한다.
   transform 과 opacity 만 움직인다(하드 게이트 8). */
@media (prefers-reduced-motion: no-preference) {
  /* 유리 카드는 커서 아래에서 아주 조금 떠오른다 */
  .lift {
    transition:
      transform var(--dur-quick) var(--ease-out),
      box-shadow var(--dur-quick) var(--ease-out);
  }
  .lift:hover {
    transform: translate3d(0, -3px, 0);
    box-shadow: var(--glass-hi), var(--shadow-3);
  }

  /* 링크 밑줄이 커서 쪽에서 자란다 */
  .link-grow {
    position: relative;
    text-decoration: none;
  }
  .link-grow::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -3px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-quick) var(--ease-out);
  }
  .link-grow:hover::after { transform: scaleX(1); }

  /* 쇼케이스 판은 들여다보면 조금 다가온다 */
  .zoom-in { transition: transform var(--dur-normal) var(--ease-out); }
  .zoom-in:hover { transform: scale(1.012); }
}

/* ---- 패럴랙스 -----------------------------------------------
   scroll-driven animation 으로만 만든다. scroll 리스너는 쓰지 않는다.
   움직이는 것은 transform 뿐이라 지원되지 않는 브라우저에서는
   요소가 제자리에 그대로 있다. 콘텐츠가 사라질 수 없다. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .par-slow,
    .par-fast,
    .par-deep,
    .par-lag {
      animation: par linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
      will-change: transform;
    }
    .par-slow  { --par-from: 26px;  --par-to: -26px; }
    .par-fast  { --par-from: 58px;  --par-to: -58px; }
    .par-deep  { --par-from: 92px;  --par-to: -92px; }
    /* 짝이 되는 요소보다 조금 늦게 따라온다.
       옆의 것은 고정이고 이것만 움직여야 '저것 때문에 이것'으로 읽힌다. */
    .par-lag   { --par-from: var(--par-lag, 14px); --par-to: calc(var(--par-lag, 14px) * -1); }
    @keyframes par {
      from { transform: translate3d(0, var(--par-from), 0); }
      to   { transform: translate3d(0, var(--par-to), 0); }
    }
  }

  /* 히어로는 페이지 맨 위라 view() 의 entry 구간이 이미 지나 있다.
     그래서 문서 스크롤을 직접 타임라인으로 쓴다 — 시작점이 확정된다.

     글자는 올라가고 배경은 내려간다. 서로 반대로 움직여야 시차가 보인다.
     같은 방향으로 속도만 다르게 하면 그냥 느린 스크롤로 읽힌다. */
  @supports (animation-timeline: scroll()) {
    .hero-grid,
    .hero-bg {
      animation: linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 92vh;
      will-change: transform;
    }
    .hero-grid { animation-name: hero-rise; }
    .hero-bg   { animation-name: hero-sink; }

    @keyframes hero-rise {
      from { transform: translate3d(0, 0, 0); }
      to   { transform: translate3d(0, -5.5rem, 0); }
    }
    @keyframes hero-sink {
      from { transform: translate3d(0, 0, 0); }
      to   { transform: translate3d(0, 3.5rem, 0); }
    }
  }
}

/* ---- 배경 레이어 --------------------------------------------
   패럴랙스는 깊이 차가 보여야 성립한다. 배경이 단색이면 앞의 것이
   몇 px 어긋나 봐야 아무도 못 느낀다 — 기준면이 없기 때문이다.

   기준면은 `BgPhoto` 컴포넌트가 만든다.

   CSS 로 그린 격자를 한동안 같이 깔았는데 걷어냈다. 사진이 들어온 뒤로는
   배경이 두 겹이 되어 서로 경쟁했고, 격자는 배경이라기보다 표로 읽혔다.
   배경도 한 섹션에 하나여야 한다. */
.has-bg {
  position: relative;
  isolation: isolate;
  /* `clip-path: inset(0)` 는 두 가지를 동시에 한다.
       1) 배경이 섹션 밖으로 넘치지 않게 자른다
       2) `position: fixed` 자식의 **컨테이닝 블록**이 된다

     2번이 핵심이다. 그래서 배경이 뷰포트에 고정되면서도 이 섹션 안에서만 보인다 —
     콘텐츠만 그 위를 지나가고 배경은 원경처럼 멈춰 있다.

     `background-attachment: fixed` 를 쓰지 않은 이유는 iOS Safari 가 GPU 메모리
     때문에 그것을 throttle 해서 스크롤 중 배경이 튀기 때문이다.

     주의: 여기에 `transform`·`filter`·`mask` 를 추가하면 안 된다. 그것들도
     컨테이닝 블록을 만들지만, 그 순간 fixed 가 섹션 기준이 되어 고정이 사라진다. */
  clip-path: inset(0);
}

/* ---- 유틸 -------------------------------------------------- */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.muted { color: var(--ink-muted); }
.mono { font-family: var(--font-mono); font-size: var(--mono-size); }
.nums { font-variant-numeric: tabular-nums; }
.table-scroll { overflow-x: auto; min-width: 0; }
