/* 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;
}

/* ── 하위 메뉴(펼침) ──
   site.mjs 의 nav 항목에 children 이 있을 때만 나온다. JS 없이 동작한다:
   모바일은 드롭다운 패널 안에 들여쓴 목록으로 항상 보이고, 데스크톱은 hover 와
   focus-within(키보드 탭)으로 열린다. 링크가 항상 마크업에 있으므로 크롤러도 읽는다. */
.site-subnav { list-style: none; margin: 2px 0 8px; padding: 0 0 0 14px; }
.site-subnav a { color: var(--muted); font-size: .875rem; font-weight: 500; }
.site-subnav a:hover { color: var(--accent); }

/* 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;
  }

  /* 데스크톱: **얹은 항목만** 그 아래로 펼친다(오너 지정, 2026-08-28).
     한때 전체 폭 패널로 모든 열을 함께 열었는데, 원하는 동작은 그게 아니었다 —
     퓨리틱에 얹으면 퓨리틱 것만, 가이드에 얹으면 가이드 것만 열린다.
     여는 조건은 그 항목의 hover 와 focus-within(키보드) 둘뿐이다. */
  .site-nav__group { position: relative; }
  .site-nav__group > a::after { content: '⌄'; margin-left: .3em; font-size: .9em; opacity: .7; }

  .site-subnav {
    position: absolute; top: 100%; left: -16px; z-index: 30;
    min-width: 190px; margin: 0; padding: 10px 0;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    opacity: 0; visibility: hidden; transform: translateY(-6px);
    transition: opacity .18s ease, transform .18s ease, visibility .18s;
  }
  .site-nav__group:hover .site-subnav,
  .site-nav__group:focus-within .site-subnav { opacity: 1; visibility: visible; transform: none; }
  .site-subnav a {
    display: block; width: 100%; padding: 8px 16px; font-size: .8125rem; white-space: nowrap;
  }
}

/* ── 히어로 ── */
.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); }

/* ── 자사몰 유도 배너·팝업 (site.mjs mallPromo) ─────────────────────────────
   배너: 헤더 바로 아래, 페이지 최상단에 고정으로 붙는 얇은 CTA 바.
   팝업: 스크롤 50% 또는 8초 경과 중 먼저 오는 시점에 뜬다(shared/layout.mjs
   스크립트). 방문 세션당 한 번만 — sessionStorage. 이미지 한 장 전체가
   자사몰 링크다(별도 제목·CTA 문구 없음). */
.mall-banner {
  display: block;
  background: var(--accent);
  color: var(--cta-fg);
  text-align: center;
  font-weight: 600;
  font-size: .95rem;
  padding: 10px var(--pad-inline);
  text-decoration: none;
}
.mall-banner:hover { filter: brightness(1.06); }
/* 홈은 body 상단 패딩이 없다(히어로가 고정 헤더 밑에 깔리는 게 의도된 그림이라
   body:not(.is-home) 에만 패딩을 준다). 그래서 흐름의 첫 요소인 이 배너가 헤더에
   통째로 가려져 있었다 — 실측 배너 top 0 / 헤더 bottom 52. 배너가 있는 홈에서만
   헤더 높이만큼 내린다(서브페이지는 body 패딩이 이미 처리한다). */
.is-home .mall-banner { margin-top: var(--header-h); }

.mall-popup {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(var(--bg-rgb), .6);
}
.mall-popup[hidden] { display: none; }
.mall-popup__box {
  position: relative;
  max-width: 360px; width: 100%;
  border-radius: 16px;
  overflow: hidden;
}
.mall-popup__link { display: block; }
.mall-popup__img { display: block; width: 100%; height: auto; }
.mall-popup__close {
  position: absolute; top: 10px; right: 10px; z-index: 1;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, .5);
  border: none; border-radius: 50%;
  color: #fff;
  font-size: 1.25rem; line-height: 1; cursor: pointer; padding: 0;
}
.mall-popup__close:hover { background: rgba(0, 0, 0, .7); }

/* ── 사이트 팔레트 (sites/jdinside/site.mjs 의 tokens) ─────────────────────
   위 :root 를 덮는다. 여기 없는 토큰은 공통 값을 그대로 쓴다. */
:root {
  --bg: #FFFFFF;
  --bg-rgb: 255, 255, 255;
  --surface: #F3F4F5;
  --surface-2: #E8EAEC;
  --fg: #15181B;
  --muted: #5F6672;
  --accent: #0F766E;
  --line: #E1E4E7;
  --cta-fg: #FFFFFF;
  --card-bg: #F3F4F5;
}

/* sites/jdinside/css/style.css — 이 사이트만의 확장(tools/build.mjs 가 공용 시트 뒤에 붙인다).

   3라운드 디자인 리뷰 후 전면 다크 리디자인. 계기: "헤더가 이상하고 너무
   AI같다"는 피드백 + 참고 사이트(lostmary.com) — 다크 배경, 채도 높은 브랜드
   컬러 하나, 굵은 디스플레이 타이포, 비대칭 카드. 이 사이트는 제품 사진이
   없는 비교 정보 허브라 그 톤을 그대로 베끼진 않고 다음 세 가지로 옮겼다:
   1. 단일 accent(cyan-teal #22D3D0, 처음엔 red-orange #FF4B36 이었으나 "너무
      빨갛다·청량한 쿨톤으로" 피드백으로 교체) — 로고와 호버를 이거 하나로 처리한다.
      이전 navy+red 2트랙 절제가 오히려 "색이 없다"처럼 읽혔다.
   2. 헤더는 진짜 버그였다 — 공용 .site-header__inner 는 display:grid(3열,
      짧은 한줄 nav 기준)인데 이전 오버라이드는 flex-wrap/row-gap 만 얹어서
      grid엔 아무 효과가 없었다(중간 auto 트랙이 17개 nav의 max-content 폭으로
      계산되며 로고·2번째 줄이 임의 위치에 어긋났다). 데스크톱은 로고 행 + nav
      밴드로 완전히 새로 짠다. position:static → relative 로 바꾼 것도 버그
      수정이다(모바일 드롭다운 nav 가 absolute 로 이 헤더에 앵커링돼야 하는데
      static 은 포지셔닝 컨텍스트가 아니다).
   3. 타이포는 위계 대비를 키운다(제목은 800/더 크게, 본문·표는 그대로) —
      전엔 h1/h2/본문이 비슷한 무게라 "컴포넌트 기본값을 조립한" 인상이었다.
   카드는 테두리 대신 배경 톤 차이(surface/surface-2)로 구분한다. (처음엔 홈
   브랜드 그리드 상위 3개만 accent 채움 + 사진 없이 강조해 단조로움을 깨려
   했는데, "왜 이 3개만 사진이 없냐"는 피드백으로 5라운드에서 되돌렸다 — 지금은
   15개 전부 사진 카드로 균일하다.)

   4라운드: 제품 렌더가 거의 무채색 스튜디오 컷(흰 배경 + 연기 wisp)으로
   바뀌면서 다크 UI 위에 얹혔을 때 대비가 너무 셌다 — 사진에 맞춰 사이트 전체를
   다크 → 라이트로 다시 뒤집었다(site.mjs tokens). 이 파일의 규칙 자체는
   안 바뀐다 — 전부 var(--bg)/var(--surface)/var(--accent) 토큰 참조라 값만
   바뀌면 그대로 라이트 테마가 된다. */

/* 카드 radius: 애플 실측(rf-hcard-content, buy-mac) 18px 대비, 이 사이트 카드는
   더 작으므로(337px 카드 기준) 16px가 비례상 맞다. .pagelinks a/.hero-split__img/
   .sidebox/.pagelinks img 전부 이 토큰 하나로 물려 있다 — 여기 하나만 바꾸면 전파된다.
   (주의: shared/css/style.css 의 .product/.device/.review/.page-figure/.flavor img 는
   이 토큰과 무관하게 28px 로 하드코딩돼 있고, 그 템플릿들은 jdinside 에 없다.) */
:root { --radius-card: 16px; }

/* ── 헤더 ── */
.site-header {
  position: relative; /* fixed→relative: 모바일 드롭다운 nav(absolute)가 여기 앵커링된다 */
  height: auto;
  padding-block: 14px;
  background: var(--bg);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 1px solid var(--line);
}
body:not(.is-home) { padding-top: 0; }

.site-logo { font-weight: 800; font-size: 1.125rem; letter-spacing: -.01em; }
.site-logo span { color: var(--accent); }
.site-nav a:hover { color: var(--accent); }

/* nav = 홈(1) + 브랜드 16개(2~17) + 종류 4개(18~21) — 종류 그룹 앞에 구분선. */
.site-nav__list li:nth-child(18) {
  border-top: 1px solid var(--accent); margin-top: 4px; padding-top: 8px;
}

@media (min-width: 768px) {
  /* 공용 3열 그리드(로고|nav|토글, 토글은 이 폭에서 안 보임)는 21개짜리
     항상-보임 2줄 nav 를 못 받는다 — 로고 행 / nav 밴드 두 단으로 다시 짠다. */
  .site-header__inner {
    display: flex; flex-direction: column; align-items: stretch;
  }
  .site-nav {
    margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line);
  }
  .site-nav__list { flex-wrap: wrap; gap: 6px 22px; }
  .site-nav a {
    font-size: .6875rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
    padding: 4px 0;
  }
  .site-nav__list li:nth-child(18) {
    border-top: 0; margin-top: 0; padding-top: 0;
    border-left: 1px solid var(--accent); margin-left: 12px; padding-left: 14px;
  }
}

/* ── 섹션 세로 리듬 ──
   이전 값은 padding-block: 44px 32px 로 위아래가 비대칭이었다(실측). 섹션이
   연달아 쌓이면 그 8px 차가 누적돼 경계가 흔들린다 — 대칭으로 맞추고, 정보
   허브치고 답답하던 세로 여백을 Product 레지스터 기준(약 96px)까지 넓힌다. */
.section { padding-block: clamp(56px, 7vw, 96px); }

/* 홈의 "브랜드별로 찾기"(--bg)와 "최근 올린 글" 사이에 --surface 톤 밴드를
   끼워 섹션 경계를 보더 대신 배경 교차로 표시한다. */
#home-types { background: var(--surface); }

/* 홈 히어로 — 제목(좌) + 제품 렌더(우).
   한동안 텍스트만 남긴 납작한 헤더였는데(애플 스토어 페이지 헤더 실측 기반)
   오른쪽이 통째로 비어 균형이 무너졌다. 렌더를 되살리고 배경에 accent 를 아주
   옅게 깐 밴드를 둬서 헤더-히어로-카드 그리드의 경계를 만든다. */
.section--hero {
  padding-block: clamp(48px, 6vw, 80px);
  background:
    radial-gradient(120% 140% at 88% 30%, rgba(15, 118, 110, .10), transparent 62%),
    linear-gradient(180deg, var(--surface), var(--bg) 78%);
  border-bottom: 1px solid var(--line);
}
.storehead { display: grid; gap: 28px; align-items: center; }
@media (min-width: 860px) {
  /* 텍스트 : 이미지 = 대략 황금비. 이미지를 고정폭으로 두면 좁은 데스크톱에서
     텍스트가 먼저 찌그러지므로 minmax 로 이미지 쪽이 먼저 줄게 한다. */
  .storehead { grid-template-columns: minmax(0, 1.15fr) minmax(240px, .85fr); gap: 48px; }
}
.storehead .page-title { margin: 0 0 16px; }
.storehead__lead {
  margin: 0; color: var(--muted);
  font-size: 1.0625rem; line-height: 1.65; max-width: 46ch;
}
.storehead__img {
  display: block; width: 100%; height: auto; justify-self: end;
  max-width: 520px; border-radius: var(--radius-card);
}

/* 카테고리 페이지 히어로 — 브랜드 하나당 렌더 하나, 홈보다 작게. */
.hero-split--cat { gap: 24px; }
@media (min-width: 860px) {
  .hero-split--cat { grid-template-columns: 1fr 240px; }
  .hero-split--cat .hero-split__img { max-width: 240px; justify-self: end; }
}

/* ── 타이포 위계 — 제목 무게·크기 대비를 키운다. 본문·표는 그대로(밀도 유지). ── */
.page-title { font-weight: 800; font-size: clamp(2.5rem, 6vw, 4.25rem); line-height: 1.05; }
h2 { font-weight: 800; font-size: clamp(1.75rem, 3vw, 2.25rem); letter-spacing: -.01em; }
.pagelinks h3 { font-size: 1.0625rem; font-weight: 700; }
.sidebox h3 { font-size: 1.0625rem; font-weight: 700; }

/* ── 최근 글 목록(홈) · 카테고리 글 목록 ── */
.postlist { list-style: none; margin: 24px 0 0; padding: 0; border-top: 1px solid var(--line); }
.postlist li {
  border-bottom: 1px solid var(--line); padding: 14px 2px;
  display: flex; justify-content: space-between; align-items: baseline; gap: 14px;
}
.postlist a { color: var(--fg); text-decoration: none; font-size: 1rem; }
.postlist a:hover { color: var(--accent); }
.postlist time { font-size: .8125rem; color: var(--muted); white-space: nowrap; }

/* ── 브랜드/종류 타일 그리드(.pagelinks, 공용 컴포넌트 재사용) — 테두리 대신
   배경 톤 차이로 카드를 구분한다(연한 회색 테두리 카드는 "제네릭 SaaS"로
   읽힌다). ── */
.pagelinks a {
  border: 0; border-radius: var(--radius-card); padding: 20px;
  background: var(--surface);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .05);
  transition: transform .2s ease, box-shadow .2s ease, background-color .2s ease;
}
.pagelinks a:hover {
  border-color: transparent; background: var(--surface-2);
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, .10);
}
.pagelinks a:focus-visible { outline-offset: 2px; }
/* 브랜드 타일은 전부(15개) 제품 사진을 카드 위쪽에 건다 — 처음엔 상위 3개
   (아이코스·릴·글로)만 accent 채움 + 사진 없이 강조했는데, 브랜드가 15개로
   늘면서 "왜 이 3개만 사진이 없냐"는 피드백을 받고 전부 균일한 사진 카드로
   되돌렸다. 자사 브랜드(레딜·엔엔티·제타닉)는 실사, 나머지는 Gemini 렌더. */
.pagelinks img {
  display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
  border-radius: calc(var(--radius-card) - 2px); margin-bottom: 14px; background: var(--bg);
}

/* 브랜드 카드(#home-brands)는 이미지를 카드 가장자리까지 꽉 채운다(애플 카드
   실측: 이미지가 패딩 없이 카드 모서리에 바로 닿고, 텍스트만 아래 여백을
   가진다) — 사방 20px 패딩 안에 이미지를 가두면 실제보다 작아 보인다. */
#home-brands .pagelinks a { padding: 0; overflow: hidden; }
#home-brands .pagelinks img { border-radius: 0; margin-bottom: 0; aspect-ratio: 4 / 3; }
#home-brands .pagelinks h3, #home-brands .pagelinks p { padding-inline: 24px; }
#home-brands .pagelinks h3 { margin-top: 20px; }
#home-brands .pagelinks p { padding-bottom: 24px; }

/* ── 브랜드/종류 카드 그리드 ──
   한동안 가로 스크롤 캐러셀(애플 스토어 실측 기반)이었는데 되돌린다. 이유는
   취향이 아니라 수치다 — 캐러셀 컨테이너가 .wrap(1140px)을 벗어나 100vw 로
   풀블리드하는데 카드는 280~400px 고정폭이라 1440px 화면에서도 마지막 카드가
   항상 잘린 채로 걸렸다(실측: #home-types ul 이 1440px, 내용 폭은 1060px).
   "더 있다"는 신호가 아니라 레이아웃이 깨진 것으로 읽혔다. 카드 수가 15개로
   고정돼 있어 가로로 흘릴 이유도 없다. */
#home-brands .pagelinks {
  display: grid; gap: clamp(16px, 2vw, 24px);
  grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
}
/* 모바일은 2열로 접는다. 1열로 두면 풀폭 이미지 카드 15장이 세로로 쌓여
   홈이 7,300px 을 넘는다(실측). 이전 디자인 시안도 같은 문제가 있었고
   (8,474px) 거기서 고치지 못한 부분이다 — 카드 성격(제품 렌더 그리드)은
   그대로 두고 열 수만 늘려 훑는 거리를 절반 이하로 줄인다. */
@media (max-width: 599px) {
  #home-brands .pagelinks { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  #home-brands .pagelinks h3 { font-size: 1rem; margin-top: 14px; padding-inline: 14px; }
  #home-brands .pagelinks p { font-size: .8125rem; padding: 0 14px 16px; }
}
@media (min-width: 900px) {
  /* auto-fill 에 맡기면 1060px 내용 폭에서 3열이 되지만, 창이 더 넓어져도
     3열을 유지해 카드(=제품 렌더) 면적을 키운다. 이전 버전과 같은 배치다. */
  #home-brands .pagelinks { grid-template-columns: repeat(3, 1fr); }
}
#home-brands .pagelinks h3 { font-size: 1.25rem; margin-bottom: 6px; }
#home-brands .pagelinks p { font-size: .9375rem; color: var(--muted); }

/* 종류 카드는 사진 자산이 없다 — 브랜드 카드와 섞이지 않게 별도 타일로 둔다.
   카드 높이가 제각각이면 줄이 어긋나 보이므로 최소 높이를 맞춘다(실측에서
   "것" 한 글자가 다음 줄로 넘어가며 카드 하나만 길어지는 문제가 있었다). */
.pagelinks--text {
  display: grid; gap: clamp(16px, 2vw, 24px);
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
}
.pagelinks--text a {
  display: flex; flex-direction: column; justify-content: center;
  min-height: 132px; padding: 24px 26px;
}
.pagelinks--text h3 { font-size: 1.125rem; margin-bottom: 8px; }
.pagelinks--text p { font-size: .9375rem; color: var(--muted); }

/* ── 카테고리 페이지: 본문 + 사이드박스 2열 ── */
.split { display: grid; grid-template-columns: 1fr; gap: 28px; margin-top: 8px; }
@media (min-width: 860px) { .split { grid-template-columns: 1fr 280px; } }
.sidebox {
  border: 0; border-radius: var(--radius-card);
  padding: 18px 20px; margin-bottom: 16px; background: var(--surface);
}
.sidebox h3 {
  margin: 0 0 10px; padding-bottom: 8px;
  color: var(--accent); border-bottom: 2px solid var(--accent);
}
.sidebox ul { margin: 0; padding-left: 18px; font-size: .875rem; color: var(--muted); }
.sidebox li { margin: 6px 0; }
.sidebox a { color: inherit; text-decoration: none; }
.sidebox a:hover { color: var(--fg); }

/* ── 글 페이지 하단 태그 ── */
.tag {
  display: inline-block; font-size: .8125rem;
  background: var(--surface-2); color: var(--muted);
  border: 0; border-radius: 99px;
  padding: 4px 12px; margin: 2px 6px 2px 0;
}


/* ── 발행 전 체크리스트 노트 — 카드가 아니라 한 줄 메모로 가볍게 둔다. ── */
.todo-box {
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 8px; padding: 10px 14px; margin: 16px 0;
  font-size: .8125rem; color: var(--muted);
}
.todo-box::before { content: "준비중 · "; font-weight: 700; color: var(--accent); }

/* site.footer.links — 카테고리 19개라 푸터도 전체 사이트맵 역할을 한다.
   shared 가 아니라 여기 두는 이유: shared 를 건드리면 전 사이트 CSS 해시가 바뀌어
   손대지 않기로 한 사이트(nntkorea 계열)의 산출물까지 같이 움직인다. */
/* site.footer.links (선택) — 전체 메뉴를 한 줄로 까는 사이트맵 성격의 링크.
   카테고리가 많아 색인 유입을 푸터에서도 만들어야 하는 사이트에서만 채운다. */
.footer__links { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 20px; }
.footer__links a { color: var(--muted); text-decoration: none; font-size: .8125rem; }
.footer__links a:hover { color: var(--fg); }
