/* premium.css — 프리미엄 티어 모션 레이어
 *
 * 이 파일의 모든 규칙은 html.tier-premium 아래에만 붙는다.
 * 기본 티어(?tier=premium 없이 열었을 때)는 한 픽셀도 바뀌지 않는다.
 * 같은 사이트를 두 번 보여주고 "이 차이에 값이 붙는다"를 눈으로 만들기 위한 구조다.
 *
 * 원칙 셋
 *  1. 레이아웃을 건드리는 속성은 애니메이션하지 않는다(width·height·margin·top).
 *     transform 과 opacity 만 쓴다. 이 프로젝트가 이미 한 번 데인 함정이다.
 *  2. 모션은 무드에서 나온다. 브루탈리즘과 일러스트가 같은 튕김을 쓰면 안 된다.
 *     data-mood 로 --spring 을 갈아끼운다.
 *  3. JS 가 없어도, 모션을 꺼둔 사용자에게도 내용은 전부 보인다.
 */

/* ── 스프링 이징 ─────────────────────────────────────────────
   cubic-bezier 는 제어점이 4개라 '넘어갔다 되돌아오는' 움직임을 못 만든다.
   감쇠 스프링 x(t)=1-e^(-ζω₀t)[cos(ω_d t)+(ζω₀/ω_d)sin(ω_d t)] 를
   직접 샘플링해 linear() 로 구웠다. JS 도 라이브러리도 쓰지 않는다.
   ──────────────────────────────────────────────────────── */
:root {
  /* MIN·CRP·EDT — 거의 안 튕긴다. 절제된 확신.
     ζ=0.83 · 최대 100.9% · 안정까지 483ms */
  --spring-crisp: linear(0, 0.017, 0.063, 0.128, 0.206, 0.291, 0.379, 0.466, 0.55, 0.629, 0.702, 0.767, 0.825, 0.874, 0.916, 0.95, 0.977, 0.997, 1.012, 1.021, 1.026, 1.028, 1.027, 1.024, 1.02, 1.016, 1.011, 1.007, 1.004, 1.001, 0.999, 0.998, 0.997, 0.996, 0.996, 0.997, 0.997, 0.998, 0.998, 0.999, 0.999, 1, 1, 1);
  --spring-crisp-dur: 483ms;
  /* SFT·NAT·LUX — 살짝 넘어갔다 조용히 자리잡는다.
     ζ=0.74 · 최대 103.0% · 안정까지 550ms */
  --spring-soft: linear(0, 0.014, 0.053, 0.109, 0.178, 0.255, 0.336, 0.418, 0.499, 0.576, 0.649, 0.716, 0.777, 0.831, 0.878, 0.918, 0.952, 0.981, 1.003, 1.021, 1.034, 1.043, 1.048, 1.05, 1.049, 1.047, 1.043, 1.038, 1.033, 1.027, 1.021, 1.016, 1.011, 1.007, 1.003, 1, 0.998, 0.996, 0.995, 0.994, 0.994, 0.996, 0.998, 1);
  --spring-soft-dur: 550ms;
  /* ILL·Y2K·3DM — 두 번 튕긴다. 장난기.
     ζ=0.46 · 최대 119.5% · 안정까지 883ms */
  --spring-bounce: linear(0, 0.047, 0.169, 0.336, 0.523, 0.708, 0.874, 1.011, 1.113, 1.178, 1.207, 1.204, 1.176, 1.13, 1.074, 1.016, 0.964, 0.923, 0.895, 0.882, 0.883, 0.895, 0.916, 0.943, 0.972, 1.001, 1.026, 1.046, 1.06, 1.066, 1.066, 1.061, 1.051, 1.04, 1.027, 1.015, 1.005, 0.997, 0.992, 0.99, 0.991, 0.994, 0.997, 1);
  --spring-bounce-dur: 883ms;
  /* BRU·NEO·GLS — 무겁게 밀려와 묵직하게 멈춘다.
     ζ=0.82 · 최대 101.1% · 안정까지 704ms */
  --spring-heavy: linear(0, 0.016, 0.058, 0.119, 0.192, 0.272, 0.355, 0.438, 0.518, 0.594, 0.665, 0.73, 0.788, 0.839, 0.883, 0.92, 0.951, 0.976, 0.995, 1.01, 1.021, 1.028, 1.032, 1.033, 1.032, 1.03, 1.027, 1.023, 1.019, 1.015, 1.011, 1.007, 1.004, 1.002, 0.999, 0.998, 0.996, 0.995, 0.995, 0.995, 0.996, 0.997, 0.999, 1);
  --spring-heavy-dur: 704ms;
  /* 작은 요소(체크·토글) 전용. 짧고 단단하다.
     ζ=0.76 · 최대 102.7% · 안정까지 358ms */
  --spring-snap: linear(0, 0.016, 0.059, 0.122, 0.198, 0.281, 0.367, 0.452, 0.534, 0.61, 0.681, 0.744, 0.801, 0.85, 0.892, 0.928, 0.957, 0.981, 0.999, 1.013, 1.023, 1.029, 1.032, 1.032, 1.031, 1.028, 1.024, 1.02, 1.015, 1.011, 1.007, 1.004, 1.001, 0.999, 0.997, 0.996, 0.995, 0.995, 0.995, 0.996, 0.997, 0.998, 0.999, 1);
  --spring-snap-dur: 358ms;
}

/* ── 무드가 스프링을 고른다 ──────────────────────────────────
   같은 모션 어휘라도 어느 스프링을 타느냐로 성격이 갈린다.
   이게 없으면 32작품이 "한 라이브러리에서 나온 32개"로 읽힌다. */
html.tier-premium                      { --spring: var(--spring-soft);   --spring-dur: var(--spring-soft-dur); }
html.tier-premium[data-mood="MIN"],
html.tier-premium[data-mood="CRP"],
html.tier-premium[data-mood="EDT"]     { --spring: var(--spring-crisp);  --spring-dur: var(--spring-crisp-dur); }
html.tier-premium[data-mood="SFT"],
html.tier-premium[data-mood="NAT"],
html.tier-premium[data-mood="LUX"]     { --spring: var(--spring-soft);   --spring-dur: var(--spring-soft-dur); }
html.tier-premium[data-mood="ILL"],
html.tier-premium[data-mood="Y2K"],
html.tier-premium[data-mood="3DM"]     { --spring: var(--spring-bounce); --spring-dur: var(--spring-bounce-dur); }
html.tier-premium[data-mood="BRU"],
html.tier-premium[data-mood="NEO"],
html.tier-premium[data-mood="GLS"]     { --spring: var(--spring-heavy);  --spring-dur: var(--spring-heavy-dur); }

/* 튕김의 폭도 무드마다 다르다 — 리프트 거리·눌림 정도의 배율 */
html.tier-premium                      { --pm-lift: 1;   --pm-press: 1; }
html.tier-premium[data-mood="MIN"],
html.tier-premium[data-mood="CRP"]     { --pm-lift: .55; --pm-press: .6; }
html.tier-premium[data-mood="ILL"],
html.tier-premium[data-mood="Y2K"],
html.tier-premium[data-mood="3DM"]     { --pm-lift: 1.5; --pm-press: 1.4; }
html.tier-premium[data-mood="BRU"]     { --pm-lift: .8;  --pm-press: 1.6; }

/* ══ 0. 진입에 대하여 ══════════════════════════════════════
   전체화면 커튼을 넣었다가 걷어냈다.

   들어오자마자 색 화면이 1.2초 덮고 있다 걷히는 건 프리로더다.
   2015년쯤 쓰던 패턴이고 지금은 "아마추어가 생각하는 고급스러움"으로 읽힌다.
   무엇보다 작품을 옮겨다닐 때마다 매번 기다리게 만들어,
   포폴을 훑는 행위 자체를 방해했다.

   대신 내용 위에서 움직인다 — 사진이 열리고 제목이 올라온다.
   기다림 없이, 첫 프레임부터 볼 것이 있다. */

/* ══ 0-B. 첫 화면을 사진이 덮는다 ═══════════════════════════
   실측: 32작품 중 28개가 "왼쪽 글 · 오른쪽 사진" 2단 분할이었다.
   제목은 30개가 정확히 80px, 이미지는 화면의 평균 50%.
   어떤 템플릿에서나 보는 배치이고, 여기에 모션을 얹어봐야 구조는 그대로다.

   프리미엄에서는 사진이 화면을 덮고 글자가 그 위에 얹힌다.
   제목도 화면 크기에 물려 1440px 에서 80px → 130px 가 된다.

   ★ 사진 위 글자는 대비가 생명이다. 이 프로젝트가 이미 한 번 데인 곳이라
     스크림 농도를 고정값으로 두지 않는다 — premium.js 가 제목 색의 밝기를 읽어
     --pm-scrim 을 밝은/어두운 쪽으로 잡아준다. */
@media (min-width: 900px) {
  html.tier-premium .hero {
    grid-template-columns: minmax(0, 1fr);   /* 2단 분할을 푼다 */
    position: relative;
    align-items: end;                        /* 글자를 아래로 내린다 */
  }
  html.tier-premium .hero__media {
    position: absolute;
    inset: 0;
    z-index: 0;
    grid-area: unset;
    /* inset:0 만으로는 모자란다. 원래 폭을 정해둔 작품(width:52%)이 있는데
       left/right/width 가 동시에 걸리면 CSS 는 width 를 살리고 right 를 버린다.
       그래서 사진이 화면의 절반만 덮었다. 폭 지정을 되돌린다. */
    width: auto;
    height: auto;
  }
  html.tier-premium .hero__media .img-slot,
  html.tier-premium .hero__media > figure {
    position: absolute;
    inset: 0;
    aspect-ratio: auto;
    border-radius: 0;
  }
  html.tier-premium .hero__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* 사진과 글자 사이에 농도막. 색은 JS 가 정한다. */
  html.tier-premium .hero::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    /* 제목이 아래쪽에만 있는 게 아니다 — 리드·버튼·스펙이 아래로 이어지면서
       제목은 오히려 화면 중간에 온다. 실측에서 제목이 그라디언트의 투명 구간에
       걸려 대비가 2.7 까지 떨어졌다.
       진한 구간이 어디서 끝나는지는 62% 같은 고정값으로 정하지 않는다 —
       premium.js 가 제목의 실제 위치를 재서 --pm-solid 로 넘긴다. */
    background: linear-gradient(
      to top,
      var(--pm-scrim, rgb(0 0 0 / .62)) 0%,
      var(--pm-scrim, rgb(0 0 0 / .62)) var(--pm-solid, 62%),
      var(--pm-scrim-mid, rgb(0 0 0 / .34)) calc(var(--pm-solid, 62%) + 16%),
      transparent 100%);
  }

  /* 상자를 ch 로 묶으면 안 된다. 60ch 는 본문 글자 기준이라 572px 인데,
     그 안에 121px 제목을 넣으면 세 글자마다 줄이 바뀐다. 실제로 그랬다.
     상자는 풀고, 폭 제한은 각 요소에 그 요소의 글자 크기로 건다. */
  html.tier-premium .hero__text,
  html.tier-premium .hero__body,
  html.tier-premium .hero__grid {
    position: relative;
    z-index: 2;
    max-width: none;
    width: 100%;
    padding-inline: clamp(1.5rem, 5vw, 5rem);
    padding-bottom: clamp(2.5rem, 6vh, 5rem);
    justify-content: flex-end;
    align-items: flex-start;
  }

  /* 바깥 상자만 풀어서는 모자란다. 7작품은 그 안에 한 겹이 더 있어서
     (hero__card / hero__badge / hero__grid) 제목이 414px 상자에 갇혔다.
     안쪽 겹은 사진 위에 얹힌 흰 카드이기도 했다 — 사진을 가리므로 지운다. */
  html.tier-premium .hero:has(.hero__media) .hero__card,
  html.tier-premium .hero:has(.hero__media) .hero__badge {
    max-width: none;
    width: 100%;
    align-self: stretch;
    background: none;
    box-shadow: none;
    border: 0;
    padding: 0;
    backdrop-filter: none;
  }

  /* 제목이 화면에 물린다. 80px 고정은 1440px 화면에서 작다.
     max-width 의 ch 는 제목 자신의 글자 크기로 계산되므로 넉넉하다. */
  html.tier-premium .hero__title,
  html.tier-premium .hero h1 {
    /* 글자 수가 많은 작품은 121px 로 키우면 5줄이 되어 히어로가 화면을 넘친다.
       줄 수를 세어 premium.js 가 --pm-h1 을 낮춰준다(기본값은 여기 값). */
    font-size: var(--pm-h1, clamp(3rem, 8.4vw, 8.5rem));
    line-height: .94;
    letter-spacing: -.03em;
    max-width: 15ch;
    text-wrap: balance;
  }

  /* 스크림은 넓은 면의 평균을 맞출 뿐, 글자 획 하나가 사진의 밝은 점 위에
     정확히 걸리는 것까진 못 막는다. 획 둘레에만 얇게 깔아 그걸 받는다.
     색은 JS 가 스크림 방향에 맞춰 정한다(어두운 막이면 검정, 밝은 막이면 흰색). */
  html.tier-premium .hero:has(.hero__media) .hero__title,
  html.tier-premium .hero:has(.hero__media) h1 {
    text-shadow: 0 1px 2px var(--pm-tsh, rgb(0 0 0 / .42)),
                 0 2px 20px var(--pm-tsh, rgb(0 0 0 / .42));
  }
  html.tier-premium .hero__lead { max-width: 42ch; }
  html.tier-premium .hero__spec { max-width: 56ch; }
}

/* 첫 화면에 사진이 없는 작품(대시보드형)은 건드리지 않는다 */
html.tier-premium .hero:not(:has(.hero__media)) { align-items: stretch; }
html.tier-premium .hero:not(:has(.hero__media))::before { display: none; }

/* ══ 1. 누르는 맛 ═══════════════════════════════════════════
   실물 버튼은 눌리면 들어갔다 튀어나온다. 그 짧은 순간이 "잘 만든 것" 의 신호다. */
html.tier-premium .btn,
html.tier-premium .chip,
html.tier-premium .pill {
  transition: transform var(--spring-dur) var(--spring),
              background var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
  will-change: transform;
}
html.tier-premium .btn:active,
html.tier-premium .chip:active,
html.tier-premium .pill:active {
  transform: scale(calc(1 - .045 * var(--pm-press)));
  transition-duration: 90ms;                  /* 누를 땐 즉각, 놓을 때 튕긴다 */
}

/* ══ 2. 카드가 들리는 방식 ══════════════════════════════════ */
html.tier-premium .card {
  transition: transform var(--spring-dur) var(--spring),
              box-shadow var(--dur-slow) var(--ease-out),
              border-color var(--dur) var(--ease);
}
html.tier-premium .card:hover {
  transform: translateY(calc(-6px * var(--pm-lift)));
}

/* ══ 3. 들어오면서 자리잡기 ═════════════════════════════════
   base.css 의 .reveal 은 페이드+이동이다. 프리미엄은 스프링으로 자리를 잡고,
   형제 요소가 순서대로 따라 들어온다(계단식). */
html.tier-premium.js .reveal {
  transform: translateY(18px) scale(.985);
  transition: opacity var(--spring-dur) var(--ease-out),
              transform var(--spring-dur) var(--spring);
}
html.tier-premium.js .reveal.is-in {
  transform: none;
  transition-delay: var(--pm-delay, 0ms);
}

/* .reveal 을 안 쓰는 작품(대시보드형 등)을 위해 premium.js 가 런타임에 붙인다.
   작품 파일은 건드리지 않는다. */
html.tier-premium.js .pm-reveal {
  opacity: 0;
  transform: translateY(16px) scale(.99);
  transition: opacity var(--spring-dur) var(--ease-out),
              transform var(--spring-dur) var(--spring);
}
html.tier-premium.js .pm-reveal.is-in {
  opacity: 1;
  transform: none;
  transition-delay: var(--pm-delay, 0ms);
}

/* ══ 4. 이미지가 숨을 쉬며 들어온다 ════════════════════════ */
html.tier-premium.js .img-slot { overflow: hidden; }
html.tier-premium.js .img-slot > img {
  transform: scale(1.055);
  transition: transform 900ms var(--spring), opacity var(--dur-slow) var(--ease-out);
}
html.tier-premium.js .img-slot.is-in > img,
html.tier-premium.js .reveal.is-in .img-slot > img { transform: none; }

/* ══ 5. 숫자가 올라간다 ════════════════════════════════════
   premium.js 가 값을 세고, 자릿수가 흔들리지 않게 폭을 고정한다. */
html.tier-premium [data-count] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ══ 6. 스크롤 진행 ════════════════════════════════════════
   문서 상단에 얇은 띠. 긴 페이지에서 "얼마나 남았나"를 알려준다. */
html.tier-premium .pm-progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 2px;
  z-index: 99;
  background: var(--brand);
  transform-origin: 0 50%;
  transform: scaleX(var(--pm-scroll, 0));
  pointer-events: none;
}

/* ══ 7. 링크가 손끝을 따라온다 ═════════════════════════════
   premium.js 가 --pm-mx/--pm-my 를 채운다. 아주 작게 — 크면 장난스러워진다. */
html.tier-premium .pm-magnet {
  transition: transform var(--spring-dur) var(--spring);
}
html.tier-premium .pm-magnet.is-near {
  transform: translate(calc(var(--pm-mx, 0) * 1px), calc(var(--pm-my, 0) * 1px));
  transition-duration: 120ms;
}

/* ══ 8. 첫 화면 제목이 가려진 뒤에서 올라온다 ══════════════
   페이드로 나타나는 것과 "마스크 뒤에서 밀려 올라오는" 것은 인상이 다르다.
   후자는 활자가 물리적으로 존재하는 느낌을 준다. */
html.tier-premium.js .pm-lines > span {
  display: inline-block;
  clip-path: inset(0 0 110% 0);            /* 아래에서 위로 열린다 */
  transform: translateY(.5em);
  transition: clip-path 620ms var(--ease-out),
              transform var(--spring-dur) var(--spring);
  transition-delay: calc(var(--i, 0) * 70ms);   /* 기다림 없이 바로 */
}
html.tier-premium.js .pm-lines.is-in > span {
  clip-path: inset(0 0 -12% 0);
  transform: none;
}

/* ══ 9. 첫 화면 이미지가 열린다 ════════════════════════════
   페이드가 아니라 클립으로 열고, 동시에 아주 느리게 줌아웃한다.
   "사진이 놓인" 게 아니라 "사진이 드러난" 것으로 읽힌다. */
html.tier-premium.js .hero .img-slot,
html.tier-premium.js .hero__media,
html.tier-premium.js [class*="hero"] > .img-slot { overflow: hidden; }

html.tier-premium.js .pm-heroshot {
  clip-path: inset(0 0 42% 0);
  transition: clip-path 820ms var(--ease-out);
}
html.tier-premium.js .pm-heroshot.is-in { clip-path: inset(0 0 0 0); }
html.tier-premium.js .pm-heroshot > img {
  transform: scale(1.055);
  transition: transform 1600ms var(--ease-out);
}
html.tier-premium.js .pm-heroshot.is-in > img { transform: scale(1); }

/* ══ 10. 스크롤에 물린 모션 ════════════════════════════════
   JS 없이 CSS 만으로 스크롤 위치에 맞춰 돈다. 스크롤이 멈추면 모션도 멈춘다 —
   시간 기반 애니메이션과 달리 "내가 조종한다"는 감각이 생긴다.
   미지원 브라우저에서는 이 블록이 통째로 무시되고 기존 리빌만 남는다. */
@supports (animation-timeline: view()) {
  /* 본문 이미지가 스크롤을 따라 아주 천천히 밀려 올라간다 */
  html.tier-premium.js .section .img-slot > img,
  html.tier-premium.js .img-slot.pm-para > img {
    animation: pmPara linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
  @keyframes pmPara {
    from { transform: translateY(3.5%) scale(1.075); }
    to   { transform: translateY(-3.5%) scale(1.075); }
  }

  /* 첫 화면은 스크롤할수록 뒤로 물러난다 */
  html.tier-premium.js .hero {
    animation: pmHeroOut linear both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  @keyframes pmHeroOut {
    from { opacity: 1; transform: scale(1); }
    to   { opacity: .38; transform: scale(.97); }
  }

  /* 섹션 제목이 들어오면서 자간이 좁혀진다 — 아주 미묘하게 */
  html.tier-premium.js .section h2 {
    animation: pmTighten linear both;
    animation-timeline: view();
    animation-range: entry 10% entry 90%;
  }
  @keyframes pmTighten {
    from { letter-spacing: .04em; opacity: .55; }
    to   { letter-spacing: inherit; opacity: 1; }
  }
}

/* ══ 티어 전환 스위치 ══════════════════════════════════════
   대조가 이 기능의 전부인데 오갈 방법이 없으면 아무 소용이 없다.
   두 티어 모두에 뜬다 — 그래서 html.tier-premium 밖에 있는 유일한 규칙이다. */
.pm-switch {
  position: fixed;
  inset-block-end: calc(14px + var(--pm-switch-lift, 0px));
  inset-inline-end: 14px;
  z-index: 250;
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  backdrop-filter: none;
  font-size: 12px;
  letter-spacing: -.01em;
}
.pm-switch a {
  padding: 0 0 2px;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ink-2, #5c6168);
  white-space: nowrap;
  text-decoration: none;
}
.pm-switch a:hover { color: var(--ink, #16181d); }
.pm-switch a[aria-current="page"] {
  background: none;
  color: var(--ink, #16181d);
  font-weight: 500;
  border-bottom: 1px solid currentColor;
}
.pm-switch__tag,
.pm-switch__hint { display: none; }
@media print { .pm-switch { display: none; } }

/* ══ 11. 사진 위에 막이 아니라, 공기가 있다 ═══════════════ */
html.tier-premium .hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: .22;
  mix-blend-mode: overlay;
  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='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
html.tier-premium .hero:not(:has(.hero__media))::after { display: none; }

/* ══ 12. 본문 사진이 가까이 온다 ══════════════════════════ */
html.tier-premium .img-slot {
  transition: transform var(--spring-dur) var(--spring);
}
html.tier-premium .img-slot:hover { transform: translateY(calc(-4px * var(--pm-lift))); }
html.tier-premium .hero .img-slot:hover { transform: none; }
html.tier-premium .img-slot > img {
  transition: transform 1.1s var(--ease-out);
}
html.tier-premium .img-slot:hover > img { transform: scale(1.045); }
html.tier-premium .hero .img-slot:hover > img { transform: none; }

/* ══ 13. 버튼에 빛 한 줄 ══════════════════════════════════ */
html.tier-premium .btn--solid,
html.tier-premium .hero .btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
html.tier-premium .btn--solid::after,
html.tier-premium .hero .btn::after {
  content: "";
  position: absolute;
  inset: 0 auto 0 -40%;
  width: 36%;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / .28), transparent);
  transform: translateX(-120%) skewX(-18deg);
  pointer-events: none;
}
html.tier-premium .btn--solid:hover::after,
html.tier-premium .hero .btn:hover::after {
  transform: translateX(340%) skewX(-18deg);
  transition: transform .7s var(--ease-out);
}

/* ══ 14. 섹션 제목 아래 가는 선 ═══════════════════════════ */
html.tier-premium .section h2,
html.tier-premium section h2.h2 {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0 1px;
  padding-bottom: .18em;
  transition: background-size 700ms var(--ease-out);
}
html.tier-premium .section h2.is-in,
html.tier-premium .section h2.reveal.is-in,
html.tier-premium section h2.h2.is-in {
  background-size: min(4.5rem, 28%) 1px;
}

/* ══ 15. 스크롤 띠를 조금 더 분명히 ═══════════════════════ */
html.tier-premium .pm-progress {
  block-size: 1.5px;
  box-shadow: 0 0 12px color-mix(in oklab, var(--brand) 55%, transparent);
}

/* ══ 16. 헤더가 내려가면 더 진해진다 ══════════════════════ */
html.tier-premium .nav,
html.tier-premium header.nav {
  transition: background .35s var(--ease), box-shadow .35s var(--ease), border-color .35s var(--ease);
}
html.tier-premium.is-scrolled .nav,
html.tier-premium.is-scrolled header.nav {
  background: color-mix(in oklab, var(--surface) 92%, transparent);
  box-shadow: 0 8px 28px rgb(0 0 0 / .08);
}



/* ══ 접근성 ════════════════════════════════════════════════
   모션을 줄여달라는 사용자에게 스프링은 특히 거슬린다. 전부 끈다. */
@media (prefers-reduced-motion: reduce) {
  html.tier-premium *,
  html.tier-premium *::before,
  html.tier-premium *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  html.tier-premium.js .reveal,
  html.tier-premium.js .pm-reveal { opacity: 1; transform: none; }
  html.tier-premium.js .pm-lines > span { clip-path: none; transform: none; }
  html.tier-premium.js .pm-heroshot { clip-path: none; }
  html.tier-premium.js .img-slot > img,
  html.tier-premium.js .pm-heroshot > img { transform: none; animation: none; }
  html.tier-premium.js .hero { animation: none; opacity: 1; transform: none; }
  html.tier-premium.js .section h2 { animation: none; letter-spacing: inherit; opacity: 1; }
  html.tier-premium .pm-progress { display: none; }
  html.tier-premium .hero::after { display: none; }
  html.tier-premium .img-slot:hover,
  html.tier-premium .img-slot:hover > img { transform: none; }
}

/* ══ 8. 조판 ═══════════════════════════════════════════════
   여기까지 프리미엄이 더한 것은 전부 "움직임" 이었다. 첫 화면만 화려하고
   본문은 기본 티어와 한 글자도 다르지 않으니, 스크롤을 내리는 순간
   격차가 사라진다. 격은 움직임이 아니라 활자에서 온다.

   실측(32작품):
     · 한글이 어절 중간에서 끊기는 요소 500개 — li 133, span 304, dd·td·summary…
     · 본문 한 줄이 최대 112자 — 45자를 넘으면 눈이 줄을 놓친다
     · 숫자 330개 중 137개만 자리를 맞춰 세로로 흔들린다 */

/* ── 한글 줄바꿈 ─────────────────────────────────────────
   base.css 는 p 와 h1~h6 에만 keep-all 을 건다. 그래서 목록·표·정의목록의
   한글이 "프리미엄 웹사이트 제" / "작" 처럼 끊겼다.
   word-break 는 상속되는 속성이다 — 뿌리에 한 번 걸면 500개가 한꺼번에 풀린다. */
html.tier-premium body {
  word-break: keep-all;      /* 어절 단위로만 끊는다 */
  line-break: strict;        /* 여는/닫는 괄호·문장부호를 줄머리에 두지 않는다 */
  overflow-wrap: break-word; /* 단, 끊을 데 없는 긴 URL 은 넘치게 두지 않는다 */
}
/* 코드·주소는 어절 개념이 없다 — 원래대로.
   .mono 를 여기 넣었다가 되뺐다. 이 프로젝트에서 .mono 는 코드가 아니라
   "2026.03 · 브랜디드 브이로그" 같은 메타 문구에 쓰는 등폭 스타일이라,
   예외로 두니 그 한글이 다시 어절 중간에서 끊겼다. */
html.tier-premium :is(code, pre, kbd, samp) {
  word-break: normal;
  overflow-wrap: anywhere;
}

/* ── 문단 마무리 ─────────────────────────────────────────
   마지막 줄에 한 단어만 남는 것(과부 줄)을 브라우저가 알아서 피한다.
   제목은 balance 가 낫고 본문은 pretty 가 낫다 — 목적이 다르다. */
html.tier-premium :is(p, li, dd, figcaption, blockquote) { text-wrap: pretty; }
html.tier-premium :is(h2, h3, h4, .kicker) { text-wrap: balance; }

/* ── 숫자 ────────────────────────────────────────────────
   비율폭 숫자는 1 이 좁고 0 이 넓어서, 세로로 쌓인 통계의 자릿수가 어긋난다.
   세는 애니메이션에서는 숫자가 바뀔 때마다 폭이 흔들려 더 티가 난다.

   b 와 strong 도 넣는다. "09:00" 같은 시각을 <b> 하나로 쓰는 곳이 있어서
   세로로 쌓인 시간표의 자릿수가 어긋났다. 숫자가 없는 <b> 에는 아무 일도 안 일어난다. */
html.tier-premium :is(dd, td, th, time, b, strong, .stat, .stat__n, .num, .kpi, [data-count]),
html.tier-premium :is(.stat, .stats, .kpi) :is(span, b, strong) {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

@media (min-width: 900px) {
  /* ── 활자 급수 ──────────────────────────────────────────
     작품마다 제목 단계를 다르게 골라 뒀다(40px / 56px). 크기를 직접 박으면
     그 차이가 사라져 32작품이 한 틀에서 찍힌 것처럼 보인다.
     토큰을 올려 각 작품의 상대 관계를 그대로 둔 채 한 급씩만 밀어 올린다. */
  html.tier-premium {
    --fs-2xl: clamp(2.0625rem, 1.597rem + 1.982vw, 2.75rem);    /* 40 → 44px */
    --fs-3xl: clamp(2.475rem,  1.508rem + 4.125vw, 3.9375rem);  /* 56 → 63px */
    /* 여백이 곧 격이다. 편집물은 글자를 키우기 전에 먼저 자리를 비운다. */
    --section-y: clamp(5rem, 2.973rem + 8.689vw, 10.5rem);      /* 128 → 168px */
  }

  /* 큰 글자일수록 자간을 좁혀야 한 덩어리로 읽힌다(광학 보정).
     같은 -0.025em 도 16px 에서는 적당하고 63px 에서는 헐겁다. */
  html.tier-premium .section :is(h2, h3) { letter-spacing: -.032em; }
}
