/* src/nnt/css/style.css */

/* 폰트(Pretendard dynamic subset)는 index.html <head>의 <link>로 건다.
   여기서 @import 하면 이 파일을 받은 뒤에야 발견돼 임계 경로가 한 단계
   길어진다. jsDelivr의 gh/orioncactus/pretendard 경로는 저장소가 50MB
   제한을 넘어 403(영구)이고, npm 원본은 웨이트당 ~766KB라 dynamic subset을
   쓴다 — 자체 호스팅/서브셋은 런칭 전 체크리스트 항목이다. */

:root {
  /* ── 팔레트: 확정 시 이 블록만 교체 ── */
  --bg:       #0B0D0F;
  /* --bg 의 RGB 채널. 히어로 스크림이 rgba(var(--bg-rgb), α) 로 쓴다 —
     반드시 --bg 와 함께 바꿀 것. 왜 color-mix(in srgb, var(--bg) α%, transparent)
     가 아닌가: 그 형태로 쓴 판을 계측했더니 모바일 Lighthouse 가 92 → 79 로
     떨어졌다(같은 그림, 같은 겹 수, rgba 로만 바꾸니 92 복귀). :root 한 곳
     원칙은 이 토큰으로 지키고 페인트는 빠른 길로 간다. */
  --bg-rgb:   11, 13, 15;
  --surface:  #14181C;
  --fg:       #F2F4F5;
  --muted:    #8B959C;
  --accent:   #3DDC97;
  --accent-2: #7C5CFF;
  --line:     #232A30;
  --cta-fg:     #06231A;
  --accent-glow: color-mix(in srgb, var(--accent) 25%, transparent);

  /* 제품컷 스튜디오 배경(#F3F3F3 실측)과 맞춘 카드 상단 배경.
     제품명/설명 텍스트는 이 위가 아니라 카드 하단의 --surface 영역에 둔다
     (--fg on --card-bg 대비 1.01:1로 사실상 안 보임 — 절대 텍스트를 얹지 말 것). */
  --card-bg:  #F3F3F3;

  --maxw: 1140px;
  --pad-block: clamp(72px, 10vw, 140px);
  --pad-inline: clamp(20px, 5vw, 40px);

  --fs-hero:  clamp(2.5rem, 7vw, 5rem);
  --fs-h2:    clamp(1.75rem, 4vw, 2.75rem);
  --fs-body:  clamp(1rem, 1.6vw, 1.125rem);

  /* 헤더가 전 페이지 공통으로 fixed 라(아래 .site-header 참고) 고정 높이 하나로
     충분하다 — scroll-padding-top 과 서브페이지 body 상단 여백 둘 다 이 값을 쓴다. */
  --header-h: 52px;
}

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

/* 헤더가 붙박이가 됐으므로 앵커로 점프할 때 제목이 헤더 밑에 깔린다.
   scroll-padding-top 이 그만큼 미리 띄운다(--header-h + 여유 8px). */
html { scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 8px); }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.7;
  word-break: keep-all;      /* 한글 단어 중간 줄바꿈 방지 */
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* ── 레이아웃 유틸 ── */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--pad-inline); }
.section { padding-block: var(--pad-block); }
.section + .section { border-top: 1px solid var(--line); }

.eyebrow {
  font-size: .8125rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); margin: 0 0 12px;
}
h2 { font-size: var(--fs-h2); line-height: 1.25; margin: 0 0 20px; font-weight: 700; }
p  { margin: 0 0 16px; color: var(--muted); }

/* ── CTA (자사몰 유도) ──
   애플 실측(11/21 패딩·980px radius·hover 무모션)을 반영: 그림자·이동 없이
   밝기만 살짝 바뀐다. */
.cta {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; border-radius: 999px;
  background: var(--accent); color: var(--cta-fg);
  font-weight: 600; text-decoration: none;
  transition: filter .2s ease;
}
.cta:hover { filter: brightness(1.08); }
.cta:focus-visible { outline: 3px solid var(--accent-2); outline-offset: 3px; }

/* 보조 CTA는 버튼 상자를 아예 안 만든다 — 애플식 보조 액션은 테두리 필이
   아니라 그냥 텍스트 링크 + 화살표다. 색은 --fg 그대로 둔다: 히어로에서는
   재생 중인 영상 위에 얹히는데, 그 대비값이 checks/hero-contrast.mjs 로
   실측·문서화돼 있어(logo/navLink 표) --accent(휘도 .54)로 바꾸면 어두운
   프레임에서 --fg(휘도 .90)보다 대비가 나빠질 수 있다 — 검증된 색을 지킨다. */
.cta--ghost {
  background: none; border: none; padding: 10px 4px;
  color: var(--fg); font-weight: 500;
  transition: color .2s ease;
}
.cta--ghost::after { content: '→'; }
.cta--ghost:hover { color: var(--accent); }

/* ── 사이트 헤더 ─────────────────────────────────────────────────────────────
   빌드 파이프라인이 없으므로 헤더 **마크업**은 모든 페이지에 손으로 복제된다.
   그래서 페이지별 차이를 마크업에 두지 않는다 — 유일한 구분자는 `<body class="is-home">`
   하나이고, 헤더 블록 자체는 6개 페이지에서 글자까지 같다(스모크 스위트가 문자열
   완전 일치로 지킨다). 링크가 전부 루트 절대경로인 것도 같은 이유다.

   홈: 풀뷰포트 히어로 위에 겹친다(absolute + 상단 스크림). 서브페이지: 문서 흐름
   안의 단색 바. */
.skip-link {
  position: absolute; left: 16px; top: 8px; z-index: 30;
  padding: 8px 16px; border-radius: 999px;
  background: var(--accent); color: var(--cta-fg);
  font-weight: 700; text-decoration: none;
  transform: translateY(-200%);   /* 화면 밖 — 포커스될 때만 내려온다 */
}
.skip-link:focus-visible { transform: none; outline: 3px solid var(--accent-2); outline-offset: 3px; }

/* 전 페이지 공통 fixed + 유리질감(애플 실측: blur(20px) saturate(1.8), 테두리 없음).
   전엔 홈만 fixed(히어로 위에 겹침)·서브페이지는 sticky+불투명 바로 갈렸는데,
   그 이원화 자체를 없앤다 — 항상 같은 재질로 뜬다. fixed 라 히어로의 100svh
   계산에 끼어들지 않으므로 .hero 의 높이·패딩·스크림은 안 건드린다. 서브페이지는
   흐름에서 빠진 만큼을 body 상단 패딩(아래 body:not(.is-home))으로 보충한다. */
.site-header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 20;
  height: var(--header-h);
  background: rgba(var(--bg-rgb), .72);
  backdrop-filter: blur(20px) saturate(1.8);
  -webkit-backdrop-filter: blur(20px) saturate(1.8);
  border-bottom: none;
}
/* 히어로 글자용 국소 가독성(영상이 밝아지는 프레임 대비) — 유리질감 배경만으론
   보장이 안 돼 그대로 남긴다. 이 값을 바꾸면 `node checks/hero-contrast.mjs` 로
   logo/navLink 대비를 다시 잴 것(그 스크립트가 두 항목을 함께 잰다).
   실측(고정+블러 헤더 적용 후, nnt, 기준 4.5:1): 전체 최악 6.03:1(360×740 title).
   logo 16.72~17.75:1, navLink 12.19~16.69:1, ctaGhost 14.60~17.15:1 — 전부
   이전(불투명 스크림) 대비 같거나 더 여유 있다(블러가 밝은 프레임을 눌러줌). */
.is-home .site-header a, .is-home .site-header button {
  text-shadow: 0 1px 2px rgba(var(--bg-rgb), .92), 0 2px 14px rgba(var(--bg-rgb), .70);
}
/* 서브페이지는 헤더가 흐름 밖(fixed)이라 그만큼을 상단 패딩으로 되돌려준다.
   홈은 히어로가 항상 100svh 를 채워서 헤더 밑에 깔리는 게 의도된 그림이라 제외. */
body:not(.is-home) { padding-top: var(--header-h); }

/* 1fr auto 1fr — 가운데 트랙이 뷰포트 기준 중앙이라 메뉴가 진짜로 가운데 온다
   (space-between 은 로고 폭만큼 밀린다). 모바일에서는 가운데 트랙이 0이 된다. */
.site-header__inner {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 12px; height: 100%;
}
.site-logo {
  grid-column: 1; justify-self: start;
  font-weight: 700; font-size: 1rem; text-decoration: none; white-space: nowrap;
}
.site-logo span { color: var(--accent); }

/* 햄버거. DOM 순서는 로고 → 토글 → 메뉴라 탭 순서가 자연스럽고,
   화면 배치는 grid-column 이 잡는다(토글 오른쪽, 메뉴 가운데). */
.nav-toggle {
  grid-column: 3; justify-self: end;
  width: 44px; height: 44px; display: inline-grid; place-items: center;
  background: none; border: 1px solid var(--line); border-radius: 12px;
  color: inherit; cursor: pointer;
}
.nav-toggle__bars { position: relative; }
.nav-toggle__bars, .nav-toggle__bars::before, .nav-toggle__bars::after {
  content: ''; display: block; width: 18px; height: 2px;
  background: currentColor; border-radius: 2px;
}
.nav-toggle__bars::before { position: absolute; top: -6px; }
.nav-toggle__bars::after  { position: absolute; top: 6px; }

/* 모바일 우선: 메뉴는 헤더 아래로 떨어지는 패널이다. 여는 것은 CSS 가 하고
   (aria-expanded 선택자), JS 는 그 속성만 뒤집는다 — 상태가 두 곳에 생기지 않는다. */
.site-nav {
  grid-column: 2;
  position: absolute; top: 100%; left: 0; right: 0;
  display: none;
  background: var(--surface); border-bottom: 1px solid var(--line);
  padding: 8px var(--pad-inline) 16px;
}
.nav-toggle[aria-expanded="true"] + .site-nav { display: block; }

.site-nav__list { list-style: none; margin: 0; padding: 0; }
.site-nav a {
  display: block; width: 100%; text-align: left;
  padding: 10px 14px; font-size: .9375rem; font-weight: 600; letter-spacing: .04em;
  color: var(--fg); text-decoration: none;
  transition: color .32s cubic-bezier(.4, 0, .6, 1);
}
.site-nav a:hover { color: var(--accent); }
.site-nav a:focus-visible, .nav-toggle:focus-visible {
  outline: 3px solid var(--accent-2); outline-offset: 2px;
}

/* 768px 부터 가로 메뉴. 900 이었을 때는 태블릿 가로(768~899)가 화면이 넓은데도
   햄버거였다. 6항목이 nowrap 으로 768 에 들어간다. */
@media (min-width: 768px) {
  .nav-toggle { display: none; }
  .site-nav {
    position: static; display: block;
    background: none; border: 0; padding: 0;
  }
  .site-nav__list { display: flex; align-items: center; gap: clamp(10px, 2vw, 22px); }
  /* 데스크톱은 애플 실측(12px/500, 자간 없음)을 그대로 쓴다 — 모바일 드롭다운
     항목(위 기본값, 15px/600)은 터치 타깃이라 그대로 크게 둔다. */
  .site-nav a {
    width: auto; white-space: nowrap;
    padding: 8px 2px; font-size: .75rem; font-weight: 500; letter-spacing: normal;
  }
}

/* ── 히어로 ── */
.hero {
  /* 풀뷰포트. svh(작은 뷰포트) 를 쓰는 이유: 100vh 는 모바일에서 주소창이
     접힐 때의 "큰 뷰포트"라 스크롤하는 순간 히어로가 늘었다 줄었다 한다.
     svh 는 크롬이 펼쳐진 상태 기준이라 그 점프가 없다. 짧은 가로 창(랜드스케이프
     폰 ~380px)에서 카피가 뭉개지지 않도록 560px 바닥을 깐다.
     첫 줄은 svh 미지원 브라우저용 폴백이다 — max()·svh 를 못 읽으면 선언 전체가
     무효가 되므로 앞줄이 살아남는다. */
  min-height: 100vh;
  min-height: max(560px, 100svh);
  /* 카피를 아래쪽에 앉힌다(레퍼런스: 헤드라인이 뷰포트 45% 지점). 아래 패딩이
     곧 "카피 블록 밑단과 화면 바닥 사이"라, 카피가 길어지면 위로 자란다. */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-block: clamp(72px, 10vw, 120px) clamp(104px, 15vh, 168px);
  position: relative;      /* 배경 영상 레이어의 기준 */
  isolation: isolate;      /* 음수 z-index가 body 배경 뒤로 빠지지 않게 */
  /* overflow:hidden 을 두지 않는다. 영상·스크림은 .hero를 컨테이닝 블록으로
     하는 absolute inset:0 라 애초에 섹션 밖으로 새지 못하고, 이 규칙은 실제로는
     제목·CTA·3D가 넘치는 것을 가려 360px 가로 스크롤 검사를 무력화한다. */
}

/* 배경 영상이 없는 사이트의 히어로. 브랜드 그라디언트만으로 화면을 채운다.
   왜 사진을 깔지 않는가 — 레딜 사진은 폰 촬영(최대 794px)이라 풀뷰포트로 늘리면
   1920 화면에서 2.4배 업스케일된다. 그리고 이 방식은 이미지 요청이 0건이라 LCP 가
   h1 텍스트로 남는다(엔엔티가 크로스 오리진 영상 히어로에서 얻은 것과 같은 결과).
   색은 사이트 토큰에서 온다 — 없으면 transparent 로 접혀 --bg 단색이 된다.
   color-mix 를 쓰지 않는 이유는 위 :root 주석과 같다(모바일 Lighthouse 92 → 79 실측). */
.hero--flat {
  background:
    radial-gradient(120% 80% at 12% 8%, var(--hero-tint-1, transparent) 0%, transparent 60%),
    radial-gradient(90% 70% at 92% 96%, var(--hero-tint-2, transparent) 0%, transparent 62%),
    var(--bg);
}

/* 레이어: 영상(-2) → 스크림(-1) → 카피(기본) */
/* 커버 크롭용 클리핑 박스. overflow:hidden 은 여기까지만 — .hero 에 걸면
   제목·CTA 의 오버플로까지 가려 가로 스크롤 검사가 무력화된다. */
.hero__bg {
  position: absolute; inset: 0;
  z-index: -2;
  overflow: hidden;
  container-type: size;   /* 아래 cqw/cqh 의 기준 박스 */
  pointer-events: none;
}

#hero-bg {
  /* 세로는 top:0 으로 고정한다(가운데 정렬 아님). 폰트가 늦게 로드되면 히어로
     높이가 커지는데, 세로 가운데 정렬이면 이 거대한 박스의 원점이 같이 움직여
     레이아웃 시프트로 잡힌다 — 형제 브랜치 실측으로 모바일 CLS 0.090 → 0.193
     이었고 top:0 으로 되돌리니 0.090 으로 복귀했다. 가로만 가운데 정렬한다. */
  position: absolute; top: 0; left: 50%;
  transform: translateX(-50%);
  border: 0;
  /* iframe 은 치환 요소라 inset:0 만으로는 늘어나지 않는다. 크기를 직접 준다.
     폴백(컨테이너 단위 미지원): 늘려서 채운다 — 비율은 왜곡되지만 레터박스는 없다. */
  width: 100%; height: 100%;
  /* 커버 크롭: 16:9 를 히어로 박스 양축에 채우고 넘치는 쪽은 버린다.
     ⚠️ 이 두 줄을 **키우지 말 것**(오버스캔 유혹). YouTube 크롬은 프레임
     가장자리에서 고정 px 에 붙으므로 iframe 을 키우면 화면 밖으로 밀려나긴
     한다. 실제로 재 봤다 — 세로 +120px 판은 우하단 "YouTube에서 보기"를
     확실히 지웠지만 영상이 그만큼 더 확대·크롭되면서 **프레임 평균 휘도가
     10% 떨어졌다**(390×844 는 −13%). 직전 라운드에서 오너 요청으로 +17.5%
     밝힌 것을 절반 이상 돌려주는 셈이라 폐기했다. 대신 아래 .hero::after 의
     ④ 우하단 라디얼로 덮는다(같은 목적, 휘도 비용 1/10). */
  width:  max(100cqw, 100cqh * 16 / 9);
  height: max(100cqh, 100cqw * 9 / 16);
  opacity: 0;                       /* 로드 전엔 --bg 그대로, 튀는 전환 없음 */
  /* transition 을 여기 두지 않는다. 두면 스타일시트가 처음 적용되는 순간
     UA 기본값 1 → 0 으로 **페이드아웃이 실제로 돌아서**, load 직후 1.2초 동안
     iframe 이 최대 93% 불투명하게 남는다(실측). 죽은 임베드의 에러 카드가
     번쩍일 수 있는 창이 정확히 거기다. 페이드는 아래 playing 규칙이 맡는다 —
     보이게 될 때만 부드럽게, 사라질 때는 즉시. */
  pointer-events: none;             /* 클릭·스크롤을 가로채지 않는다 */
}
/* playing 일 때만 보인다. 이 선택자는 안전장치의 절반이다 — 임베드가 죽으면
   (비공개·삭제·지역차단·광고차단기·YouTube 장애) hero-3d.js 가 playing 을
   찍지 않으므로 iframe 은 opacity 0 으로 남고, 히어로는 --bg 단색이 된다.
   YouTube 의 "이 동영상은 볼 수 없습니다" 카드가 h1 위에 뜨지 않게 하는 것이
   목적이니, 여기에 기본 opacity 를 주거나 상태 없는 규칙을 추가하지 말 것. */
#hero-bg[data-video-state="playing"] { opacity: 1; transition: opacity 1.2s ease; }

/* JS 게이트는 모듈 로드 시 1회만 평가한다. 세션 도중 OS 설정을 켠 사용자를
   위해 표시 쪽은 선언적으로 즉시 끈다(다운로드 차단은 게이트가 계속 담당). */
@media (prefers-reduced-motion: reduce) { .hero__bg { display: none; } }

/* 스크림 — 통짜 어둡게가 아니라 **어두워야 하는 곳만** 어둡게 한다.
   전판은 100deg 한 겹으로 화면 전체를 45~92% 덮어(영상 opacity 도 .6) 영상이
   배경 무늬로 읽혔다. 지금은 **세 겹**으로 쪼개 상단·우측을 영상에 돌려준다:
     ① 좌상단 라디얼 — YouTube 채널 워터마크(아바타 + "NNT official") 억제 전용.
        임베드 파라미터로는 못 지운다(변형 5종 실측, youtube-bg 리포트 §6).
        390px 에서는 커버 크롭이 워터마크를 화면 밖으로 밀어내지만 데스크톱
        폭에서는 화면 안에 남으므로 여기서 덮는다.
     ② 카피 타원(10% 66% 앵커) — 실제로 글자가 놓이는 영역만 덮는다. 스크림의
        본체는 이쪽이다.
     ③ 아래→위 리니어 — 타원의 아래쪽 가장자리를 화면 바닥까지 잇는다.
     ④ 우하단 라디얼 — "YouTube에서 보기" 버튼(프레임 우측 172px · 하단 60px
        지점, 142×48) 억제 전용. ① 이 좌상단 워터마크에 하는 일을 우하단에
        그대로 한다. 밝기 라운드로 ③ 이 얇아지면서 드러났다.
        반지름 340×190 은 감이 아니다 — 버튼의 가장 먼 모서리(우측 172px,
        하단 60px)가 .90 등고선(62%) 안에 들어오는 최소 크기다. ③·② 와
        합성하면 그 자리 알파가 .92 → .977 이 된다(흰 로고가 배경과 6/255
        이내로 붙는다). **네 겹 모두 같은 색이라 합성 결과는 순서와 무관하다.**
        390px 폭에서는 커버 크롭이 이 버튼을 화면 밖으로 밀어내므로 이 겹은
        빈 코너를 덮을 뿐이다(그 자리는 ③ 이 이미 .86 이다).

   과거에 있던 또 다른 겹(왼→오 리니어)은 뺐다. 타원(②)이 그 역할을 거의 다 하고
   있어서, 빼고도 최악 대비가 유지됐다(그 시점 실측 6.26:1). 없어도 되는 겹은
   두지 않는다.

   숫자를 감으로 정하지 않았다. 스크림을 끈 상태의 히어로 프레임을 두 뷰포트 ×
   타임스탬프 9개(1·3·8·15·22·25·30·35·42초, 밝아지는 t≈3s 오렌지 코일과
   t≈25s 올리브 포함)로 캡처하고, 후보 그라데이션을 흰 바탕 위에 렌더해 알파
   맵을 역산한 뒤 **같은 프레임 위에서** 오프라인 합성해 후보 5종을 비교했다.
   대비는 요소 박스가 아니라 Range.getClientRects() 로 딴 **글자 줄박스**에서
   잰다 — h1/eyebrow 는 블록이라 박스가 컬럼 전체 폭이고, 글자가 없는 오른쪽
   빈 구간(스크림이 거의 없는 밝은 영상)이 섞이면 수치가 통째로 무의미해진다.

   ── 밝기 완화 판(브랜드 오너: "영상이 너무 어둡다") ──
   세 겹의 알파를 어디서 깎을지는 **오프라인 합성**으로 골랐다. 라이브 측정은
   YouTube 재생 위치가 회차마다 흔들려 같은 CSS 로 4.18 ~ 4.66:1 이 나온다 —
   ±0.05 알파를 그 수치로 튜닝하는 것은 불가능하다. 그래서 스크림을 끈 프레임을
   두 뷰포트 × 7개 타임스탬프로 한 번 캡처해 두고, 후보 스크림을 **같은 픽셀**
   위에 합성해 비교했다(라이브와 ±0.15:1 이내로 일치했다).
   그 비교가 말해 준 것: ③ 아래→위 리니어가 가장 넓은 면적을 높은 알파로 덮고
   있었지만(.58 이 높이 22% 까지) 그 구간의 대비 여유는 8.5~17:1 로 가장 컸다.
   반대로 ② 타원의 **코어**(카피 바로 아래)는 여유가 없다 — 코어를 .93→.85 로
   내린 후보는 최악 대비가 4.06:1 로 무너졌다. 그래서 코어는 그대로 두고
   **③의 전 구간**과 **②·①의 바깥 자락**만 걷었다. 영상 opacity 는 .95 → 1.
   결과: 프레임 평균 휘도 +17.5%, 최악 대비 5.60 → 5.18:1(오프라인 동일 프레임 기준).
   한 단계 더 연 후보(평균 휘도 +21%)도 있었지만 오프라인 4.98 · 라이브 4.83~4.87
   로 기준 여유가 0.35 밖에 안 남아 되돌렸다 — 영상 교체 여지를 남긴다.

   잃은 국소 가독성은 카피의 text-shadow 로 되샀다(아래 규칙) — 화면 전체를
   어둡게 하지 않고 글자 밑에만 어둠을 둔다.
   ⚠️ text-shadow 는 `checks/hero-contrast.mjs` 의 수치에 **잡히지 않는다**
   (그 검사는 카피를 숨긴 프레임에서 배경 휘도만 잰다). 즉 아래 표는 실제
   가독성을 과소평가한다 — 표는 스크림만의 성적표로 읽을 것.

   채택안의 라이브 실측 최악값(배경 픽셀 **최댓값** 기준, 6개 타임스탬프):
     1440×900  eyebrow 9.25:1  h1 7.00:1  lead 15.00:1
     390×844   eyebrow 5.03:1  h1 5.11:1  lead  7.00:1
   전체 최악은 5.03:1 (390×844 eyebrow @ t≈25s). 기준 4.5:1 대비 여유를 일부러
   남겼다 — 프레임 의존 측정이고 영상이 교체될 수 있기 때문이다.
   더 열 곳이 남았다면 ②의 자락과 ③ 쪽이다. ①(좌상단 라디얼)은 워터마크
   전용이라 건드리면 밝기가 아니라 워터마크가 드러난다. ②의 코어는 건드리지 말 것.
   값을 바꿨으면 대비를 다시 재고 이 표를 갱신할 것 — `node checks/hero-contrast.mjs` 가 그 측정을
   그대로 다시 돌린다(스모크 스위트에는 넣지 않았다: 크로스 오리진 영상 프레임에
   의존해 간헐적으로 흔들린다. 사유는 그 파일 헤더).

   ⚠️ 공통 하네스(smoke.mjs §6)의 대비 어서션은 이 값을 못 본다 — 조상 체인의
   background-color 만 보므로 히어로 카피는 body 의 --bg(#0B0D0F) 위에 있는
   것으로 계산된다. 영상 위 실측 대비는 그 검사가 아니라 위 절차로만 확인된다. */
.hero::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  /* 색은 :root 의 --bg-rgb 한 곳에서만 온다(위 주석 참고).
     rgba(var(--x), α) 는 계산값 단계에서 평범한 색으로 접히므로 폴백 선언이
     필요 없다 — color-mix 판에 있던 이중 선언은 그래서 사라졌다. */
  background:
    radial-gradient(58% 34% at 0% 0%,
      rgba(var(--bg-rgb), .97) 0%, rgba(var(--bg-rgb), .93) 30%,
      rgba(var(--bg-rgb), .45) 62%, rgba(var(--bg-rgb), 0) 92%),
    radial-gradient(98% 64% at 10% 66%,
      rgba(var(--bg-rgb), .93) 0%, rgba(var(--bg-rgb), .87) 46%,
      rgba(var(--bg-rgb), .58) 77%, rgba(var(--bg-rgb), 0) 106%),
    radial-gradient(340px 190px at 100% 100%,
      rgba(var(--bg-rgb), .93) 0%, rgba(var(--bg-rgb), .90) 62%,
      rgba(var(--bg-rgb), .45) 82%, rgba(var(--bg-rgb), 0) 100%),
    linear-gradient(to top,
      rgba(var(--bg-rgb), .86) 0%, rgba(var(--bg-rgb), .52) 20%,
      rgba(var(--bg-rgb), .15) 46%, rgba(var(--bg-rgb), 0) 67%);
}

/* 카피 밑에만 어둠을 둔다 — 스크림을 걷어낸 만큼의 국소 가독성을 여기서 되산다.
   화면 전체를 덮는 대신 글자 획 주변 몇 px 만 어두워지므로 "영상이 어둡다" 는
   인상 없이 밝은 프레임(t≈3s 오렌지, t≈25s 올리브) 위에서도 글자가 선다.
   색은 :root 의 --bg-rgb 한 곳에서만 온다.
   .cta(채워진 초록 버튼)에는 걸지 않는다 — 그 글자는 영상이 아니라 단색
   --accent 위에 있어 그림자가 이득 없이 지저분해진다. 유령 버튼만 포함한다.
   두 겹인 이유: 1px 짜리 타이트한 겹이 획 가장자리를 세우고, 넓고 옅은 겹이
   글자 주변 배경을 통째로 눌러 밝은 프레임에서 획이 배경에 묻히는 것을 막는다. */
.hero .eyebrow, .hero__title, .hero__lead, .hero .cta--ghost {
  text-shadow:
    0 1px 2px rgba(var(--bg-rgb), .92),
    0 2px 14px rgba(var(--bg-rgb), .70);
}

/* 단일 컬럼 고정 — 히어로는 이제 카피 하나뿐이다(브랜드 오너 결정: "영상만").
   2열 브레이크포인트는 오른쪽 칸(poster + 3D)을 위한 것이었고 그 칸 자체가
   없어졌다. 3D 를 복구하면 아래 "3D 레이어 — 주석 처리" 블록과 함께
   @media (min-width: 880px) { .hero__grid { grid-template-columns: 1.05fr .95fr; } }
   를 되돌린다. */
.hero__grid {
  display: grid; gap: clamp(40px, 6vw, 72px);
  grid-template-columns: 1fr;
  align-items: center;
}

.hero__title {
  font-size: var(--fs-hero); line-height: 1.1; letter-spacing: -.02em;
  margin: 0 0 24px; font-weight: 700;
}
/* 히어로 리드만 --muted 를 쓰지 않는다. --muted(#8B959C)는 --bg 단색 위에서는
   6.14:1 이지만, 영상이 밝아지는 프레임(t≈3s 오렌지 코일)에서는 스크림을 통과한
   실제 배경이 밝아져 4.5:1 아래로 내려간다 — 실측으로 확인했다. 리드를 읽히게
   하려고 스크림 전체를 다시 어둡게 하면 "영상을 보이게 한다"는 목적이 사라지므로,
   대신 이 문단만 --fg 로 올렸다(레퍼런스도 흰 카피다). */
.hero__lead { max-width: 42ch; margin-bottom: 32px; color: var(--fg); }
.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; }

/* 스크롤 큐 — 바닥 가운데. 1px 세로선 안에서 짧은 막대가 흘러내린다.
   여기 overflow:hidden 은 자기 자신(1px 폭 absolute 박스) 안쪽만 자른다.
   .hero / body 의 overflow 를 건드리는 것과는 다른 이야기다 — 그 둘은 360px
   가로 스크롤 검사를 무력화하기 때문에 의도적으로 비워 둔 자리다(위 .hero 주석). */
.hero__scroll {
  position: absolute; left: 50%; bottom: clamp(24px, 4vh, 40px);
  transform: translateX(-50%);
  width: 1px; height: 48px;
  background: color-mix(in srgb, var(--fg) 22%, transparent);
  overflow: hidden; pointer-events: none;
}
.hero__scroll::after {
  content: ''; position: absolute; left: 0; top: 0; width: 1px; height: 16px;
  background: var(--accent);
  animation: hero-scroll-cue 2s cubic-bezier(.6, 0, .4, 1) infinite;
}
/* top 이 아니라 transform 을 애니메이션한다. top 판을 실제로 계측했더니 모바일
   Lighthouse 의 메인스레드 작업이 1.0s → 2.4s(paint 55ms → 605ms)로 뛰고 FCP 가
   2.0s → 3.6s 로 밀렸다 — top 은 매 프레임 레이아웃을 다시 돌리고, 무한 반복이라
   그게 트레이스 내내 계속된다. transform 은 컴포지터에서 끝난다.
   prefers-reduced-motion 은 :root 블록의 전역 규칙이 잡는다
   (animation-duration: .01ms !important) — 여기서 다시 쓰지 않는다. */
@keyframes hero-scroll-cue {
  0%   { transform: translateY(-16px); }
  100% { transform: translateY(48px); }
}

/* ── 3D 레이어 — 주석 처리 (복구법은 index.html 히어로 주석 블록 참고) ──
   .hero__visual 은 poster 가 없어진 지금 3D 전용 컨테이너라 함께 묶는다.
   position:relative + aspect-ratio 는 #hero-model(absolute inset:0)의 기준이다.
   퓨리틱은 폭:높이 ≈ 1:5.4 로 세로로 긴 제품이라 세로로 긴 프레임을 준다.

.hero__visual { position: relative; aspect-ratio: 3 / 4; }

#hero-model {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  opacity: 0; transition: opacity .6s ease;
  background: transparent;
}
#hero-model[data-state="loaded"] { opacity: 1; }
   ── 3D 레이어 주석 끝 ── */

/* ── 브랜드 소개 ── */
.brand__grid { display: grid; gap: clamp(24px, 4vw, 56px); grid-template-columns: 1fr; }
@media (min-width: 820px) { .brand__grid { grid-template-columns: .8fr 1.2fr; } }

/* ── 유통 채널(#channels) ──
   전용 CSS 없음. 예전에 있던 .channels__list / .channel / .channel__name /
   .channels__note 는 편의점 체인 로고 타일용이었고, 그 섹션이 질문형 h2 +
   .answer + .spec-table 로 바뀌면서 전부 참조가 사라져 삭제했다.
   지금은 .answer · .table-scroll · .spec-table · .article__cta 를 재사용한다. */

/* ── 제품 갤러리 ──
   제품컷은 밝은 스튜디오 배경(#F3F3F3)이라 배경을 지우지 않고
   카드 상단을 같은 색으로 맞춰 여백 없이 붙인다(다크 페이지 위의 밝은 타일).
   텍스트는 전부 카드 하단의 --surface 영역(어두운 톤)에 둔다 —
   --fg on --surface 16.17:1, --muted on --surface 5.84:1 (둘 다 AA 4.5:1 이상, 계산 확인 완료). */
.products__grid {
  list-style: none; margin: 40px 0 0; padding: 0;
  display: grid; gap: clamp(16px, 2.5vw, 28px);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.product {
  background: var(--surface);
  border-radius: 28px; overflow: hidden; text-align: center;
  display: flex; flex-direction: column;
}
.product img { margin: 0; background: var(--card-bg); }
.product__body { padding: 20px; }
.product__name { font-size: 1.25rem; margin: 0 0 8px; }
.product__desc { font-size: .9375rem; margin-bottom: 20px; }

/* ── 디바이스 카드 ── */
.device {
  margin-top: clamp(16px, 2.5vw, 28px);
  background: var(--surface);
  border-radius: 28px; overflow: hidden;
  display: grid; grid-template-columns: 1fr;
}
@media (min-width: 620px) {
  .device { grid-template-columns: minmax(200px, 280px) 1fr; align-items: stretch; }
}
.device img { margin: 0; background: var(--card-bg); width: 100%; height: 100%; object-fit: cover; }
/* min-width:0 — 그리드 아이템의 기본 min-width:auto는 자식(.spec-table)의
   min-content 폭까지 트랙을 늘린다. 그러면 overflow-x:auto인 .table-scroll이
   자기보다 큰 부모 트랙 안에서는 스크롤할 일이 없어져 표 오른쪽 끝이
   카드 밖(그리고 .device의 overflow:hidden)에 잘려 나간다 — 360px 실측으로
   확인됨. 0으로 낮춰야 트랙이 카드 폭에 맞게 줄고, .table-scroll이 진짜로
   스크롤을 떠맡는다. */
.device__body { padding: clamp(20px, 3vw, 32px); min-width: 0; }
.device__body h3 { font-size: 1.5rem; margin: 0 0 12px; }

/* 스펙 표: 360px에서 8행짜리 2열 표가 폭을 넘길 수 있으므로 표만 가로
   스크롤시킨다(페이지 자체는 절대 넘기지 않는다 — 반응형 제약). */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.table-scroll { overflow-x: auto; margin: 20px 0; }
.spec-table { width: 100%; min-width: 360px; border-collapse: collapse; }
.spec-table th, .spec-table td {
  text-align: left; padding: 10px 16px;
  border-bottom: 1px solid var(--line); font-weight: 400;
}
.spec-table th { color: var(--fg); font-weight: 600; white-space: nowrap; }
.spec-table td { color: var(--muted); }

/* ── 비전 ── */
.vision__list {
  list-style: none; margin: 40px 0 0; padding: 0;
  display: grid; gap: clamp(20px, 3vw, 36px);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.vision__num {
  display: block; font-size: 2.5rem; font-weight: 700;
  color: var(--accent-2); line-height: 1; margin-bottom: 16px;
}
.vision__item h3 { font-size: 1.25rem; margin: 0 0 10px; }

/* ── 정보성 본문 ──
   읽기 줄길이는 74ch 로 묶되 **컨테이너 폭은 .wrap 그대로** 둔다.
   .article 자체에 max-width 를 주면 .wrap 의 margin-inline:auto 가 좁아진 상자를
   다시 가운데로 밀어, 같은 페이지 안에서 .wrap 섹션과 좌측 그리드선이 갈라진다
   (1440 실측: .wrap 150–1290 vs .wrap.article 323–1117, 173px 안쪽).
   그래서 폭 제한은 자식으로 내린다 — 상자는 제자리, 글줄만 짧아진다.
   단위가 ch 가 아니라 rem 인 이유: ch 는 **그 요소의** 폰트 크기로 풀린다.
   자식에 걸면 제목(h2 는 본문의 2배 이상)에서 74ch 가 컨테이너보다 커져 제한이
   풀린다(실측 h2 1060px). 50rem ≈ 800px 은 이전 컨테이너 폭 794px 과 같다. */
.article > * { max-width: 50rem; }
.article h3 {
  font-size: 1.25rem; margin: 40px 0 12px; color: var(--fg); font-weight: 700;
}
.article__list { margin: 0 0 16px; padding-left: 1.2em; color: var(--muted); }
.article__list li { margin-bottom: 10px; }
.article__list strong { color: var(--fg); }
.article__cta { margin-top: 40px; }

/* ── 서브페이지 공통 ──
   /puritik/ · /sticks/ · /guide/ 는 JS를 쓰지 않는다(히어로 영상·3D는 메인 전용). */
.subnav { padding-block: 24px 0; font-size: .875rem; color: var(--muted); }
.subnav a { color: var(--muted); text-decoration: none; }
.subnav a:hover, .subnav a:focus-visible { color: var(--fg); }
.subnav span { margin-inline: 6px; }

.page-title {
  font-size: clamp(2rem, 5vw, 3.25rem); line-height: 1.15; letter-spacing: -.02em;
  margin: 0 0 20px; font-weight: 700;
}
.page-lead { color: var(--fg); }

/* 제품컷은 밝은 스튜디오 배경(#F3F3F3)이라 카드처럼 배경을 깔아 준다. */
.page-figure {
  max-width: 320px; width: 100%; margin: 32px 0;
  background: var(--card-bg); border-radius: 28px;
}

/* AEO: 소제목 바로 아래 첫 문단이 곧 답이다. 시각적으로도 답을 먼저 읽게 한다. */
.answer { color: var(--fg); }

.steps { margin: 24px 0; padding-left: 1.3em; color: var(--muted); }
.steps li { margin-bottom: 12px; }
.steps strong { color: var(--fg); }

/* 맛별 블록: 이미지 + 설명. min-width:0 은 .device__body와 같은 이유
   (그리드 아이템 기본 min-width:auto가 트랙을 콘텐츠 min-content까지 늘린다). */
.flavor {
  display: grid; grid-template-columns: 1fr; gap: clamp(16px, 3vw, 32px);
  align-items: center;
  margin-top: 40px; padding-top: 40px; border-top: 1px solid var(--line);
}
.flavor:first-of-type { margin-top: 24px; padding-top: 0; border-top: 0; }
@media (min-width: 620px) { .flavor { grid-template-columns: minmax(0, 240px) 1fr; } }
.flavor img { border-radius: 28px; background: var(--card-bg); }
.flavor__body { min-width: 0; }
.flavor__body h3 { font-size: 1.25rem; margin: 0 0 10px; }

/* 페이지 간 상호 링크 카드 */
.pagelinks {
  list-style: none; margin: 32px 0 0; padding: 0;
  display: grid; gap: clamp(16px, 2.5vw, 24px);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.pagelinks a {
  display: block; height: 100%; padding: 24px; text-decoration: none;
  background: var(--surface); border: 1px solid transparent; border-radius: 28px;
  transition: border-color .2s ease, transform .2s ease;
}
.pagelinks a:hover { border-color: var(--accent); transform: translateY(-2px); }
.pagelinks a:focus-visible { outline: 3px solid var(--accent-2); outline-offset: 3px; }
.pagelinks h3 { font-size: 1.125rem; margin: 0 0 8px; }
.pagelinks p { margin: 0; font-size: .9375rem; }

/* 비교 표(궐련형 vs 액상형)는 헤더 행이 있다 */
.spec-table thead th { color: var(--fg); font-weight: 700; }

/* ── 소셜 ── */
.social__links { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }

/* ── 푸터 ── */
.footer {
  padding-block: clamp(48px, 6vw, 80px);
  border-top: 1px solid var(--line);
  background: var(--surface);
  font-size: .875rem;
}
.footer__brand { font-weight: 700; font-size: 1.125rem; color: var(--fg); margin-bottom: 20px; }
.footer__brand span { color: var(--accent); }
.footer__note { color: var(--fg); margin-top: 20px; }
.footer__copy { margin: 20px 0 0; font-size: .8125rem; }

/* ── 외부(자사몰) 링크 표시 ──
   SHOP 만 사이트 밖으로 나간다. 글리프는 CSS 로 붙인다 — 마크업에 넣으면
   헤더 라벨의 textContent 에 섞여 들어가고, 그 문자열은 스모크 스위트가
   값으로 못박는 대상이다. 스크린리더용 안내는 마크업의 .sr-only 가 맡는다. */
.site-nav__ext::after { content: '↗'; margin-left: .25em; font-size: .85em; }

/* ── 후기 카드 ──
   구조는 .products__grid / .product 와 같다(이미지 위, 본문 아래). 다른 점은
   제품이 특정되지 않은 후기에 붙일 이미지가 없다는 것 — 고객이 올린 사진이
   아니라 제품컷을 쓰기 때문에, 제품을 모르면 사진을 붙일 근거가 없다.
   .review--text 가 그 경우이고 인용문만 남는다. */
.review-grid {
  list-style: none; margin: 32px 0 0; padding: 0;
  display: grid; gap: clamp(16px, 2.5vw, 28px);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.review {
  background: var(--surface);
  border-radius: 28px; overflow: hidden;
  display: flex; flex-direction: column;
}
.review img { margin: 0; background: var(--card-bg); width: 100%; }
.review__body { padding: 20px; display: flex; flex-direction: column; gap: 12px; flex: 1; }
.review__score { margin: 0; font-size: .9375rem; letter-spacing: .1em; color: var(--accent); }
.review__score span + span { letter-spacing: 0; color: var(--fg); font-weight: 700; }
.review__quote { margin: 0; }
.review__quote p { margin: 0; font-size: .9375rem; color: var(--fg); }
/* 본문 앞뒤에 따옴표를 CSS 로 붙인다 — 원문에 없는 글자를 마크업에 섞지 않기 위해서다. */
.review__quote p::before { content: '“'; }
.review__quote p::after  { content: '”'; }
.review__meta { margin: auto 0 0; font-size: .8125rem; color: var(--muted); }
