/* IA v2 (SAYO_IA_V2) 전용 스타일시트.
 *
 * 이 파일은 플래그가 ON 인 빌드에서만 dist 로 나가고, 그때만 두 셸
 * (base.html · app/base.html) 의 head 에 <link> 로 실린다. `style.css` 에는 한
 * 바이트도 넣지 않는다 - 그 파일은 OFF 산출물의 정본이라 거기 규칙을 더하면
 * "OFF 빌드 바이트 무변" 오라클이 그 자리에서 죽는다. 고양이 `cat/` · 셸
 * `app-shell/` 과 같은 처분이다.
 *
 * 규율 둘:
 *   - 모든 선택자는 `body.ia-v2` 아래로 스코프한다. 스코프 밖 규칙은 OFF
 *     산출물에는 안 실리지만, ON 에서 v2 가 아닌 표면(법무·어드민 셸)까지
 *     번질 자리를 만든다.
 *   - 값은 `var(--...)` 토큰만 쓴다 (DESIGN 3.6). 색·간격 리터럴 금지.
 */

/* [U2-0] 플래그 스코프. 규칙은 후속 유닛이 채운다. */
body.ia-v2 {
}

/* [impeccable critique 2026-09-24 접근성] 건너뛰기 링크는 잉크 바탕 위 바탕색
 * 글자인데(`style.css` `.skip-link`), 전역 `a:hover` 가 hover 때 글자를
 * `--accent` 로 바꿔 #4f46e5 on #18181b = 2.8:1 로 떨어졌다. hover·focus 에서도
 * 글자를 바탕색으로 고정한다(잉크/바탕 짝 = 본문 대비). `style.css` 는 OFF
 * 정본이라 무접촉 - OFF 산출물에는 같은 결함이 남는다(PR 본문). */
body.ia-v2 .skip-link:hover,
body.ia-v2 .skip-link:focus,
body.ia-v2 .skip-link:focus-visible {
  color: var(--bg);
  text-decoration: none;
}

/* [U2-1 · 가안 2026-09-19] 경량 크롬 헤더 - 로고(좌) + [로그인]·[시작하기](우).
 *
 * 탭 레일(`nav.site-nav-links`)도 로그인 UI 도 ON 에서는 DOM 에 없으므로
 * `style.css` 의 그 규칙들은 무효화할 대상 자체가 없다(부재로 닫는다).
 * 여기서 잠그는 것은 남은 세 요소가 좁은 폭에서도 한 줄을 유지하는 것과,
 * 우측 둘 중 [시작하기] 만 "채운 버튼" 으로 읽히는 것 둘뿐이다 [실측: 360px
 * 에서 한 줄, 아래 텍스트 링크·채운 버튼 블록].
 */
body.ia-v2 .site-header-lite .site-header-inner,
body.ia-v2 .site-header-lite .site-header-actions {
  flex-wrap: nowrap;
}

/* [IA-V2 HEADER] 상단바 = 지면 위에 얹힌 얇은 유틸리티 줄.
 *
 * Kevin 2026-09-19: "상단바 역할이 바뀌었는데 색깔이랑 두께랑 왜 다 그대로냐".
 * `style.css` 의 `.site-header-bar` 값(흰 fill · 하단 1px 선 · sticky · 글래스
 * blur)은 5탭 레일과 로그인 UI 를 이고 있던 **내비 띠**의 값이다. v2 공개면에
 * 남은 것은 로고 하나와 앱 진입 둘뿐이라, 그 자리는 띠가 아니라 줄이다.
 * 띠를 지우면 지면이 상단에서 끊기지 않고 그대로 이어져 올라간다.
 *
 * 왜 각 선언이 필요한가 - `style.css` 에서 되살아나는 것들을 하나씩 닫는다:
 *   - `position` : `.site-header-bar` 가 `sticky; top: 0; z-index: 50`.
 *     static 으로 내리면 z-index 도 같이 무의미해진다(별도 선언 안 한다).
 *   - `padding`  : `@media (min-width: 720px)` 의 `.site-header { padding-top:
 *     28px }` 가 `.site-header-bar { padding: 0 }` 보다 **뒤에** 있어 이긴다.
 *     그 28px 이 데스크톱 90.8px 의 주범이다.
 *   - `background`·`border-bottom` : 기본 블록 + `@supports` 글래스 블록 +
 *     `prefers-reduced-transparency` 블록 셋이 같은 두 속성을 쓴다. 전부
 *     `.site-header-bar`(0,1,0) 라 이 (0,2,0) 규칙 하나가 셋을 같이 덮는다.
 *   - `backdrop-filter` : 배경을 투명으로 내려도 blur 는 계속 걸린다. 지면이
 *     상단에서 흐려지면 "얹혀 있다" 가 아니라 "덮여 있다" 로 읽힌다.
 *
 * 높이는 두 단이다: <768 은 56px, >=768 은 64px. 768 은 새 폭 체계가 아니라
 * `style.css` 가 이미 쓰는 단이다(`@media (min-width: 768px)` 의 quiet-card
 * 2열 그리드). 안쪽 내용 실측은 이 상한 아래다 - 최대치는 >=641 의 워드마크
 * 줄 상자(22px 글자 + 위아래 --space-3) 약 50px 과 CTA 36px 이고, coarse
 * pointer/<=640 에서는 M-3 탭 표면 규칙이 CTA 를 44px 로 올리므로 56px 안에
 * 12px 여유로 앉는다.
 *
 * 좌우 거터는 **안 건드린다**. `.site-header-inner` 의 880px/18px 는 전역
 * `main` 규칙의 거울이라 이미 본문과 같은 자리에서 시작한다.
 */
body.ia-v2 .site-header-lite {
  position: static;
  height: 56px;
  padding: 0;
  background: transparent;
  border-bottom: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

@media (min-width: 768px) {
  body.ia-v2 .site-header-lite {
    height: 64px;
  }
}

/* [헤더 우측 가안 2026-09-19] 텍스트 [로그인] + 채운 [시작하기].
 *
 * 값은 전부 tokens.css 의 기존 토큰이다 - 새 팔레트 0.
 *
 * **이 블록은 9/19 오전의 고스트 버튼 규칙을 대체한다.** 그 판의 근거는 "헤더가
 * 화면에서 가장 무거운 덩어리가 되면 안 된다" 였고 그래서 면색을 뺐다. 같은 날
 * 확정된 가안은 그 근거를 뒤집지 않는다 - 무게를 나누는 방법을 바꿀 뿐이다:
 * 요소는 둘인데 **채운 면은 하나**이고, 다시 온 사람이 찾는 [로그인] 은 텍스트로
 * 물러난다. 강조가 하나라는 계약은 그대로고, 그 하나가 이제 할 일을 말한다.
 *
 * [로그인] 은 쉬는 색이 `--sub` 다. 헤더의 다른 텍스트 링크(`style.css` 의
 * `.site-nav-links a` · `.nav-app-open`)가 쓰는 바로 그 값이라 유틸리티 줄의
 * 리듬이 한 벌로 남고, hover 에서 `--ink` 로 **진해지는** 여지도 거기서 나온다
 * (쉬는 색이 이미 `--ink` 면 더 진해질 칸이 없다). 밑줄은 없다 - 있으면 이 줄에서
 * 유일하게 남는 밑줄이 된다(`.nav-app-open` 이 같은 이유로 지운다).
 *
 * [시작하기] 는 `--primary` 3종이다. `--accent` 를 직접 쓰지 않는 이유는 다크에서
 * accent 가 밝은 인디고(#a5b4fc)라 흰 글자가 안 서기 때문이다 - `--primary-
 * foreground`(= `--accent-ink`)가 라이트 #ffffff · 다크 #0d0b09 로 갈려 두 테마
 * 대비를 한 선언으로 지킨다. OFF 팔의 채운 브랜드 액션(`.nav-search-pill`)이 쓰는
 * 것과 같은 짝이다.
 *
 * radius 는 `--radius-pill` 이다. 알약은 채운 면을 전제한 모양이고(고스트 판의
 * 주석이 같은 말을 반대편에서 했다), 가안 시안의 `btn-fill` 도 알약이다.
 *
 * 높이 36px 는 유틸리티 줄의 값이고(v1 40px), coarse pointer 의 44px 탭 표면
 * 규칙(`style.css` 의 단 하나뿐인 M-3 블록 안 `.nav-app-open` 선언)이 그 위에
 * 그대로 얹히므로 모바일 히트 영역은 44px 로 남는다 - `.nav-app-cta` 에
 * `min-height` 를 여기서 선언하지 않는다(선언하면 그 접근성 상수를 덮는다).
 *
 * [F안 2026-09-21 Kevin "시작하기 버튼이 있는데 로그인 버튼은 왜 있는거야"]
 * 이 자리에 있던 [로그인] 텍스트 링크 3규칙(기본·hover·focus-visible)과 아래
 * coarse pointer 44px 블록을 **함께** 걷어 냈다 - 템플릿에서 그 링크가
 * 사라졌으므로 규칙만 남기면 아무것도 안 그리는 선언이 된다. 액션 줄에 남는
 * 것은 채운 CTA 하나뿐이라 44/36 혼재도 같이 사라진다.
 */
body.ia-v2 .site-header-lite .nav-app-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  height: 36px;
  /* [레이아웃 체계 2026-09-23] 버튼 한 벌의 sm 규격(아래 [레이아웃 체계] 절의
   * `.hero-cta-primary` 가 lg). 종전엔 폰에서 `style.css` M-3 블록의
   * `.nav-app-open` 의 `min-height: 44px` 가 이 36 을 이겨 80x44 로 로고(45)만큼
   * 컸다. 눈에 보이는 상자는 36 으로 두고 44px 탭 표면은 아래 `::before` 가
   * 따로 진다(접근성 상수는 그대로, 시각 크기만 sm). */
  min-height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--radius-md);  /* [Quiet Ledger K1 2026-10-23] 필 버튼 폐지 - r8 */
  background: var(--primary);
  color: var(--primary-foreground);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: none;
  transition: background-color 120ms ease-out;
}

/* 탭 표면 44px. 상자는 36 이라 위아래로 4px 씩 보이지 않는 히트 영역을 더한다
 * (헤더 줄 56/64 안에 들어간다 - 상자 top 10 -> 히트 top 6). */
body.ia-v2 .site-header-lite .nav-app-cta::before {
  content: "";
  position: absolute;
  inset: -4px 0;
}

body.ia-v2 .site-header-lite .nav-app-cta:hover {
  background: var(--primary-hover);
  /* `style.css` 의 `.nav-app-open:hover { color: var(--accent) }` 가 글자를
   * 브랜드색으로 튀게 만든다 - 채운 면 위에서 그러면 글자가 면에 묻는다. */
  color: var(--primary-foreground);
}

body.ia-v2 .site-header-lite .nav-app-cta:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

/* 두 요소 사이 간격. `style.css` 의 `.site-header-actions` 는 `--space-2`(8px)
 * 인데, 그 값은 pill·앱 열기·아바타 **넷**이 한 줄에 서던 OFF 팔의 리듬이다.
 * 여기는 둘뿐이고 하나는 채운 면이라, 한 단 벌려야 텍스트 링크가 버튼에
 * 달라붙지 않는다. 8 -> 12 는 기존 토큰 안의 한 칸이다(새 값 0).
 * [F안 2026-09-21] ON 액션 줄이 [시작하기] 하나로 줄었지만 이 gap 은 남긴다 -
 * 로고와 CTA 사이 리듬이 그대로여야 한다(ON 전용 셀렉터, OFF 무관). */
body.ia-v2 .site-header-lite .site-header-actions {
  gap: var(--space-3);
}

/* [IA-V2 LANDING] 랜딩 히어로 상하배열 - 태그라인 -> 리드 -> 검색창 -> 데모.
 *
 * Kevin 2026-09-17 "9k1 처럼 태그라인-검색창-데모 상하배열" + 승인 시안
 * (`ui-critique-20260917/landing-layouts.html` (A) 안, "랜딩 시안 굿").
 *
 * 템플릿은 무접촉이다 - `about.html` 의 DOM 순서가 이미 h1 -> 리드 ->
 * `.hero-search-col`(폼) -> `<aside class="search-preview hdemo">` 다. 2열은
 * `style.css` 의 `.about-hero .about-hero-grid`(2열 그리드)가 만들고 900px
 * 이하에서는 이미 1열로 접힌다. 그러니 여기서 할 일은 그 그리드를 **전 폭에서**
 * 1열로 고정하는 것 하나다 - 데스크톱이 모바일과 같은 순서로 읽힌다.
 *
 * 열 폭은 검색창 폭에 맞춘다: `style.css` 의 `.hero-search-col` 상한
 * 640px [실측] 이 이 지면의 측정 폭이고, 말(제목·리드)과 데모 카드가 그
 * 폭을 공유해야 세 요소가 한 기둥으로 읽힌다. 640 은 토큰에 없는 값이라
 * 리터럴을 흘리는 대신 이 파일 스코프의 변수 하나로 든다(DESIGN 3.6 의
 * "값은 var() 로" 를 지키는 방법 - 새 전역 토큰을 만들지는 않는다).
 */
body.ia-v2 {
  --ia-v2-hero-col: 640px;
}

body.ia-v2 .about-hero .about-hero-grid {
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: var(--space-6);
}

body.ia-v2 .about-hero .about-hero-main,
body.ia-v2 .about-hero .search-preview.hdemo {
  width: 100%;
  max-width: var(--ia-v2-hero-col);
}

/* 한 기둥이 되었으므로 말은 가운데 정렬이다(2열 시절의 왼쪽 정렬은 오른쪽
 * 카드와 짝을 이루던 조형이다). `style.css` 가 2열을 위해 리드·검색창의
 * 자동 여백을 0 으로 눌러 뒀으므로(`.about-hero .about-hero-grid .hero-lede`
 * 계열) 그것부터 되돌린다 - 안 되돌리면 `text-align: center` 가 리드
 * 상한폭(560px) **안에서만** 글을 가운데로 옮겨, 640px 기둥 안에서 리드
 * 덩어리 자체가 왼쪽에 붙은 채로 남는다. 물리 롱핸드로 쓴다 - 되돌릴 선언이
 * `margin-left`/`margin-right` 라 같은 축으로 겹쳐야 캐스케이드가 자명하다. */
body.ia-v2 .about-hero .about-hero-main {
  text-align: center;
}

body.ia-v2 .about-hero .about-hero-grid .hero-lede,
body.ia-v2 .about-hero .about-hero-grid .hero-search-col {
  margin-left: auto;
  margin-right: auto;
}

/* [리뷰 #6(b)·K4(b) 2026-09-23] 히어로 뒤 방사형 그라디언트 밴드. Kevin
 * K4 확정: "(a) 창 본문 --surface #FFF + 사이드바 #F1F1F3 유지, 그리고
 * (b) 히어로 뒤 radial-gradient 밴드 둘 다" - 사진/일러스트 배경(Aside 식
 * 방향 전환)은 채택하지 않는다.
 *
 * 값은 리뷰 수치 그대로다(`radial-gradient(120% 80% at 50% 0%, …)`)이되
 * 색은 리터럴이 아니라 토큰이다: 밝은 중심 = `--accent-soft`(#eef0fe),
 * 바깥 = `--bg`(#f7f7f8 - 페이지 배경과 같은 값이라 밴드 경계가 눈에 안
 * 띈다). 높이는 리뷰 지시("창 상단에서 +200px 까지")의 고정값 근사다 -
 * 창 상단 y 는 헤드라인 줄바꿈·CTA 유무에 따라 뷰포트마다 흔들리므로 장식
 * 배경에 그 정밀도가 필요 없다(실측 1440x900 창 상단 y≈280대, +200⇒약
 * 480~530 - 560px 로 여유를 둔다).
 *
 * `::before` + 음수 z-index - 절대배치 장식층이 히어로 텍스트·창 아래로
 * 깔린다(정적 자식은 스태킹 순서상 음수 z-index 위다, 표준 배경층 기법). */
body.ia-v2 .hero.about-hero {
  position: relative;
}

body.ia-v2 .hero.about-hero::before {
  content: "";
  position: absolute;
  /* [레이아웃 체계 2026-09-23] 위쪽도 경계가 없게 - 가장 짙은 중심이 히어로
   * 윗변(= 헤더 아랫변 y 56/64)에 있어 그 자리에 가로 한 줄이 섰다. 밴드를 헤더
   * 높이(>=768 의 64, 폰에서는 8px 더 올라가 화면 밖)만큼 끌어올려 중심이 페이지
   * 맨 위에 오게 한다. 헤더는 투명 줄이라([IA-V2 HEADER]) 그 아래로 비친다. */
  inset: -64px 0 auto 0;
  height: 560px;
  /* [레이아웃 체계 2026-09-23] 종전 `radial-gradient(120% 80% …, --bg 60%)` 는
   * 가로 반지름이 상자 폭의 120% 라 좌우 끝에서 아직 `--bg` 에 못 닿았다 -
   * 실측 1440: x=178/1262 에서 (245,246,250) 대 바깥 (247,247,248), 딱딱한
   * 사각 경계가 보였다. 가로 반지름을 상자 절반(50%)으로 줄여 **좌우 끝에서
   * 정확히 0** 이 되게 하고, 바깥 색을 `--bg` 가 아니라 `transparent` 로 둔다 -
   * 어떤 배경 위에서도 끝이 지면과 같은 값이라 경계가 원리적으로 안 생긴다. */
  background: radial-gradient(50% 80% at 50% 0%, var(--accent-soft) 0%, transparent 100%);
  z-index: -1;
  pointer-events: none;
}

/* [IA-V2 FOLD] 첫 화면 완결 - 상단바 높이로 재도출한 2구간.
 *
 * `style.css` 의 `[FOLD-COMPLETE]` 블록(94/61/89)은 5탭 레일이 있던 OFF
 * 헤더의 값이다. `style.css` 는 한 바이트도 안 만진다(OFF 정본) - 그래서 ON
 * 값은 여기서 `body.ia-v2` 스코프로 얹는다.
 *
 * [재도출 2026-09-19] 이전 판은 50/62/90 세 단이었다. 그 값은 v1 상단바를
 * 폭 스윕으로 **실측**해서 내림한 것이고, 그 블록 끝줄이 "헤더 높이가 또
 * 바뀌면 이 세 값을 다시 실측해야 한다" 고 적어 뒀다. 위 [IA-V2 HEADER] 가
 * 바로 그 변경이다. 이제 상단바 높이는 실측이 아니라 **선언값**이라 재도출은
 * 스윕이 아니라 그 선언을 그대로 읽는 것이다: <768 은 56px, >=768 은 64px.
 * 단이 셋에서 둘로 준 것도 같은 이유다 - 641/721 은 v1 헤더가 폭에 따라 3단
 * 으로 접히던 자리였고, 선언 높이에는 그 접힘이 없다.
 *
 * 방향 규율은 그대로다: 가정 <= 실측(과소추정)만 안전하다. 뺄셈 값이 작을수록
 * 히어로가 커져서 다음 절이 폴드 밖으로 더 밀린다. 여기서는 가정 == 선언값
 * 이므로 등호로 닿는다(오라클은 `>=` 로 잰다).
 *
 * vh 를 먼저, svh 를 뒤에 선언한다 - svh 미지원 브라우저는 뒤 선언이 무효라
 * 앞선 vh 가 캐스케이드에 남는다(선언 **순서**가 계약이다).
 *
 * 상단바 높이가 또 바뀌면 이 두 값과 경계 폭을 같이 옮겨야 한다.
 */
body.ia-v2 .hero.about-hero {
  min-height: calc(100vh - 56px);
  min-height: calc(100svh - 56px);
}
@media (min-width: 768px) {
  body.ia-v2 .hero.about-hero {
    min-height: calc(100vh - 64px);
    min-height: calc(100svh - 64px);
  }
}

/* [IA-V2 HERO SCALE] 표제 한 단 축소 + 히어로 상단 여백 축소.
 *
 * 승인 시안 (A) 안 6항 (2): "헤드라인 48px 상당(현 62px 축소) + 히어로 상단
 * 여백 축소 -> 1280x800 첫 화면에 데모 카드 전체 + 입력 도크까지".
 *
 * [계획서 정정] 62px 은 전역 `.hero-title`(`style.css` 의 clamp 36~62) 이고,
 * **랜딩** 은 이미 `.about-hero .hero-title` 로 clamp 32~52 로 내려와 있다
 * [실측: 1280 에서 52px]. 그래서 이 유닛의 실제 델타는 52 -> 48 이다.
 * 상한만 낮추는 것이 목적이므로 규칙을 641px 이상으로 스코프한다 - 모바일
 * (<=640) 의 26px 은 승인 시안의 폰 캔버스가 그대로 든 값이라 건드리지
 * 않는다(시안 `.phone .m-h1` 환산 ~26px). 전 폭에 걸면 clamp 하한 32px 이
 * 모바일 표제를 26 -> 32 로 **올려** 시안과 어긋나고, 360px 폭에서 표제가
 * 3행으로 접혀 데모 카드의 폴드 수납 여유를 그만큼 먹는다
 * [실측 2026-09-18, 360px 에서 리터럴 clamp 시 h1 67.6 -> 83.2 = +15.6px].
 *
 * 4vw 는 641~1200 구간을 잇는 기울기다 - 1200px 에서 48px 에 닿고 그 위는
 * 상한에 고정된다.
 */
@media (min-width: 641px) {
  body.ia-v2 .about-hero .hero-title {
    font-size: clamp(32px, 4vw, 48px);
  }
}

/* 상단 여백: `style.css` 가 <=640 에 --space-4(16px), >=641 에 --space-6(24px)
 * 을 준다. ON 은 641 이상에서만 --space-2(8px) 로 줄인다 - 모바일은 이미
 * 데모 카드가 폴드 안에 수납돼 있어(오라클 5) 회수할 이유가 없고, 데스크톱만
 * 16px 을 되찾아 데모 카드 + 입력 도크를 첫 화면에 넣는다. 새 토큰 0.
 *
 * 721 이상에서는 `style.css` 의 수직 센터링(`margin-top/bottom: auto`)이
 * 회수분의 절반을 다시 위아래로 배분한다 - 도크가 폴드에 닿을 만큼 빡빡해지면
 * 여기서 한 단 더 줄이는 대신 그 센터링 블록을 다시 읽어야 한다(값만 깎으면
 * 첫 화면이 상단 앵커로 되돌아간 것처럼 보인다). */
@media (min-width: 641px) {
  body.ia-v2 .hero.about-hero {
    padding-top: var(--space-2);
  }
}

/* [U2-7 APP-MODE] 앱 모드 - 공개 URL 문서 위 셸 레일 (계획서 §3.5).
 *
 * 술어는 CSS 가 아니라 head 프리페인트(`_app_mode_prepaint.html`)가 진다: 로그인
 * 세션 키가 있으면 `<html>` 에 `app-mode` 가 붙고 여기 규칙이 그 클래스로 선다.
 * 그래서 이 블록이 `panel.css` 가 아니라 이 파일에 산다 - `panel.css` 는
 * `panel.mjs` 부트가 늦게 심는 링크라 **프리페인트가 될 수 없다**. 패널 자신의
 * 상자(`.sp-overlay` 기하)는 그쪽이, 지면이 비켜 앉는 일은 여기가 한다.
 *
 * 값은 전역 토큰만 쓴다. `--sp-*` 는 `panel.css` 의 `.sayo-panel` 스코프라 이
 * 시점에 정의되지 않았다(빈 var() 는 조용히 상속색이 된다) - 그 파일이 읽는
 * **전역 원본**(`--panel`·`--line`)을 같은 값으로 읽고, 그 미러 관계는 레일
 * 기하 미러 테스트가 panel.css 에서 유도해 잠근다(파일명은 여기 적지 않는다 -
 * 앞선 판이 엉뚱한 파일을 가리켰고, 출하되는 바이트는 옮겨 다니는 좌표를 들고
 * 있어야 할 이유가 없다).
 * 리터럴 둘은 선언한다: z-index 59(= `.sp-overlay` 의 60 바로 아래, 도킹
 * 자리표시자가 70 아래 69 를 쓴 것과 같은 규율)와 <1024 탭 표면 44px
 * (손가락 최소 표면은 팔레트가 아니라 접근성 상수다, WCAG 2.5.5).
 */

@media (min-width: 1024px) {
  /* 레일 폭은 `panel.css .sayo-panel.sp-page` 의 그리드 트랙과 **같은 문자열**
   * 이어야 한다(미러 테스트 대상). 패널이 `--sp-side-w` 를 재정의하면 본문
   * 패딩이 같이 따라간다. */
  html.app-mode body.ia-v2 {
    padding-left: var(--sp-side-w, 280px);
  }
  /* 레일 표면 자리표시자. `panel.css` 가 도착하기 전 한 프레임을 메운다 -
   * 도킹의 `style.css body.sayo-shell.sp-prepaint::after` 와 같은 처분이고,
   * 색도 같은 방식으로 실제 사이드바(`.sp-page .sp-side` = 배경 `--sp-inset` ·
   * 우측 경계 `--sp-border`)가 읽는 전역 토큰을 그대로 읽는다. */
  html.app-mode body.ia-v2::before {
    content: "";
    position: fixed; top: 0; left: 0; bottom: 0;
    width: var(--sp-side-w, 280px);
    z-index: 59;
    background: var(--panel); border-right: 1px solid var(--line);
  }
  /* [U2-7c] 고양이가 걸어 들어가지 않을 좌측 구간. 레일은 z 60 이고 고양이는
   * z 49 라, 레일이 덮은 좌측 280px 로 들어간 고양이는 그냥 안 보인다(고양이를
   * 레일 위로 올리는 안은 기각이다 - 사이드바 바닥 계정 행이 클릭 표면이라 그
   * 위에 앉으면 클릭을 먹는다, 계획서 §3.7). `cat.mjs` 가 이 변수 하나를 읽어
   * 보행 구간·착지·홈·리사이즈 클램프의 좌측 경계를 한꺼번에 옮긴다.
   *
   * 변수가 없는 면(플래그 OFF · 비앱모드 · <1024 드로어 폭)에서는 고양이가
   * 0 을 읽어 **현행 그대로** 움직인다 - 부재로 닫는다.
   *
   * 폭 표현은 위 본문 패딩·자리표시자와 **같은 것**이다. 셋이 한 값이어야
   * 고양이가 서는 경계와 레일의 경계가 픽셀로 맞는다. 값을 여기서 따로 쓰지
   * 말고 그 둘이 옮겨가면 같이 따라가게 둔다. */
  html.app-mode body.ia-v2 {
    --sayo-cat-inset-left: var(--sp-side-w, 280px);
  }
  /* [U2-7 K-3] 데스크톱 앱 모드의 공개 헤더는 사라진다 - 사이드바가 유일한
   * 내비다(푸터는 법적 고지라 유지, DESIGN D4). Kevin 실견 뒤 뒤집을 수 있게
   * **독립 1규칙**으로 격리한다: 되돌리기는 이 블록 한 덩이 삭제다. */
  html.app-mode body.ia-v2 .site-header {
    display: none;
  }
  /* [뉴스피드 머리 2026-09-19 Kevin 실견] 위 규칙이 헤더를 지우면 본문 기둥의
   * 상단 여백이 **0 이 된다** - `style.css` 의 `main` 은 `padding: 0 18px …`
   * 라 위쪽을 안 잡고, 헤더가 잡아 주던 리듬은 방금 사라졌다. 그래서 스크롤
   * 영역 맨 위 요소가 천장에 붙어 잘린 것처럼 보였다(뉴스피드 피처드 카드).
   *
   * 여백을 `.explore-lead` 나 `.value-bar` 에 주지 않는 이유: 그 바는
   * localStorage 로 닫히는 dismissible 이라, 한 번 닫은 사용자에게는 여백도
   * 같이 사라진다. 지면마다 첫 요소가 무엇인지도 다르다. 상단 리듬은 첫
   * 요소가 아니라 **본문 기둥**이 져야 지면이 바뀌어도 같은 값으로 선다.
   *
   * 값은 DESIGN.md §4 밀도 리듬이 열거한 토큰에서 고른다(섹션 경계 = 48px).
   * 그 목록은 중간 밀도(--space-8)로 수렴하지 말라고 못 박는다 - 새 토큰도,
   * px 리터럴도 없다.
   *
   * 1024 미만은 이 블록 밖이라 무접촉이다: 그 폭에서는 공개 경량 헤더가
   * 그대로 서서 같은 리듬을 이미 잡고 있다(부재로 닫는다). */
  html.app-mode body.ia-v2 #main {
    padding-top: var(--space-12);
  }
}

@media (max-width: 1023px) {
  /* 드로어 폭에서는 본문을 밀지 않고(위 규칙이 1024 스코프라 부재로 닫힌다)
   * 공개 경량 헤더가 그대로 선다. 대신 `.sp-overlay > .sp-sidetoggle` 이
   * 좌상단 44px 고정 탭이라 헤더 로고와 겹친다 - 그 폭만큼 헤더 안쪽을 민다.
   * 헤더 상자가 아니라 `-inner` 를 미는 이유: 헤더의 배경·경계는 전폭으로
   * 남아야 하고 움직여야 하는 것은 로고부터의 내용뿐이다. */
  html.app-mode body.ia-v2 .site-header-inner {
    padding-left: 44px;
  }
}

/* [IA-V2 E-HERO] 랜딩 히어로 E안 - 입력창이 화면 세로 중앙에서 시작해,
 * 타이핑 후 전송하며 열 바닥으로 도킹한다 (E-PLAN §1·§5·§6.2).
 *
 * 파일 **끝**에만 더한다 - 위 블록들은 병행 유닛(헤더 CTA)이 만지는 범위라
 * 텍스트 충돌을 범위 분리로 피한다.
 *
 * ── 이 블록의 중심 결정: 레이아웃은 **항상 최종(도킹) 상태**다 ──────────────
 *
 * 히어로 안 열은 `h1 -> 리드 -> 스레드(aside) -> 컴포저(맨 아래)` 한 벌이고,
 * 휴지(타이핑 전) 상태는 그 위에 `transform: translateY(...)` 만 얹어 컴포저를
 * 화면 세로 중앙으로 올린 것이다. 이동이 transform 뿐이라:
 *   - CLS 가 0 이다. 레이아웃 속성(grid-template-rows·flex-grow·margin)을
 *     애니메이션하면 전송 시점에 컴포저가 200px 넘게 움직이는 **레이아웃 시프트**
 *     가 되고, 랜딩은 SEO 지면이라 그 비용을 지지 않는다.
 *   - JS 없음 · `prefers-reduced-motion: reduce` · 구형 브라우저에서 정적 규칙이
 *     곧 최종 프레임이다. 폴백을 따로 설계하지 않는다 - **기본값이 폴백**이다.
 *
 * 그래서 이 블록의 게이트 밖(아래) 규칙에는 휴지 상태를 만드는 값
 * (`opacity: 0` · `visibility: hidden` · 음수 `translateY`)이 **한 줄도 없다**.
 * 그 값들은 전부 `about.html` 의 키프레임 0% 에서만 나오고, 그 키프레임은
 * `html.js-demo` **와** `@media (prefers-reduced-motion: no-preference)` 이중
 * 게이트 뒤에서만 결속된다. 전역 `reduce { * { animation: none } }` 는 보조일
 * 뿐 이 유닛은 거기 기대지 않는다(animation 을 죽여도 정적 상태는 못 죽인다).
 *
 * 스코프: `.about-hero-grid.hdemo-e` 는 `about.html` 이 `ia_v2 AND 검색 ON AND
 * 데모 세트 존재` 일 때만 찍는다. 그래서 위 [IA-V2 LANDING] 의 1열 그리드
 * 오버라이드는 **무접촉으로 남는다** - 검색 킬스위치가 내려간 ON 빌드나 데모
 * 픽스처가 없는 빌드는 여전히 그 규칙이 있어야 h1·리드가 왼쪽 열로 치우치지
 * 않는다. 아래 flex 블록은 더 특정하고 더 뒤에 있어 그 둘이 같이 매칭될 때만
 * `display`·`gap` 을 덮는다.
 */

/* 열 자체를 flex column 으로 바꿔 컴포저의 `margin-top: auto` 가 자유 공간을
 * 받게 한다. 히어로는 `min-height` 만 있고 `height` 가 없어 grid `fr` 트랙은
 * 자유 공간을 나누지 못하지만(높이 부정), 히어로가 flex column 이라
 * `flex: 1 1 auto` + `margin-top: auto` 는 그 공간을 받는다. */
body.ia-v2 .about-hero .about-hero-grid.hdemo-e {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  flex: 1 1 auto;
  margin-top: 0;
  /* [F안 2026-09-21] 창은 640 기둥보다 넓다 - 위 [IA-V2 LANDING] 이 `.about-hero-main`
   * 과 `.search-preview.hdemo` 에 건 `max-width: 640` 은 그 둘에만 걸리고 창에는
   * 안 걸린다(창은 새 요소다). 그리드 자신은 폭 상한이 없어야 창이 1200 까지 편다. */
  width: 100%;
}

/* h1·리드와 창 사이 간격. Kevin 2026-09-21 "데모 영역 존나 크니까" - 말과 창
 * 사이를 한 단 줄여 창이 첫 화면에서 최대한 크게 선다. 리드 아래 `--space-5`
 * 는 기존 토큰 안의 값이고 새 상수가 아니다. */
body.ia-v2 .about-hero-grid.hdemo-e .about-hero-main {
  /* [레이아웃 체계 2026-09-23] 20(`--space-5`, 스케일 밖) -> 32. 폰 값은
   * 아래 [레이아웃 체계] 절. */
  margin-bottom: var(--space-8);
}

/* 스레드는 카드가 아니라 대화다 - 테두리·그림자·모서리를 걷는다(E-PLAN §11
 * 결정 3). 남는 것은 글과 여백뿐이고, 그게 /search/ 실면과 같은 결이다. */
body.ia-v2 .about-hero-grid.hdemo-e .search-preview.hdemo {
  margin-top: var(--space-6);
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  overflow: visible;
}

body.ia-v2 .about-hero-grid.hdemo-e .hdemo-body {
  padding: 0;
}

/* 말풍선 회색은 **새 색이 아니라 토큰**이다 - `--panel` 이 라이트/다크 양쪽에
 * 이미 정의돼 있어(`tokens.css`) 다크 모드가 자동으로 따라온다. */
body.ia-v2 .about-hero-grid.hdemo-e .hdemo-user {
  background: var(--panel);
  color: var(--ink);
  font-weight: 500;
  font-size: 14px;
}

/* 컴포저의 휴지 위치. 도킹 자리는 창 본문의 flex 바닥이고(스크롤러가
 * `flex: 1` 이라 남는 높이를 전부 먹는다), 휴지 상태는 키프레임이
 * `--hdemo-rest-shift` 만큼 위로 옮긴 것이다.
 *
 * ── [F안 2026-09-21] 중앙 계산이 **창 기준**으로 닫힌다 ─────────────────
 * E안은 뷰포트(`50svh`)를 기준으로 삼았다. 이제 컴포저가 뷰포트가 아니라
 * **창 본문** 안에 있으므로 기준도 창이다. 창은 `height` 를 명시로 들고
 * 있어서(`--hdemo-win-min-h`) 미지수가 하나도 없다 - svh 폴백 두 줄 선언도
 * 필요 없어졌다.
 *
 * 창 본문 안 좌표(본문 상단 기준, H = `--hdemo-win-min-h`(고정 창 높이 - 아래
 * [F-WINDOW] 절 참조, 창은 더 안 자란다), h = 머리줄 높이, F = 컴포저 자신의
 * 높이):
 *   도킹 상단 y_dock = H - F
 *   휴지 목표 상단 y_rest = h + (H - h)/2 - F/2   (머리줄 아래 영역의 세로 중앙)
 *   translateY = y_rest - y_dock = (h - H)/2 + F/2
 * `translate` 의 `%` 는 자기 박스 높이 기준이라 F/2 = 50% 로 얻는다.
 * 부호가 음수(위로)인 것은 H > h 이기 때문이고, 값이 커지면 창이 커진 만큼
 * 휴지 컴포저도 위로 간다 - 그래서 `--hdemo-win-min-h` 를 키울 때 "휴지 컴포저가
 * 첫 화면 안" 오라클을 다시 돌려야 한다.
 *
 * [리뷰 #7·K8 2026-09-23 재도출] 창 상단에 `--hdemo-win-chrome-h`(36px)
 * 크롬 줄이 생기면서, 사이드바+본문 행이 차지하는 실제 높이는 더 이상 H
 * 전체가 아니라 M = H - C(C = chrome 높이)다(그리드 행이
 * `chrome-h minmax(0,1fr)` 이라 본문 행이 나머지 전부를 먹는다). 위 식의
 * H 를 M 으로 바꿔 다시 닫는다:
 *   translateY = (h - M)/2 + 50% = (h - H + C)/2 + 50%
 * C 가 0 이면(크롬이 없던 종전 판) 식이 정확히 원래 식으로 돌아간다 -
 * 이 재도출이 이전 계약을 깨지 않는 초집합이라는 뜻이다. */
/* [컴포저 즉시 전환 2026-09-23 Kevin 실견] `transition` 이 없다. 종전에는
 * 여기 `transition: transform 320ms cubic-bezier(.4, 0, .2, 1)` 을 얹어
 * 포커스 스냅(아래 `:focus-within` 이 `animation: none` 으로 키프레임을
 * 걷는 순간)에도 컴포저가 320ms 에 걸쳐 미끄러졌다 - 전송 도킹과 같은 결함이
 * 둘째 길로 새고 있었다(실측: 포커스 후 0/50/100/200ms 프레임에 서로 다른
 * translateY 가 잡혔다). 실 셸은 컴포저가 애초에 움직이지 않으므로(창
 * 바닥에 상시 고정) 전환 자체가 없다 - 여기서도 없앤다. 포커스 스냅은 이제
 * 키프레임이 걷히는 즉시(1 프레임) 최종 자리로 선다. */
body.ia-v2 .about-hero-grid.hdemo-e .hero-search-col {
  --hdemo-rest-shift: calc((var(--hdemo-win-head-h) - var(--hdemo-win-min-h) + var(--hdemo-win-chrome-h)) / 2 + 50%);
}

/* 고양이 띠. [F안 2026-09-21] 겹침 지점이 옮겼다 - 컴포저가 히어로 바닥이
 * 아니라 **창 바닥**에 있으므로, 좌하단 고양이와 부딪히는 것은 창의 아래
 * 모서리다. 띠는 그대로 둔다(같은 값이 같은 일을 한다: 히어로 바닥에 고양이
 * 상수보다 큰 여백을 둬 그 위 요소를 밀어 올린다). 겹침 0 은 헤드리스 오라클이
 * 창 기준으로 다시 잰다.
 * 원 주석: 폰 폭에서 도킹 컴포저(640 기둥 중앙)가 좌하단 고양이와 겹친다 -
 * 고양이는 z 49 클릭 표면이라 입력창 위에 앉으면 탭을 먹는다. `cat.mjs` 는
 * 한 바이트도 안 만진다(모듈 URL 이 내용 해시라 한 글자만 바꿔도 **OFF 전
 * 공개 페이지**의 HTML 이 바뀐다) - 대신 히어로 바닥에 고양이 상수보다 큰
 * 띠를 둬 컴포저를 그 위로 올린다.
 *
 * 80px 은 리터럴이 아니라 토큰 합(`--space-16` 64 + `--space-4` 16)이고,
 * `cat.mjs` 의 `SIZE`(60) + `EDGE_GAP`(16) = 76 보다 크다. 그 상수가 커지는
 * 날 정적 테스트가 red 다. 900 은 새 폭 체계가 아니라 `style.css` 의 기존
 * 2열 접힘 단이다 - 실제 겹침 구간은 792 이하이고 792~900 은 띠가 남을 뿐이다. */
@media (max-width: 900px) {
  body.ia-v2 .hero.about-hero:has(> .about-hero-grid.hdemo-e) {
    padding-bottom: calc(var(--space-16) + var(--space-4));
  }
}

/* [리뷰 #5 2026-09-23] 폭 기준 통일 - 헤더·창·섹션·푸터가 같은 컨테이너
 * 폭을 공유한다(실측: 로고 x=298 vs 창 x=120, 178px 어긋남 -> 0).
 *
 * `--landing-col` 은 아래 `.hdemo-win` 의 폭 공식과 **정확히 같다**
 * (`min(1200px, 88vw, 100vw - 168px, 50vw + 484px)`, 고양이 레인 근거는 [F-WINDOW] 절 주석).
 * 값을 두 곳에 따로 적으면 한쪽만 늙는 날이 온다.
 *
 * 정렬 원리: 자식이 `width`(max-width 아님 - main 의 padding 이 이 폭보다
 * 좁을 때도 그 폭을 그대로 유지해야 창처럼 넘쳐 나갈 수 있다) + `margin-
 * inline:auto` 를 가지면, 부모(`main`)가 몇 px 든(880 이든 1120 이든)
 * 자식의 중심은 **뷰포트 중심**과 같아진다 - 부모 자신이 `margin:0 auto`
 * 로 뷰포트에 중앙 정렬돼 있으므로 대수적으로 상쇄된다(중첩된 두 중앙
 * 정렬 상자는 안쪽 상자가 바깥쪽보다 넓어도 같은 점에 중심이 잡힌다 -
 * `main` 의 padding·max-width 를 하나도 안 건드려도 되는 이유다).
 * `.hdemo-win` 은 이미 이 원리로 뷰포트에 정중앙이다(플렉스 열의
 * `align-items:center`가 같은 대수적 상쇄를 만든다); 아래 규칙들은 같은
 * 원리를 일반 block 요소에 그대로 적용한다.
 *
 * 헤더·푸터는 전 페이지 공용 include(`_site_header.html`·`base.html`
 * footer)라 셀렉터를 `body:has(.hero.about-hero)` 로 **랜딩에만** 좁힌다 -
 * 안 좁히면 이 폭이 검색·카드 등 다른 ia_v2 지면 헤더까지 번진다(`main`
 * 자신의 폭이 지면마다 다르므로 헤더가 지면마다 출렁인다). */
body.ia-v2 {
  /* [레이아웃 체계 2026-09-24 Kevin Aside 비교 + 머지 게이트 #1449 A1] 창 폭 =
   * 뷰포트의 88%, 상한 1200, 그리고 **고양이 레인 두 항**(아래 [F-WINDOW] 절
   * 주석의 유도). 88vw 단독은 1024~1366 에서 창 좌변이 고양이 우변보다 안쪽이라
   * 고양이(z 49 클릭 표면)가 창 사이드바에 앉았다(리뷰 실측 1024x768 14.6px). */
  --landing-col: min(1200px, 88vw, 100vw - 168px, 50vw + 484px);
}

body.ia-v2:has(.hero.about-hero) .site-header-inner,
body.ia-v2:has(.hero.about-hero) .landing-latest,
body.ia-v2:has(.hero.about-hero) .landing-dir,
body.ia-v2:has(.hero.about-hero) .site-footer {
  width: var(--landing-col);
  max-width: var(--landing-col);
  margin-inline: auto;
  padding-inline: 0;
}

/* [레이아웃 체계 2026-09-23 Kevin "크기/간격/배열이 엉망"] 위 주석의 "대수적
 * 상쇄" 는 **틀렸다** - 실측 1440x900: 창 120/1320, "지금 살펴볼 사건" 178/1378
 * (58px 어긋남). 블록 자식이 부모 콘텐츠 상자(`main` 1120 - 패딩 36 = 1084)보다
 * 넓으면 `margin-inline: auto` 는 과잉 제약이 되어 auto 가 0 으로 풀리고, 자식은
 * 부모 **왼쪽**에 붙어 오른쪽으로 넘친다. 창만 가운데였던 것은 플렉스 교차축
 * 정렬(`align-items: center`)이 넘치는 자식도 양쪽으로 가르기 때문이다.
 * 헤더·푸터는 `main` 밖(`body` 직계 흐름)이라 부모가 뷰포트 폭이어서 멀쩡했다.
 *
 * 그래서 `main` 안의 두 섹션은 음수 여백으로 **직접** 가운데에 세운다:
 * 여백 = (부모 폭 - 컨테이너 폭) / 2. `%` 여백은 부모 콘텐츠 폭 기준이라 부모가
 * 좁든 넓든 식 하나로 닫힌다(1440: (1084 - 1200)/2 = -58 -> 120/1320, 390:
 * (354 - 358)/2 = -2 -> 16/374). 헤더·창·섹션·푸터 좌우 가장자리가 한 값이다
 * - 헤드리스 잠금(`tests/test_landing_layout_system.py`)이 ±1px 로 잰다. */
body.ia-v2:has(.hero.about-hero) .landing-latest,
body.ia-v2:has(.hero.about-hero) .landing-dir {
  margin-left: calc((100% - var(--landing-col)) / 2);
  margin-right: calc((100% - var(--landing-col)) / 2);
}

/* ══════════════ [레이아웃 체계] 간격 스케일 · 버튼 한 벌 ══════════════
 *
 * 간격은 4/8/12/16/24/32/48/64/96 아홉 값만 쓴다(96 = `--space-16` +
 * `--space-8`, 토큰에 96 이 없어 합으로 낸다). `--space-5`(20)·`--space-10`(40)
 * 은 이 랜딩의 배치 간격에 쓰지 않는다.
 *
 * 히어로 세로 리듬(헤더 아래부터). >=768 은 Kevin 2026-09-24 Aside 비교 목표값:
 *                 >=768   641~767   <=640
 *   헤더 -> h1     96        8         16   (히어로 padding-top)
 *   h1 -> 서브     24       12          8   (`.hero-lede` margin-top)
 *   서브 -> CTA    32       24         24
 *   CTA -> 창      64       32         24
 * 종전 실측(1440): 8 · 12 · 0(인라인 `<a>` 가 리드 바로 아래 줄에 붙었다) · 20.
 * 768 미만은 폴드 수납·고양이 회피 수학이 걸린 폭이라 한 단 촘촘하게 둔다.
 * 헤드리스 잠금이 네 간격이 스케일 집합에 드는지 잰다.
 *
 * 창 아래: 창 -> "지금 살펴볼 사건" 96(위 [리뷰 #16] 규칙), 섹션 머리 -> 내용 24,
 * 섹션 -> 섹션 64, 마지막 섹션 -> 푸터 96. */

/* 버튼 한 벌 - lg. 헤더 [시작하기](sm, 위 [헤더 우측 가안] 규칙)와 같은 모양
 * (알약 · 같은 글꼴 · 700)이고 크기만 다르다: sm 36/14/좌우 16, lg 48/16/좌우 24.
 * 종전엔 `style.css` `.cta-primary` 의 `--radius-md`(8) 각진 사각 + 600 이라
 * 헤더 알약과 다른 물건으로 읽혔다(실측 1440: 헤더 80x36 r999 700 · 히어로
 * 95x46 r8 600). `style.css` 는 OFF 정본이라 무접촉 - 이 시트에서 덮는다. */
body.ia-v2 .about-hero .hero-cta-primary {
  display: flex;
  width: max-content;
  height: 48px;
  min-height: 48px;
  margin: var(--space-6) auto 0;
  padding: 0 var(--space-6);
  justify-content: center;
  border-radius: var(--radius-md);  /* [Quiet Ledger K1] */
  font-family: inherit;
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
}

@media (max-width: 640px) {
  body.ia-v2 .about-hero-grid.hdemo-e .about-hero-main {
    margin-bottom: var(--space-6);
  }
}

/* 표제: 굵기 800 -> 600, 자간 -0.02em(모든 폭). >=768 은 줄 간격 1.1 + 크기 상한
 * 48 -> 56(1440 에서 56, 3.9vw 기울기). Kevin 2026-09-24: "헤드라인이 너무
 * 무겁다" - Aside 는 중간 굵기. `다` 강조색(`.u-all`)은 그대로다. */
body.ia-v2 .about-hero .hero-title {
  font-weight: 600;
  letter-spacing: -0.03em;  /* [Quiet Ledger 2026-10-23] display = 600 / -0.03em */
}

@media (min-width: 768px) {
  body.ia-v2 .about-hero .hero-title {
    font-size: clamp(32px, 3.9vw, 56px);
    line-height: 1.1;
  }

  body.ia-v2 .hero.about-hero {
    padding-top: calc(var(--space-16) + var(--space-8));
  }

  body.ia-v2 .about-hero .hero-lede {
    margin-top: var(--space-6);
  }

  body.ia-v2 .about-hero .hero-cta-primary {
    margin-top: var(--space-8);
  }

  body.ia-v2 .about-hero-grid.hdemo-e .about-hero-main {
    margin-bottom: var(--space-16);
  }
}

/* 재생은 창이 4분의 1 이상 보일 때 시작한다(about.html 자동 스크롤 스크립트의
 * IntersectionObserver 가 `html.hdemo-go` 를 붙인다). 그 전에는 모션 게이트 뒤
 * 키프레임이 0 초에 멈춰 **휴지 프레임**에 서 있다 - 창이 폴드 아래에서 혼자
 * 재생을 끝내 버리는 일이 없다. 이 규칙도 이중 게이트(`html.js-demo` + 모션
 * 미디어) 뒤라 모션 축소·JS 없음 경로에는 닿지 않는다. */
@media (prefers-reduced-motion: no-preference) {
  html.js-demo:not(.hdemo-go) body.ia-v2 .about-hero-grid.hdemo-e *,
  html.js-demo:not(.hdemo-go) body.ia-v2 .about-hero-grid.hdemo-e *::after {
    animation-play-state: paused;
  }
}

/* 답변 밀도(Kevin 2026-09-24 "문서가 아니라 차분한 제품 화면"): 칩 본문 줄
 * 간격 1.7 -> 1.6. 크기(14)·내용은 그대로다. */
body.ia-v2 .hdemo-win-thread .attn-chip-body {
  line-height: 1.6;
}

/* 컨트롤 줄의 작은 전송 원(28)에 맞춘 아이콘. */
body.ia-v2 .hdemo-win-main .rs-searchbox-submit svg {
  width: 16px;
  height: 16px;
}

/* 섹션 머리 -> 내용 24. "지금 살펴볼 사건" 은 `.landing-rhythm` 의 20(`--space-5`),
 * "둘러보기" 는 제목 아래 16 이었다 - 두 섹션이 같은 값을 쓴다. */
body.ia-v2:has(.hero.about-hero) .landing-rhythm {
  margin-top: var(--space-6);
}

body.ia-v2:has(.hero.about-hero) .landing-dir .home-section-title {
  margin: 0 0 var(--space-6);
}

/* 섹션 -> 섹션 64(종전 48). 마지막 섹션 -> 푸터 96: `main` 의 바닥 패딩
 * (`--space-16` + `--space-12` = 112)이 스케일 밖이라 랜딩에서만 96 으로. */
body.ia-v2:has(.hero.about-hero) .landing-latest + .landing-dir {
  margin-top: var(--space-16);
}

body.ia-v2 main:has(.hero.about-hero) {
  padding-bottom: calc(var(--space-16) + var(--space-8));
}

/* 둘러보기 = B 칩(위 [F-WINDOW] 절 배지·칩 규격 주석). 종전 타일(최소 56 · 패딩
 * 16/20 · r10 · 16px/700)을 칩 규격으로 내린다. 44 는 탭 표면 하한과 같은 값이다. */
body.ia-v2:has(.hero.about-hero) .dir-card {
  min-height: 44px;
  height: 44px;
  padding: 0 var(--space-4);
  gap: var(--space-2);
  border-radius: var(--radius-pill);
}

body.ia-v2:has(.hero.about-hero) .dir-card:focus-visible {
  border-radius: var(--radius-pill);
}

body.ia-v2:has(.hero.about-hero) .dir-card-name {
  font-size: 14px;
  font-weight: 600;
}

body.ia-v2:has(.hero.about-hero) .dir-card-icon svg {
  width: 16px;
  height: 16px;
}

/* [리뷰 #16 2026-09-23] 창과 다음 섹션 간격 25px - [전체 사건 보기] 버튼이
 * 창 그림자(`box-shadow` blur 60, [F-WINDOW] 절 `.hdemo-win`) 안에 들어가
 * 있었다(실측: 창 bottom 1400 -> "지금 살펴볼 사건" 1425). 96px 는 blur
 * 반경 60 + 여유 36 이고, 토큰 합으로 낸다(`--space-16` 64 + `--space-8`
 * 32 = 96, 리터럴 아님). `main` 의 `margin: var(--space-6) 0 0`(24px, 전
 * 사이트 공용) 을 landing 스코프에서만 웃돈다. */
body.ia-v2:has(.hero.about-hero) .landing-latest {
  margin-top: calc(var(--space-16) + var(--space-8));
}

/* `.hdemo-win` 의 폭 공식이 `≤720` 에서 갈리므로(`calc(100vw - 32px)`,
 * 고양이 레인 회피가 그 폭에서는 무의미해진다 - 위 [F-WINDOW] 절 주석)
 * 기준 컨테이너도 같은 지점에서 같이 갈린다. */
@media (max-width: 720px) {
  body.ia-v2 {
    --landing-col: calc(100vw - 32px);
  }
}


/* ══════════════ [F-WINDOW] 제품 창 (ON 전용) ══════════════
 *
 * Kevin 2026-09-21 실견 반려: "데모를 Aside 처럼 실제 프로덕트 화면으로 하고
 * 크게 가져가라고". E안은 **페이지**를 대화로 만들었고 이 유닛은 그 대신
 * 헤드라인 아래에 앱 창 하나를 세운다 - 사이드바 + 채팅 본문, 데모는 그 창
 * 안에서 돈다.
 *
 * ── 값의 출처 ────────────────────────────────────────────────────────
 * 창 안의 셸 모사값은 전부 `web/static/panel/panel.css` 에서 옮겨 온 것이다.
 * 그 파일은 여기 실리지 않는다 - 셸이 부팅할 때 `panel.mjs` 가 `<link>` 를
 * 심는 파일이라 랜딩(about.html)의 `<head>` 에는 없다. 그래서 클래스를
 * 재사용하지 않고(이름만 같고 스타일 0 이 된다) **값만** 옮기고 이름은
 * `hdemo-win-*` 로 새로 든다. 옮긴 값과 출처:
 *   사이드바 폭 280         panel.css:815-826 `--sp-side-w, 280px`
 *   네비 행 40 / gap 10     panel.css:835-847
 *   활성 행 좌측 2px 레일   panel.css:1094-1098 `inset 2px 0 0 var(--sp-accent)`
 *   그룹 라벨 10px/.12em    panel.css:1078-1084
 *   본문 리딩 폭 760        panel.css:58 `--sp-read-w`
 *   컴포저 카드 r22 / 그림자 panel.css:1030-1046
 * 색은 `--sp-*` 별칭을 거치지 않고 전역 토큰을 바로 쓴다(panel.css:21-58 이
 * 그 별칭을 전역 토큰에 묶어 둔 것과 같은 값이다) - 별칭은 panel.css 에만
 * 사는 이름이라 여기서 쓰면 정의가 없다.
 *
 * ── 답변 본문은 진짜 재사용이다 ──────────────────────────────────────
 * `.attn-head`/`.attn-chips`/`.badge-*`/`.cite`/`.fig` 는 `style.css` 에 있고
 * 이 지면에 이미 실려 있다. 창 안 기업 블록은 카드 상세와 **같은 CSS 한 벌**을
 * 쓴다(Kevin 2026-09-21 "이벤트 카드처럼 보기 좋게"). 아래 규칙은 그 블록을
 * 창 축척으로 줄이는 것뿐이고 모양을 새로 만들지 않는다.
 *
 * ── 창 높이 ──────────────────────────────────────────────────────────
 * `--hdemo-win-min-h` 한 값이 **휴지** 창 높이이자 휴지 컴포저의 중앙 계산
 * 입력이다(최종 창 높이는 내용이 정한다). 첫
 * 화면을 넘겨도 되지만(409.png 의 Aside 랜딩도 그렇다) **휴지 컴포저는 반드시
 * 첫 화면 안**이라, 값을 키울 때는 헤드리스 오라클(휴지 컴포저 bottom <=
 * vh - 24)을 다시 돌려야 한다. */
body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win {
  /* [리뷰 #5·K5 실측 재도출 2026-09-23] 250 -> 280. K5 히어로 CTA
   * (`.hero-cta-primary`, 46px)가 새로 생겨 창 상단이 그만큼 아래로
   * 밀렸다 - 1280x800 reduced-motion(최종/도킹 프레임) 실측: 도킹
   * 컴포저가 `top 737.28 · bottom 827.28` 로 폴드(vh 800)를 27px
   * 스트래들했다(`tests/test_landing_fold_completeness.py::
   * test_fold_completeness_ia_v2` red). 30px 를 더 빼(창 높이 550->520,
   * clamp 하한에 닿는다) 도킹 bottom 을 797.28 로 내려 스트래들을 없앤다.
   * 하한(520)에 닿아도 무해하다 - clamp 자체가 "이 밑으로는 안 줄어든다"
   * 는 계약이고, 그 계약은 안 바꿨다. */
  /* [레이아웃 체계 2026-09-23 재도출] 280 -> 318, 하한 520 -> 480. 히어로 세로
   * 리듬을 스케일로 맞추며(서브 -> CTA 0 -> 24, CTA -> 창 20 -> 32, CTA 46 -> 48)
   * 창 상단이 38px 내려갔다 - 같은 38 을 빼 1280x800 도킹 컴포저 바닥 자리를
   * 종전 그대로(폴드 안) 둔다. 하한도 같은 만큼 내려야 800 높이에서 뺄셈이
   * 실제로 먹는다(520 에 막히면 창이 안 줄고 컴포저가 폴드를 넘는다). */
  /* [레이아웃 체계 2026-09-24 Kevin Aside 비교] 창은 **폴드를 넘는다** - 첫
   * 화면에는 창 윗부분이 서고, 컴포저는 폴드 아래일 수 있다(Aside 구도). 폴드
   * 안 수납이 목적이던 위 두 재도출은 이 식으로 대체된다. 높이는 뷰포트에서
   * 180 을 뺀 값(1440x900 -> 720 상한, 1280x800 -> 620)이라 창 윗변이 첫
   * 화면 중간쯤일 때 창의 4분의 1 이상이 보여 재생 시작 조건(교차율 0.25,
   * about.html 자동 스크롤 스크립트)이 로드 직후 선다. */
  --hdemo-win-min-h: clamp(520px, calc(100svh - 180px), 720px);
  --hdemo-win-head-h: 46px;
  /* [레이아웃 체계 2026-09-24 Kevin Aside 비교] 사이드바 = 창 폭의 20%, 최소
   * 220(종전 셸 값 280 고정 = 1200 창의 23%, Aside 19.5%). 그리드 트랙의 `%` 는
   * 창(그리드 컨테이너) 폭 기준이다. */
  --hdemo-win-side-w: max(220px, 20%);
  /* [리뷰 #7·K8 2026-09-23] 창 상단 36px 크롬(신호등 대신 중립 점 3개 +
   * 가운데 주소 알약). 전용 그리드 행 하나를 더해 사이드바·본문 위에
   * 전 폭으로 얹는다 - 실제 브라우저 창처럼 크롬이 사이드바까지 덮는다.
   * 값이 리터럴 px 인 이유는 Aside 참조 실측이 그 값이고(리뷰 #7), 토큰
   * 표에 36px 짜리가 없다 - `--hdemo-win-head-h`(46px)도 같은 이유로
   * 리터럴이다(F-WINDOW 절 값은 전부 panel.css 실측 이식). */
  --hdemo-win-chrome-h: 36px;
  /* [레이아웃 체계 2026-09-23] 리딩 칼럼 거터(실 셸 `.sp-msgs`/`.sp-form`
   * `padding: 0 24px`). <=720 에서 16 으로 갈린다(아래 좁은 지면 절). */
  --hdemo-col-pad: var(--space-6);
  /* [레이아웃 체계 2026-09-24 Kevin Aside 비교] 리딩 칼럼 = 콘텐츠 640 + 거터
   * 둘(= 상자 688). 답변(상한 680)과 컴포저(상한 640)가 **같은 칼럼**이어야
   * 하므로(발주 4항) 둘 중 작은 640 을 콘텐츠 폭으로 쓴다. */
  --hdemo-col-w: calc(640px + var(--hdemo-col-pad) * 2);
  display: grid;
  grid-template-columns: var(--hdemo-win-side-w) minmax(0, 1fr);
  grid-template-rows: var(--hdemo-win-chrome-h) minmax(0, 1fr);
  grid-template-areas: "chrome chrome" "side main";
  /* 폭 = `min(1200px, 88vw, 100vw - 168px, 50vw + 484px)`. 뒤 두 항이 **고양이 레인**이다.
   * `cat.mjs` 의 홈은 `main`(상한 1120) 왼쪽 여백의 한가운데이고, 여백이
   * `SIZE + EDGE_GAP*2`(92) 에 못 미치면 좌하단 구석(left 16)으로 떨어진다
   * (`marginHome`/`defaultCatPosition`). 그래서 고양이 우변은
   *   구석(vw < 1304):  16 + 60 = 76
   *   여백 홈(vw >= 1304): (vw - 1120)/4 + 30   (1440 -> 110 · 1920 -> 230)
   * 이고, 창 좌변 `(100vw - W)/2` 가 그 우변 + 여유 8 보다 커야 겹침이 0 이다:
   *   구석:   W <= 100vw - 2*(76 + 8)                  = 100vw - 168px
   *   여백 홈: W <= 100vw - 2*((vw-1120)/4 + 30 + 8)   = 50vw + 484px
   * [머지 게이트 #1449 A1] 88vw 단독 판은 이 두 항이 없어 1024~1366 에서 고양이가
   * 창 사이드바(와 그 아래 "내 리서치" 링크)에 앉았다. 고양이는 유지하고 **창이
   * 비켜 준다** - `cat.mjs` 는 모듈 URL 이 내용 해시라 한 바이트도 못 만진다.
   * 잠금: `tests/test_landing_layout_system.py::
   * test_the_cat_never_covers_a_link_or_the_window`(1024~1920 스윕). */
  width: min(1200px, 88vw, 100vw - 168px, 50vw + 484px);
  /* [창 높이 고정 2026-09-23 Kevin 실견, F안 5차 반전] `height` 다(`min-height`
   * 가 아니다).
   *
   * Kevin: "데모 높이가 왤캐 길어? 실제 화면처럼 하고 답변이 길어서 잘리면
   * 스크롤로 하자 답변나오면서 저절로 스크롤다운되는식으로."
   *
   * F안 5차(2026-09-21)는 정반대로 갔다 - 창이 내용만큼
   * 자라 폴드 아래로 이어지고, 스레드 자리는 `grid-template-rows: 0fr -> 1fr`
   * 로 접혔다 펴지는 레이아웃 트릭이었다. 그 구도가 "실제 화면 같지 않다" -
   * 실 앱(`panel.css`)은 셸 창 높이가 전송 전후로 **변하지 않는다**, 스레드만
   * `.sp-msgs` 의 `overflow-y: auto` 로 내부 스크롤되고 컴포저는 창 바닥에 상시
   * 고정이다. 그래서 창은 다시 고정 높이(`--hdemo-win-min-h` 가 이제 곧 유일한
   * 창 높이다 - 이름은 유지한다, 아래 휴지 shift 식이 이 변수를 그대로 쓴다)이고,
   * 스레드 자리(`.hdemo-win-scroll`)가 실 셸처럼 `overflow-y: auto` 를 되찾는다.
   * 답변 도착 중 바닥으로 자동 스크롤하는 것은 about.html 의 인라인 스크립트
   * (page_js 블록, "창 스레드 자동 스크롤" 주석) 몫이다. */
  height: var(--hdemo-win-min-h);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl, 16px);
  background: var(--surface);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .10), 0 2px 6px rgba(0, 0, 0, .04);
  overflow: hidden;
  text-align: left;
}

/* [리뷰 #7·K8 2026-09-23] 창 상단 크롬 줄. 중립 회색 점 3개(신호등 대신 -
 * K8 확정: "웹 제품이라 macOS 색 신호등보다 중립 회색 점과 URL 이 더
 * 정직하다") + 가운데 주소 알약. 순수 장식(`about.html` 이 `aria-hidden`
 * 을 단다) - 사이드바·본문과 같은 행이 아니라 그 **위** 전 폭 행이다. */
body.ia-v2 .hdemo-win-chrome {
  grid-area: chrome;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  height: var(--hdemo-win-chrome-h);
  padding: 0 var(--space-3);
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}

/* 점 3개는 좌측에 고정 - 알약은 가운데(justify-content:center) 그대로 둔다.
 * absolute 가 아니라 margin-right:auto 로 왼쪽에 붙이면 알약의 진짜 가운데
 * 정렬이 점들의 폭만큼 밀리므로, 점을 `position:absolute; left` 로 크롬
 * 줄 자체에서 빼 알약의 중앙 정렬을 건드리지 않는다. */
body.ia-v2 .hdemo-win-dots {
  position: absolute;
  left: var(--space-3);
  display: inline-flex;
  gap: 6px;
}

body.ia-v2 .hdemo-win-dots i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--line-strong);
}

body.ia-v2 .hdemo-win-addr {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--sub);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* 사이드바. 배경은 `--panel` 이다 - 셸이 `--sp-inset`(= `--panel`) 로 본문과
 * 사이드바를 가르는 그 한 단계다. */
body.ia-v2 .hdemo-win-side {
  grid-area: side;
  display: flex;
  flex-direction: column;
  min-height: 0;
  padding: var(--space-3) var(--space-2) var(--space-2);
  background: var(--panel);
  border-right: 1px solid var(--line);
}

body.ia-v2 .hdemo-win-nav {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

body.ia-v2 .hdemo-win-navrow {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 40px;
  padding: 0 10px;
  border-radius: var(--radius-md, 10px);
  color: var(--ink);
  font-size: 14px;
  text-decoration: none;
}

body.ia-v2 .hdemo-win-navrow svg {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--sub);
}

body.ia-v2 .hdemo-win-navrow:hover {
  background: var(--surface);
}

body.ia-v2 .hdemo-win-new {
  font-weight: 600;
}

/* 세션 목록. 가짜 사용자 데이터가 아니라 출하 중인 예시 질문이라 길이가 제각각
 * 이다 - 실제 셸처럼 한 줄로 자른다. */
body.ia-v2 .hdemo-win-sessions {
  /* [F안 5차] flex-basis 가 **0** 이다. `auto` 면 목록의 내용 높이(7행 + 그룹
   * 라벨 둘)가 사이드바를 밀어 올려 휴지 창이 `min-height` 보다 커진다
   * [실측 1280: 창 641.8 vs min-height 550] - 그러면 휴지 컴포저의 중앙 계산이
   * 그만큼 어긋난다(식은 `min-height` 를 입력으로 쓴다). 기준 0 이면 목록은
   * 남는 자리만 차지하고, 넘치는 끝은 아래 마스크가 맺는다. 창이 자란 뒤
   * (최종)에는 자리가 넉넉해 목록이 다 보인다. */
  flex: 1 1 0;
  min-height: 0;
  overflow: hidden;
  margin-top: var(--space-2);
  /* 목록이 사이드바보다 길어 마지막 행이 **한가운데서 잘린다** [실측]. 실제
   * 셸은 그 자리가 스크롤이지만 창은 정지 화면이라 스크롤바를 낼 수 없다 -
   * 끝을 잘린 글자가 아니라 스미는 마스크로 맺는다(잘린 글자는 고장으로
   * 읽히고, 스미는 끝은 "아래에 더 있다"로 읽힌다). */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 44px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 44px), transparent);
}

/* [리뷰 #9 2026-09-23] 한글 라벨에 영문 캡스 관습(자간 벌림 + 대문자 변환)을
 * 지운다 - 벌어진 한글은 "이 렇 게" 처럼 오타로 읽힌다(실측: 390px 폭에서
 * "이렇게 물어보세요" 라벨). Aside 의 섹션 라벨도 자간 없이 본문보다 살짝
 * 작고 회색일 뿐이다. 크기도 [리뷰 #10] 4단(12/13/14/16) 표에 맞춘다. */
/* [레이아웃 체계 2026-09-23] 행 기하는 실 셸 값 그대로(`panel.css`
 * `.sp-page .sp-side-group` `padding: 16px 10px 6px`, `.sp-side-session`
 * `min-height: 34px; padding: 6px 10px; font-size: 14px`). 줄 높이만 20px 로
 * 못박아 "오늘" 묶음의 높이가 글꼴 메트릭과 무관한 선언값(42 + 34 = 76)이
 * 된다 - 아래 `--hdemo-win-today-h` 가 그 값을 쓴다. */
body.ia-v2 .hdemo-win-group {
  height: 42px;
  margin: 0;
  padding: 16px 10px 6px;
  color: var(--sub);
  font-size: 12px;
  font-weight: 600;
  line-height: 20px;
  letter-spacing: 0;
  text-transform: none;
}

/* [리뷰 #10] 13.5px 는 토큰 표 밖의 반 px 값이다 - 4단(12/13/14/16) 중
 * 13(사이드바 행·세션·각주 목록)으로 내린다. */
body.ia-v2 .hdemo-win-session {
  height: 34px;
  margin: 0;
  padding: 0 10px;
  border-radius: var(--radius-md, 10px);
  color: var(--ink);
  font-size: 14px;
  line-height: 34px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 활성 행. 좌측 2px 액센트 레일은 border 가 아니라 inset box-shadow 다 -
 * border 로 그리면 행 폭이 2px 줄어 같은 목록의 다른 행과 글자 시작이 어긋난다
 * (panel.css:1094-1098 가 같은 이유로 같은 처방을 쓴다). */
/* [레이아웃 체계 2026-09-23] 휴지 사이드바의 "오늘" 예약 빈칸 제거.
 *
 * 종전: "오늘" 묶음은 휴지 구간에 `visibility: hidden` 으로 **자리만** 지켜
 * "내 리서치" 와 "지난 7일" 사이에 76px 빈칸이 떴다. 자리를 지킨 이유는 CLS 0
 * 이다 - 전송 순간 묶음이 흐름에 끼어들면 아래 목록이 그만큼 밀린다.
 *
 * 수리: 묶음은 흐름에 그대로 두고(레이아웃 불변), 목록 안쪽 층
 * (`.hdemo-win-sessions-in`)을 휴지 구간에만 `translateY(-76px)` 로 올린다 -
 * 숨은 묶음은 목록 상자 위로 빠져 잘리고(`overflow: hidden`), "지난 7일" 이
 * 그 자리에 선다. 전송 순간 transform 이 0 으로 스냅하며 "오늘" 이 나타난다.
 * transform 은 레이아웃 시프트가 아니다(CLS 0 유지), 창 높이·컴포저 자리는
 * 사이드바 안쪽 이동과 무관하다(#1447 불변식). 키프레임은 about.html 의
 * `hdemo-today-*`(모션 게이트 뒤), 게이트 밖 정적 값은 transform 없음 = 최종
 * 프레임이다. */
body.ia-v2 .hdemo-win-sessions {
  --hdemo-win-today-h: 76px;
}

body.ia-v2 .hdemo-win-today {
  display: block;
  height: var(--hdemo-win-today-h);
}

body.ia-v2 .hdemo-win-session.is-active {
  background: var(--surface);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--accent);
}

/* [리뷰 #1·K3 2026-09-23, 요소 삭제] 창 사이드바 바닥의 계정 자리(로그인
 * 안내 + OAuth 시늉 링크) 세 규칙을 걷는다 - 템플릿에서 그 마크업이
 * 사라졌으므로 규칙만 남기면 아무것도 안 그리는 죽은 CSS 가 된다(다음
 * 사람이 살아 있는 계약으로 읽을 자리). E 테스트 파일의
 * `test_window_account_slot_is_the_signed_out_shell_and_invents_no_user`
 * 가 그 세 클래스명이 이 파일에 한 글자도 안 남았는지 잠근다(이 주석
 * 자신도 그 클래스명을 문자 그대로 안 적는다 - 적으면 그 잠금이 이 주석
 * 자신에 걸려 넘어진다). */

/* ── 본문 ── head / 스레드 스크롤러 / 컴포저 세 층의 flex column.
 * [리뷰 #6·K4(a) 2026-09-23] 배경을 `--bg`(페이지 배경과 같은 값, 대비비
 * 1.00 실측)에서 `--surface`(흰색)로 - 창이 페이지 위에 뜬 "물건"으로
 * 읽히려면 본문 면이 배경과 갈라져야 한다. 사이드바는 `--panel` 유지
 * (Kevin K4 확정: "창 본문 --surface #FFF + 사이드바 #F1F1F3 유지"). 값은
 * 기존 --surface 토큰 그대로다 - K9(회색 온도 통일)이 다루는 색 값 자체는
 * 여기서 한 글자도 안 바꾼다. */
body.ia-v2 .hdemo-win-main {
  grid-area: main;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--surface);
}

/* [리뷰 #18 2026-09-23] 머리줄 제목과 스레드 열의 좌변이 55px 어긋난다
 * (실측: 헤드 x=425, 스레드 x=480) - 머리줄은 자기 폭 전체(창 본문 폭)에
 * `padding:0 space-6` 만 걸고, 스레드는 그 안에서 다시 `max-width:760 +
 * margin:auto` 로 한 번 더 가운데 정렬되기 때문이다(본문 폭이 760+2*gutter
 * 보다 넓을 때 스레드가 그만큼 안으로 더 들어간다). 머리줄에 **같은** 상한
 * (`760px` 콘텐츠 + 양쪽 `--space-6` 거터)과 `margin-inline:auto` 를 주면
 * 두 폭의 콘텐츠 시작 x 가 대수적으로 같아진다(border-box 라 padding 을
 * 포함한 상한을 준다 - 그래야 패딩을 뺀 콘텐츠 폭이 정확히 760 이 된다). */
body.ia-v2 .hdemo-win-head {
  display: flex;
  align-items: center;
  gap: 6px;
  height: var(--hdemo-win-head-h);
  /* [레이아웃 체계 2026-09-23] 머리줄은 창 본문 **전 폭** 띠이고 불투명하다
   * (창 본문색 `--surface`). 종전엔 내용 폭으로 줄어든 투명 상자라, 위로
   * 스크롤된 답변이 제목 양옆과 바로 아래에서 반쯤 잘린 채 비쳤다(실측 1440·390
   * 최종 프레임 "LS 006260" 행). 실 셸(`panel.css` `.sp-page .sp-head`)도 머리가
   * 스크롤 상자(`.sp-msgs`) **위 형제**라 답변이 그 뒤로 지나가지 않는다.
   *
   * 제목은 리딩 칼럼(760 = 콘텐츠 712 + 거터 둘) 한가운데다: 좌우 패딩을
   * "칼럼 바깥 여백 + 거터" 로 줘서 제목이 칼럼 폭 안에서만 말줄임된다. */
  justify-content: center;
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 0 max(var(--hdemo-col-pad), calc((100% - var(--hdemo-col-w)) / 2 + var(--hdemo-col-pad)));
  background: var(--surface);
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
}

/* 머리줄 바로 아래 24px 페이드(위 40% 는 불투명) - 스크롤 상자의 윗 모서리에서
 * 반쯤 잘린 글줄이 보이지 않고 창 본문색으로 스민다. 머리줄의 `visibility` 키프레임을 그대로
 * 상속하므로 휴지 프레임(아직 안 보낸 대화)에는 페이드도 없다. */
body.ia-v2 .hdemo-win-head::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: var(--space-6);
  background: linear-gradient(var(--surface) 40%, transparent);
  pointer-events: none;
}

body.ia-v2 .hdemo-win-title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

body.ia-v2 .hdemo-win-chev-t {
  color: var(--sub);
  font-size: 11px;
}

/* [F안 2차 2026-09-21] 빈 새 대화 화면. 실제 셸 `renderPageHero` 의 묘사이고
 * 값은 `panel.css:984-1008` 에서 옮겼다(키커 10px/.12em · 인사 30px/600 ·
 * 카드 2열 grid r12 · 599 이하 1열 + 인사 24px).
 *
 * ⚠️ 기본값이 **부재**다. 그것이 최종 프레임이자 폴백 프레임이고(재생이 끝나면
 * 대화 화면이므로 빈 상태는 없다), 게이트 안에서만 켜진다. `opacity: 0` 이나
 * `visibility: hidden` 을 게이트 밖에 쓰지 않는 이유는 이 파일의 중심 규율이다
 * (`test_ungated_e_block_never_declares_a_resting_state`) - 휴지 상태를 만드는
 * 값은 게이트 뒤에서만 나온다. `display: none` 은 컴포저 트랙이 쓰는 것과 같은
 * 처방이라 같은 계약 안에 있다.
 *
 * 절대 배치인 이유: 흐름에 넣으면 스레드와 같은 상자를 나눠 써서 전송 순간
 * 레이아웃이 움직인다(CLS). 겹쳐도 충돌하지 않는 것은 휴지 구간의 스레드가
 * `visibility: hidden` 이기 때문이다. */
body.ia-v2 .hdemo-win-empty {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  top: var(--hdemo-win-head-h);
  bottom: 0;
  width: 100%;
  max-width: var(--hdemo-col-w);
  margin: 0 auto;
  pointer-events: none;
}

/* 휴지 컴포저가 앉는 자리를 비우는 값. 컴포저 자신의 렌더 높이(90px
 * [실측: 1280·390 양쪽에서 `.rs-searchbox` h=90])의 절반 + 숨 쉴 자리다.
 * 리터럴을 흘리는 대신 창 스코프 변수로 들어서 컴포저 패딩이 바뀌면 한 줄만
 * 따라간다. 상수를 믿지는 않는다 - 헤드리스 오라클이 **겹침 0 을 직접 잰다**. */
body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win {
  --hdemo-win-composer-h: 84px;
  --hdemo-win-empty-half: calc(var(--hdemo-win-composer-h) / 2 + var(--space-4));
}

/* 두 덩이를 **컴포저 중심 기준**으로 각각 밀어낸다.
 *
 * 처음엔 세 항목(인사 · 빈칸 · 예시)을 flex column 으로 세우고 통째로
 * `justify-content: center` 했는데, 그러면 빈칸이 컴포저에 안 맞는다 [실측:
 * 390 에서 첫 예시 카드가 입력창에 깔렸다]. 이유는 산술이다: 중앙 정렬은
 * **열 전체**의 중심을 맞추므로, 위 덩이와 아래 덩이의 높이가 다르면(예시
 * 카드가 있는 아래가 더 높다) 빈칸의 중심이 열의 중심에서 어긋난다.
 *
 * 그래서 열을 가운데 맞추는 대신 두 덩이를 각자 절대 배치한다. 이 오버레이는
 * 창 머리줄 아래부터 창 바닥까지를 덮으므로 그 `50%` 가 곧 휴지 컴포저의
 * 중심이다(컴포저도 같은 구간의 세로 중앙에 선다 - 위 `--hdemo-rest-shift`
 * 식과 같은 기준면이다). 덩이 높이가 어떻게 바뀌어도 빈칸은 안 어긋난다. */
body.ia-v2 .hdemo-win-empty-top,
body.ia-v2 .hdemo-win-empty-bottom {
  position: absolute;
  /* 안쪽 여백은 **자식이** 든다. 부모에 `padding` 을 주고 자식을 `left: 0` 으로
   * 두는 안은 아무 일도 안 한다 - 절대 배치 자식의 컨테이닝 블록은 조상의
   * **패딩 박스**라 `left: 0` 이 패딩 안쪽이 아니라 바깥쪽에 붙는다.
   *
   * 실측 두 가지가 이 규칙의 근거다:
   *   · 390 에서 히어로 텍스트가 창 좌측 테두리에 붙었다(안쪽 여백 0).
   *   · 1280 에서도 컴포저보다 **24px 왼쪽**이었다 - 데스크톱이 멀쩡해 보인 것은
   *     `max-width: 760px` + 자동 여백이 상자를 이미 들여놔서 어긋남이 창 안에
   *     머물렀을 뿐이고, 정렬 자체는 그때도 깨져 있었다.
   *
   * 값은 컴포저(`.hdemo-win-main .hero-search-col`)의 좌우 패딩과 **같은 토큰**
   * 이다. 둘이 갈리면 인사·예시와 입력창의 왼쪽 끝이 어긋난다 - 오라클이
   * `left` 일치를 전 뷰포트에서 ±1px 로 잰다. */
  left: var(--hdemo-col-pad);
  right: var(--hdemo-col-pad);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

body.ia-v2 .hdemo-win-empty-top {
  bottom: calc(50% + var(--hdemo-win-empty-half));
}

body.ia-v2 .hdemo-win-empty-bottom {
  top: calc(50% + var(--hdemo-win-empty-half));
}

body.ia-v2 .hdemo-win-greet {
  margin: 10px 0 6px;
  font-size: 30px;
  line-height: 1.25;
  font-weight: 600;
  color: var(--ink);
}

body.ia-v2 .hdemo-win-hero-hint {
  margin: 0;
  font-size: 14px;
  color: var(--sub);
}

body.ia-v2 .hdemo-win-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  width: 100%;
  margin-top: 10px;
}

/* 예시 카드는 **묘사**다 - 실제 셸에서는 누르면 그 문장이 입력창에 들어가는
 * `<button>` 이지만, 창에는 그 모듈이 없으므로 `<span>` 이고 포인터도 기본
 * 커서다(누를 수 없는 것이 눌릴 수 있는 것처럼 보이면 안 된다). */
body.ia-v2 .hdemo-win-card {
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg, 12px);
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
}

/* 창 본문이 이 오버레이의 기준면이다. */
body.ia-v2 .hdemo-win-main {
  position: relative;
}

/* 스크롤러. 페이지가 아니라 **이 상자**만 스크롤되므로 답변이 창보다 길어도
 * 랜딩 레이아웃은 안 밀린다(방문자는 창 안에서 직접 스크롤할 수 있다).
 *
 * ⚠️ 정박은 위쪽이다. 처음엔 `column-reverse` 로 바닥에 정박시켰는데 - 실제
 * 채팅이 그렇게 생겼으니 - **화면이 재생 중에 빈다** [실측: 1280x800,
 * answer-mid 4.4s 에서 본문 전체가 공백]. 이유: 계단 등장은 `opacity` 만
 * 움직이므로 아직 안 뜬 블록도 **자리는 이미 차지한다**. 바닥 정박이면 그
 * 보이지 않는 꼬리가 정확히 창을 채워서, 이미 뜬 앞 블록들은 창 위로 밀려
 * 나가고 방문자는 빈 상자를 본다.
 *
 * 자리를 안 차지하게 하려면 `display`/`height` 를 움직여야 하는데 그건 곧
 * 레이아웃 시프트이고, 이 유닛은 CLS 0 을 계약으로 든다. 그래서 정박을
 * 바꾼다: 위에서 아래로 쌓이면 먼저 뜬 블록이 창 위쪽에 남고 새 블록이 그
 * 아래에 붙는다 - 읽는 방향과 같고, 재생이 끝난 정지 화면도 꼬리(근거 목록)가
 * 아니라 **요약과 첫 기업 블록**을 보여 준다(414.png 의 구도). */
/* 스레드 자리 - **다시** 스크롤 상자다(F안 5차의 반대 방향, 2026-09-23).
 *
 * Kevin: "데모 높이가 왤캐 길어? 실제 화면처럼 하고 답변이 길어서 잘리면
 * 스크롤로 하자." 창이 이제 고정 높이라(위 `.hdemo-win` `height:` 참조)
 * 내용이 창보다 길면 잘릴 수밖에 없고, 실 셸이 그 상황에서 하는 일이 바로
 * `overflow-y: auto` 다(`panel.css` 의 `.sp-msgs`). 바닥까지 자동으로 따라가는
 * 것은 이 CSS 의 일이 아니다 - CSS 는 `scrollTop` 을 애니메이션할 수 없다(값이
 * 숫자이지 스타일 프로퍼티가 아니다). about.html 의 인라인 스크립트(page_js
 * 블록, "창 스레드 자동 스크롤" 주석)가 진다. */
body.ia-v2 .hdemo-win-scroll {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* [레이아웃 체계 2026-09-23] 좌우 거터는 스크롤 상자가 아니라 칼럼(아래
   * `.hdemo-win-thread`)이 든다 - 컴포저 칼럼과 같은 상자 모양이어야 두 칼럼의
   * 좌우 끝이 같다. */
  padding: 0;
}

/* [리뷰 #19 2026-09-23, 판단] 리뷰는 "760px 는 14px 본문 기준 58자라
 * 리딩 폭 과다, 680px + 15px 로 45자" 를 제안했다. 실측 대조: 실 셸
 * (`panel.css:58` `--sp-read-w:760px`, `panel.css:947`
 * `.sayo-panel.sp-page .sp-msg{font-size:15px;line-height:1.75}`)이 정확히
 * 폭 760 · 크기 15 조합을 쓰고 있다 - 이 창은 그 셸의 축소 재현이므로
 * (about.html [F-WINDOW] 절 주석) 폭은 셸 값(760)을 지키고 **크기만**
 * 셸 값(15)으로 맞춘다(아래 `.hdemo-prose` 15px). 리뷰의 680px 은 셸에
 * 없는 수치라 채택하지 않는다 - "실 셸 값 우선" 판단 규율(발주문)의 적용. */
/* [레이아웃 체계 2026-09-23] 답변 칼럼 = 컴포저 칼럼. 실 셸은 `.sp-msgs`·
 * `.sp-form`·`.sp-head` 셋이 **같은 상자**(`max-width: var(--sp-read-w)` 760 ·
 * 좌우 패딩 24 · `margin: 0 auto`, panel.css `.sayo-panel.sp-page` 절)라 글줄과
 * 입력창의 좌우 끝이 같다. 종전 창은 스레드가 콘텐츠 760(바깥 거터는 스크롤
 * 상자), 컴포저가 상자 760(콘텐츠 712)이라 실측 1440 에서 답변 480~1240 대
 * 컴포저 504~1216 으로 24px 씩 어긋났다. 둘을 같은 상자 모양으로 맞춘다.
 * 위 패딩 24 는 `.sp-msgs` 의 `padding-top` 과 같은 값이다. */
body.ia-v2 .hdemo-win-thread {
  width: 100%;
  max-width: var(--hdemo-col-w);
  margin: 0 auto;
  padding: var(--space-6) var(--hdemo-col-pad) 0;
}

/* 스레드 아래 숨 쉴 자리 - 마지막 블록이 스크롤 상자 바닥에 바짝 붙지 않게. */
body.ia-v2 .hdemo-win-scroll > .hdemo-win-thread > :last-child {
  margin-bottom: var(--space-4);
}

/* 스레드 안의 기존 데모 요소들 - 창 축척으로만 조정한다. */
body.ia-v2 .hdemo-win-thread .search-preview.hdemo {
  max-width: none;
  margin-top: 0;
}

body.ia-v2 .hdemo-win-thread .hdemo-prose {
  font-size: 15px;
  /* [레이아웃 체계 2026-09-24] 1.75 -> 1.6 (Kevin Aside 비교: 차분한 제품 화면) */
  line-height: 1.6;
  color: var(--ink);
}

/* 기업 블록. 카드 상세의 로스터 행 한 벌을 그대로 쓰되, 카드 지면의 `<ol>`
 * 순위 번호와 그 자리를 비우는 좌측 들여쓰기만 걷는다 - 창 안에는 각주 위첨자가
 * 곳곳에 있어서 22px 굵은 숫자가 하나 더 서면 둘이 같은 어휘로 읽힌다.
 *
 * ⚠️ `list-style: none` 만으로는 안 지워진다 [실측: 회사명 위에 회색 "1" 이
 * 겹쳐 그려졌다]. `style.css:821-833` 의 번호는 `list-style` 이 아니라
 * **카운터 + `::before` 절대배치**라, 마커 속성이 아니라 그 가상 요소를 꺼야
 * 한다. 같이 걷는 `padding-left: 40px` 은 그 번호가 서던 자리다 - 한쪽만
 * 걷으면(패딩만 0) 번호가 갈 곳을 잃고 글자 위로 올라온다. 그게 실측된 그림이다. */
body.ia-v2 .hdemo-win-firms {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
}

/* [리뷰 #14 2026-09-23] 기업 블록 사이 간격을 20px(`--space-5`, 리뷰 원문
 * 수치)로 넓힌다 - 종전 위 16 · 아래 8 비대칭은 두 번째 기업(LS) 의 시작이
 * 1px 구분선 하나에만 기대게 만들었다. 위아래를 같은 값으로 맞춰 "새
 * 기업 블록이 시작한다" 는 신호를 여백만으로도 읽게 한다. */
body.ia-v2 .hdemo-win-firm {
  padding-block: var(--space-6);
  border-top: 1px solid var(--line);
}

/* [레이아웃 체계 2026-09-23] 근거 위 이중 구분선 -> 한 줄. 카드 지면의
 * `.attention-item:last-child { border-bottom }`(style.css) 과 아래
 * `.hdemo-win-refs { border-top }` 이 약 30px 간격으로 두 줄을 그었다. 데모
 * 창에서만 목록 끝 선을 걷는다(카드 상세 무접촉) - 근거 머리의 선 하나가 남는다. */
body.ia-v2 .hdemo-win-firm:last-child {
  border-bottom: 0;
}

body.ia-v2 .hdemo-win-firm::before {
  content: none;
}

/* [리뷰 #14] 기업명 16 -> 17px/700 - 배지 4종이 5종 어휘로 몰린 자리에서
 * 위계 맨 위 글자가 배지들과 덜 섞이도록 한 단 올린다. */
body.ia-v2 .hdemo-win-firm .attn-name {
  /* [레이아웃 체계 2026-09-24] 17 -> 16 (밀도 한 단 낮춤) */
  font-size: 16px;
  font-weight: 600;
}

/* [리뷰 #14] 배지 문법을 1종(외곽선 · 의미색은 글자에만)으로 통일한다.
 * 카드 상세의 확실성 배지(`.badge-certainty-event-party`, style.css:1115)는
 * 채운 녹색이다 - 전 사이트 공용 규칙이라 **무접촉**이고, 여기서는 데모
 * 스코프에서만 연관도 배지와 같은 외곽선 모양으로 되돌린다. 글자색
 * (`--color-verified`, 초록)은 그대로 둔다 - 의미는 색이 아니라 글자가
 * 진다는 원칙은 안 바뀐다. */
body.ia-v2 .hdemo-win-firm .badge-certainty-event-party {
  background: transparent;
  border-color: var(--line-strong);
}

/* [레이아웃 체계 2026-09-23] 배지·칩 규격 2종.
 *
 *   A 라벨(누를 수 없는 표지)  높이 24 · radius `--radius-sm`(6) · 12px/600 · 좌우 8
 *     - 종목행 배지(거래 관계 확인 · 이 뉴스와 관련: 중간), [내 종목 담기](창 안에서는
 *       묘사라 누를 수 없다), 칩 라벨(왜 닿아요 · 얼마나·언제 · 주의)
 *   B 칩(누르는 것)            높이 44 · radius `--radius-pill` · 14px/600 · 좌우 16
 *     - "둘러보기" 칩(아래 [레이아웃 체계] 둘러보기 절)
 *
 * 종전 실측(1440): 배지 20 r6 · 담기 26.8 r6 · 칩 라벨 22 r999 · 둘러보기 61.5 r10
 * - 네 벌이었다. 카드 상세의 같은 클래스들(style.css)은 무접촉, 데모 창 스코프만. */
body.ia-v2 .hdemo-win-firm .badge,
body.ia-v2 .hdemo-win-firm .hdemo-win-add,
body.ia-v2 .hdemo-win-firm .attn-chip-label {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 600;
  line-height: 1;
}

/* [리뷰 #12 2026-09-23] 칩 본문 굵게를 한 단계 내린다 - 픽스처(`landing_
 * search_demo.json`)가 이미 "블록당 굵은 수치 하나" 로 줄였지만, 남은 그
 * 하나도 카드 상세와 같은 700 굵기·잉크색이면 창처럼 좁은 폭에서 여전히
 * 눈에 튄다(리뷰 원문). 카드 상세(`.attn-chip-body .fig`, style.css:5224)는
 * **무접촉**이다 - 거기는 전 사이트 공용 규칙이라 여기서 낮추면 카드 페이지
 * 본문까지 옅어진다. 데모 창 스코프에서만 600·색 상속으로 내린다. */
body.ia-v2 .hdemo-win-thread .attn-chip-body .fig {
  font-weight: 600;
  color: inherit;
}

/* [리뷰 #1·K1 2026-09-23] 내부 용어 "주목도" 표면 노출 - Kevin 확정 규칙
 * 위반(내부 명칭 표면 노출 금지). accent 색 + 화살표가 시세 신호처럼
 * 읽힌다(리뷰 원문). **데모 창에서만** 숨긴다 - 카드 상세는 이 클래스를
 * 그대로 쓰고 있어 무접촉이다(K1 결정: 삭제가 아니라 숨김, 카드 페이지는
 * 손대지 않는다). 사보타주 대조: 이 규칙을 지우면
 * `test_the_attn_c_is_hidden_inside_the_demo_window_only` 가 red. */
body.ia-v2 .hdemo-win .attn-c {
  display: none;
}

/* [내 종목 담기] 는 묘사다 - 실제 등록 버튼(`button.card-add-listing`)과 같은
 * 모양이되 `<span>` 이라 누를 수 없고, 포인터도 기본 커서로 둔다. */
body.ia-v2 .hdemo-win-add {
  cursor: default;
}

/* 각주. 카드의 `.cite` 는 포커스 가능한 팝오버 트리거인데 창 안 각주는 묘사라
 * 팝오버가 없다 - 번호만 남기고 상호작용 신호를 뺀다. 번호가 가리키는 곳은
 * 바로 아래 [근거] 목록이다. */
body.ia-v2 .hdemo-win-cite {
  cursor: default;
}

/* [리뷰 #20 2026-09-23, 재도출] 종전 처방(margin-left:3px)은 "5 와 1 이
 * 붙어 51 로 읽힌다"는 결함은 고쳤지만, 그 대가로 두 번호가 남남처럼
 * 떨어져 보였다(리뷰 "⁴ ¹" 판정). 올바른 모양은 각주 여럿이 **한
 * 덩어리**로 읽히는 "¹˒⁴" 다 - margin 대신 쉼표 구분자를 넣는다. 순서
 * 자체(오름차순)는 데이터 쪽(`landing_search_demo.json` parts 순서, P0
 * 픽스처 재작성 때 함께 정렬했다)이 이미 잠근다 - 여기서는 표시만 고친다. */
body.ia-v2 .hdemo-win-cite + .hdemo-win-cite {
  margin-left: 0;
}

body.ia-v2 .hdemo-win-cite + .hdemo-win-cite sup::before {
  content: ",";
}

body.ia-v2 .hdemo-win-refs {
  margin-top: 0;
  padding-top: var(--space-6);
  border-top: 1px solid var(--line);
}

/* [impeccable critique 2026-09-24 P1] 근거 목록은 기본 닫힘 `<details>` 다
 * (about.html 근거 블록 주석). 접힘 모양은 셸의 관례(`panel.css`
 * `.sp-think-summary`: 기본 마커 숨김 + `::after` 꺾쇠 + 열림 때 회전)를
 * 그대로 옮긴다. 꺾쇠 글리프는 창 머리줄 `.hdemo-win-chev-t` 와 같은 ▾ 다.
 * 누르는 자리라 높이 32 를 준다(창 축척 - 폰 44 는 아래 coarse 규칙). */
body.ia-v2 .hdemo-win-refs-title {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 32px;
  margin: 0;
  color: var(--sub);
  font-size: 12px;
  font-weight: 600;
  list-style: none;
  cursor: pointer;
}

body.ia-v2 .hdemo-win-refs-title::-webkit-details-marker {
  display: none;
}

body.ia-v2 .hdemo-win-refs-title::after {
  content: "\25BE";
  transition: transform .15s ease;
}

body.ia-v2 .hdemo-win-refs[open] > .hdemo-win-refs-title::after {
  transform: rotate(180deg);
}

body.ia-v2 .hdemo-win-refs-title:hover {
  color: var(--ink);
}

@media (pointer: coarse) {
  body.ia-v2 .hdemo-win-refs-title {
    min-height: 44px;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.ia-v2 .hdemo-win-refs-title::after {
    transition: none;
  }
}

body.ia-v2 .hdemo-win-reflist {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

body.ia-v2 .hdemo-win-reflist li {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: 12px;
}

body.ia-v2 .hdemo-win-refno {
  flex: none;
  min-width: 14px;
  color: var(--sub);
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}

body.ia-v2 .hdemo-win-refbody {
  display: flex;
  gap: 6px;
  min-width: 0;
  color: var(--sub);
  text-decoration: none;
}

body.ia-v2 .hdemo-win-refbody:hover {
  color: var(--ink);
}

body.ia-v2 .hdemo-win-refsrc {
  flex: none;
  color: var(--accent);
  font-weight: 600;
}

/* [impeccable critique 2026-09-24 P2] 데모 -> 앱 다리. 종전 13px 텍스트 링크는
 * 답변 끝의 유일한 행동인데 약했다 - 높이 44 · 15px 버튼형 링크로 키운다.
 * 버튼 한 벌(sm 36/14 · lg 48/16, 위 [레이아웃 체계] 절)의 알약 · 700 을 따르되
 * 채운 면은 히어로 [시작하기] 하나뿐이라 여기는 테두리 알약(고스트)이다 -
 * 채우면 창 안에서 히어로 CTA 와 주 행동을 다툰다. `style.css` `.hdemo-cta` 는
 * OFF 정본이라 무접촉, 이 시트에서 덮는다. */
body.ia-v2 .hdemo-win-thread .hdemo-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  min-height: 44px;
  margin-top: var(--space-4);
  padding: 0 var(--space-6);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);  /* [Quiet Ledger K1] */
  background: var(--bg);
  color: var(--ink);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
}

body.ia-v2 .hdemo-win-thread .hdemo-cta:hover {
  border-color: var(--accent);
  color: var(--accent);
  text-decoration: none;
}

/* ── 컴포저 ── 실제 셸 컴포저(`sp-composer-card`)의 모양: 둥근 카드, 입력 한 줄,
 * 아래 바 줄에 깊이 선택과 전송. 모드 토글([대화|관련주 검색])과 과금 안내 한
 * 줄은 **그리지 않는다** - Kevin 2026-09-21 "아랫쪽에 대화/관련주검색 구분도
 * 없애고 통일하자. '검색은 무료예요…' 이 안내도 없애고". 제품에서 곧 사라질
 * 것을 랜딩이 미리 광고하지 않는다. */
body.ia-v2 .hdemo-win-main .hero-search-col {
  width: 100%;
  max-width: var(--hdemo-col-w);
  margin: 0 auto;
  padding: 0 var(--hdemo-col-pad) var(--space-4);
}

body.ia-v2 .hdemo-win-main .rs-searchbox {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* [레이아웃 체계 2026-09-24 Kevin Aside 비교] 한 줄 입력(약 40) + 그 아래 작은
   * 컨트롤 줄(심층 · 28 원형 전송) = 카드 약 84(종전 90 + 32 전송). */
  gap: 0 8px;
  height: auto;
  padding: 6px 8px 8px 16px;
  border-radius: 22px;
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
}

/* [리뷰 #15 2026-09-23] "알약 안의 알약" - 위 `.rs-searchbox` 가 이미 r22
 * 흰 카드인데, 전역 `.rs-searchbox-input`(style.css:3031)의 `border-
 * radius:var(--radius-pill)`·`box-shadow:0 1px 2px…,0 8px 24px…` 가 여기서
 * 지워지지 않은 채 남아 있었다 - `border:0` 하나만으론 안 벗겨진다(라운드
 * 와 그림자는 별개 속성이다). 입력 줄이 평면이 되도록 셋 다 끈다. */
body.ia-v2 .hdemo-win-main .rs-searchbox-input {
  flex: 1 1 100%;
  width: 100%;
  min-width: 0;
  height: auto;
  padding: 8px 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  font-size: 15px;
}

/* [2026-09-24 Kevin] 데모 창 컴포저는 작동하지 않는 소품. Kevin: "랜딩 데모에서는
 * 입력창 실제 작동 안되게 하자 ... 실제로는 그냥 새 대화라서 헷갈림".
 * 마크업(`about.html` `_e` 팔)에서 `<form>`·`<input>`·`<button>` 이 같은 클래스의
 * `<div>`/`<span>` 으로 바뀌었다. 여기서는 두 가지를 한다.
 *
 * ① 입력이 아니라는 것을 손에서도 드러낸다: 컴포저 전체가 `pointer-events:
 *    none`(hover·active·클릭·텍스트 선택 0 - 전역 `.rs-searchbox-submit:hover`
 *    ·`:active` 재질 변화도 이 한 줄로 발화 0), 커서는 `default`(텍스트 커서
 *    금지). 클릭은 아래 열(`.hero-search-col`)로 떨어지고 거기도 `default` 다.
 * ② 픽셀은 그대로 둔다: 종전 `<input>` 은 UA 가 `line-height: normal`·한 줄
 *    고정을 줬고 안내문은 네이티브 placeholder(`style.css` `.rs-searchbox-input::
 *    placeholder` 의 `--sub`·`.72`)였다. span 은 본문 줄 간격을 상속하고 줄을
 *    바꿀 수 있으므로 그 셋을 명시로 되돌린다. 헤드리스 전후 사각형 대조가
 *    1440·390 에서 같은 값을 잰다. */
body.ia-v2 .hdemo-win-main .hero-search-col,
body.ia-v2 .hdemo-win-main .rs-searchbox {
  cursor: default;
}

body.ia-v2 .hdemo-win-main .rs-searchbox {
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

body.ia-v2 .hdemo-win-main .rs-searchbox-input {
  line-height: normal;
  white-space: nowrap;
  overflow: hidden;
  color: var(--sub);
  opacity: .72;
}

/* 타이핑 오버레이·placeholder 오버레이가 위 입력의 첫 줄에 정확히 얹히도록
 * 기하를 창 기준으로 다시 준다(전역 `.rs-searchbox-placeholder` 는 알약형
 * 한 줄 검색창의 좌표다). */
body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win-main .rs-searchbox-placeholder,
body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win-main .hdemo-composer-track {
  left: 16px;
  right: 16px;
  top: 14px;
  bottom: auto;
  transform: none;
  font-size: 15px;
  line-height: 1.5;
  text-align: left;
}

/* 로테이션 모듈이 없는 팔이라 오버레이는 CSS 가 직접 켠다: `html.js-demo` 일
 * 때만 보이고, 그때 네이티브 placeholder 는 투명이다. 두 층이 동시에 그려지는
 * 일이 원리적으로 없다(모듈이 `is-overlaying` 으로 하던 판정을 게이트가 한다).
 * JS 도 CSS 도 못 온 경로에서는 오버레이가 숨고 네이티브 한 층만 남는다. */
body.ia-v2 .hdemo-win-main .rs-searchbox-placeholder.hdemo-win-ph {
  display: none;
}

/* 바 줄: 깊이 선택 묘사(좌) + 전송(우). 전송 버튼은 절대 배치를 벗는다 -
 * 창 컴포저는 한 줄 알약이 아니라 두 줄 카드다. */
body.ia-v2 .hdemo-win-bar {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
}

body.ia-v2 .hdemo-win-deep {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--sub);
  font-size: 13px;
  font-weight: 600;
}

body.ia-v2 .hdemo-win-chev {
  width: 14px;
  height: 14px;
}

/* 전송 버튼 = **원형 아이콘 버튼**. 값은 실제 셸의 그것을 그대로 옮겼다
 * (`panel.css:1040-1043` `.sp-send-icon`: inline-flex · 중앙 정렬 · 32x32 ·
 * padding 0 · flex none · 999px).
 *
 * ⚠️ 여섯 선언이 **다 필요하다**. 처음엔 `width`/`height`/`flex`/`position` 넷만
 * 옮겼는데 데스크톱에서 버튼이 44x32 알약이 되고 아이콘이 우하단으로 밀렸다
 * [실측 1280·1440·1920: w 44 h 32, 아이콘 중심 오차 +10/+6px]. 390 만 멀쩡했다.
 *
 * 이유는 `style.css` 의 두 갈래다:
 *   · 기본형(:3051)은 **글자 라벨을 담는 알약**이라 `padding: 12px 22px` 이고
 *     너비 선언이 없다. `box-sizing: border-box` 아래에서 그 패딩(좌우 합 44)이
 *     내 `width: 32px` 보다 커서 사용 너비가 44 로 바닥을 친다 - 선언이 무시된
 *     게 아니라 패딩이 이긴 것이다. 세로도 콘텐츠 상자가 32-24=8px 로 눌려
 *     20px 아이콘이 아래로 넘친다.
 *   · 원형은 `@media (max-width: 720px)`(:4154) 안에만 있다. 그래서 폰만
 *     정상이었고, 데스크톱에는 원형으로 만들 규칙이 애초에 없었다.
 * 즉 "빠진 한 덩이" 는 `display`/정렬/`padding: 0` 이다 - 그것을 제품 값으로
 * 채운다. 오라클이 정원(w == h)과 아이콘 중심 일치를 전 뷰포트에서 잰다. */
body.ia-v2 .hdemo-win-main .rs-searchbox-submit {
  position: static;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 999px;
  transform: none;
}

/* [리뷰 #4·K7 2026-09-23] 721~1023 태블릿 - 데모 창 사이드바를 숨긴다
 * (K7 확정: "실제 앱 드로어 닫힘과 같은 뜻"). 당시 실측(768x1024): 옛 폭
 * 공식(고양이 레인 240 뺄셈)과 사이드바 고정폭 280px 이 겹쳐 창 528px 중 대화
 * 열이 **198px** 로 무너졌다(721~1023 은 `≤720` 사이드바 부재 규칙의 사각지대
 * 였다). 폭 공식은 이 블록이 아니라 위 [F-WINDOW] 절이 진다(현재 고양이 레인
 * 두 항 포함, 768 에서 600px). 사이드바 트랙만 걷으면 그 폭 전부가 본문으로
 * 돌아간다(1열 그리드로 전환). `≤720` 은 폭 공식도 `calc(100vw - 32px)` 로
 * 따로 좁힌다(폰은 고양이 레인을 뺄 만큼 폭이 크지 않다). */
@media (min-width: 721px) and (max-width: 1023px) {
  body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win {
    grid-template-columns: minmax(0, 1fr);
  }

  body.ia-v2 .hdemo-win-side {
    display: none;
  }
}

/* ── 좁은 지면 ── 실제 모바일 셸은 드로어가 닫힌 상태라 사이드바가 화면에
 * 없다(panel.css:1051-1057). 창도 같다: 1열이 되고 사이드바는 부재다
 * (숨김이 아니라 `display: none` - 탭 순서와 읽기 순서 양쪽에서 빠진다). */
/* [impeccable critique 2026-09-24 P1 · 고양이 재측정] 끝 장면이 최댓값 스크롤
 * 대신 첫 기업 행에 서면서, 둘째 기업 머리줄이 폰 좌하단 고양이 자리로
 * 내려왔다. 480~720 에서 고양이는 SIZE 60 + EDGE_GAP 16 = 우변 76 인데
 * (`cat.mjs`, 480 미만은 SIZE_COMPACT 46 -> 62), 기업명 링크 좌변이 73 이라
 * 3px 겹쳤다(실측 700x800: 고양이 16..76 x 724..784, `.attn-name` 73..91 x
 * 702..728). 세로 위치는 스크롤·창 높이에 따라 흔들리므로 **가로**로 떼어
 * 놓는다: 기업 목록을 8px 들여 기업명 좌변을 81 로 민다. 480 미만은 고양이가
 * 작아 여유가 이미 11px 라 칩 폭을 줄이지 않는다. */
@media (min-width: 480px) and (max-width: 720px) {
  body.ia-v2 .hdemo-win-firms {
    padding-left: var(--space-2);
  }
}

@media (max-width: 720px) {
  body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win {
    grid-template-columns: minmax(0, 1fr);
    width: calc(100vw - 32px);
    /* [리뷰 #5·K5 실측 재도출 2026-09-23] 300 -> 313. K5 히어로 CTA
       (`.hero-cta-primary`, 46px)가 새로 생겨 창 상단(y)이 그만큼 아래로
       밀렸다 - 700x800 실측: 도킹 컴포저 bottom 734.25 가 고양이 top 724
       를 10.25px 덮었다(`test_the_cat_never_lands_on_the_window` red). 13px
       를 더 빼면 700x800 에서 4.75px 여유로 정리된다. 20px 는 과했다 -
       360x800 의 빈 화면 예시 카드 바닥이 창을 2.6px 넘쳤다
       (`test_the_empty_state_never_collides_with_the_resting_composer`
       red, 360 의 여유는 원래 약 17.4px뿐이었다). 13px 는 두 오라클이
       다 서는 교집합이다(700x800·360x800·390x844 전부 재검증 완료). */
    /* [레이아웃 체계 2026-09-24 Kevin Aside 비교] 데스크톱과 **같은 식**
       (`100svh - 180px`)이다. 창이 폴드를 넘는 구도가 되면서 좁은 지면의 두 제약
       (좌하단 고양이 vs 도킹 컴포저, 360x800 빈 화면 예시 카드 수납)이 한 방향으로
       풀린다: 창 바닥이 폴드 아래로 충분히 내려가 도킹 컴포저가 첫 화면(스크롤 0)
       에서 고양이 띠 밖(폴드 아래)에 앉고, 창이 커져 빈 화면도 여유가 는다.
       실측 도킹 컴포저 top: 390x844 845 · 360x800 801 · 700x800 816(고양이 bottom
       828 · 784 · 784 보다 아래). 상한 560 -> 720 은 그 여유를 위한 것이다. */
    --hdemo-win-min-h: clamp(420px, calc(100svh - 180px), 720px);
    --hdemo-win-head-h: 42px;
    border-radius: var(--radius-lg, 12px);
  }

  body.ia-v2 .hdemo-win-side {
    display: none;
  }

  /* [레이아웃 체계 2026-09-23] 좁은 지면 거터 16 - 머리줄·스레드·컴포저·빈
   * 화면이 이 한 변수를 읽는다(종전엔 머리줄·스크롤 상자에 각자 적었다). */
  body.ia-v2 .about-hero-grid.hdemo-e .hdemo-win {
    --hdemo-col-pad: var(--space-4);
  }

  /* [리뷰 #17·K6 2026-09-23] 폰 고양이가 답변 본문을 가리는 결함(실측:
   * 390x844, 고양이 x16 y782 46x46 이 "...12조7,835억원" 칩 본문을 덮음).
   * `cat.mjs` 는 무접촉(모듈 URL 이 내용 해시라 한 바이트도 못 만진다) -
   * K6 확정: 창 쪽에서 스크롤러 바닥에 고양이 높이 + 16px 이상 패딩을 둬
   * 마지막 내용을 밀어 올린다. `SIZE`(60) + `EDGE_GAP`(16) 를 웃도는
   * `--space-16`(64) + `--space-3`(12) = 76px 를 쓴다(토큰 조합, 리터럴
   * 아님). `test_scroller_bottom_padding_clears_the_cat_at_phone_width` 가
   * 이 여유를 `cat.mjs` 상수와 대조해 잠근다 - 상수가 커지는 날 같이 red. */
  body.ia-v2 .hdemo-win-scroll {
    padding-bottom: calc(var(--space-16) + var(--space-3));
  }

  body.ia-v2 .hdemo-win-main .hero-search-col {
    padding: 0 var(--hdemo-col-pad) var(--space-3);
  }

  body.ia-v2 .hdemo-win-thread .hdemo-prose {
    font-size: 13px;
  }

  body.ia-v2 .hdemo-win-firm .attn-name {
    font-size: 15px;
  }

  /* 빈 화면도 셸의 좁은 지면 규칙을 그대로 탄다(panel.css:1006-1009).
   * 좌우는 컴포저가 이 폭에서 쓰는 것과 **같은 토큰**이다(위 주석 참조). */

  body.ia-v2 .hdemo-win-grid {
    grid-template-columns: 1fr;
  }

  body.ia-v2 .hdemo-win-greet {
    font-size: 24px;
  }

  /* 폰 폭에서는 카드 넷이 다 서면 컴포저를 밀어내므로 앞 둘만 남긴다 -
   * 실제 셸은 그 자리가 스크롤이지만 창은 정지 화면이라 잘라서 보여 줄 수
   * 없다. 지우는 것은 **예시**뿐이고 인사·안내는 그대로다. */
  body.ia-v2 .hdemo-win-card:nth-child(n + 3) {
    display: none;
  }
}

/* 컴포저 타이핑 오버레이. 기본이 `display: none` 인 것이 계약이다 - 게이트
 * 안에서만 `block` 이 되므로 JS 없음·reduce 경로에서는 진짜 입력창 위에 가짜
 * 글자가 얹히는 일이 원리적으로 없다.
 *
 * 기하는 `style.css` 의 `.rs-searchbox-placeholder`(:6486, :6496) 거울이다 -
 * 두 벌이 갈리면 타이핑 글자가 placeholder 자리와 어긋난다. 정적 테스트가 두
 * 규칙의 left/right/font-size 를 문자열로 대조한다. */
body.ia-v2 .rs-searchbox .hdemo-composer-track {
  display: none;
  position: absolute;
  left: 54px;
  right: 152px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 17px;
  line-height: normal;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  pointer-events: none;
}

@media (max-width: 720px) {
  body.ia-v2 .rs-searchbox .hdemo-composer-track {
    left: 22px;
    right: 62px;
    font-size: 16px;
  }
}

/* ── 모션 게이트 ───────────────────────────────────────────────────────────
 *
 * 여기부터가 이중 게이트 안쪽이다: `html.js-demo`(모듈이 no-preference 에서만
 * 붙이는 클래스 + 같은 술어의 동기 프리페인트) **와**
 * `@media (prefers-reduced-motion: no-preference)` 둘 다 뒤다.
 *
 * 휴지 상태를 만드는 값은 이 블록이 결속하는 키프레임에서만 나온다 - 그래서
 * 이 게이트가 안 서는 경로(JS 없음 · reduce 선호 · 구형 브라우저)에서는 위
 * 정적 규칙만 남아 **완성된 최종 프레임**이 된다. 전역
 * `reduce { * { animation: none } }` 는 보조일 뿐 여기 기대지 않는다.
 *
 * 사보타주 대조: 아래 어느 한 줄을 미디어 **밖**으로 옮기면
 * `tests/test_ia_v2_landing_e.py::test_every_js_demo_selector_stays_behind_the_motion_gate`
 * 가 red 다.
 */
@media (prefers-reduced-motion: no-preference) {
  /* 세트 1 은 ON 에서 영구 비표시다 - `style.css` 의 같은 게이트 안
   * `display: flex` 를 덮는다. 마크업은 양 팔 모두 남는다(요소 삭제 0). */
  html.js-demo body.ia-v2 .hdemo-set + .hdemo-set {
    display: none;
  }

  /* 컴포저 트랙은 **이 한 줄로만** 존재한다. 게이트 밖 기본값이
   * `display: none` 이라, 이 줄이 안 서는 경로에서는 진짜 입력창 위에 가짜
   * 글자가 얹힐 수 없다. */
  /* [F안] 창 placeholder 오버레이는 게이트 안에서만 그려진다 - 그때 네이티브
   * placeholder 는 아래 `color: transparent` 로 비켜 준다. 게이트 밖(JS 없음 ·
   * reduce)에서는 오버레이가 `display: none` 이고 네이티브 한 층만 남는다. */
  html.js-demo body.ia-v2 .hdemo-win-main .rs-searchbox-placeholder.hdemo-win-ph {
    display: block;
  }

  /* [F안 2차] 빈 새 대화 화면은 **게이트 안에서만** 선다(기본값은 부재).
   * 그래서 JS 없음·모션 축소·구형 브라우저에서는 처음부터 대화 화면 한 장이고,
   * 빈 상태가 남아 답변을 가리는 경로가 원리적으로 없다. */
  html.js-demo body.ia-v2 .hdemo-win-empty {
    display: flex;
  }

  /* [2026-09-24 Kevin] 데모 창 컴포저는 작동하지 않는 소품 - 종전 여기는
   * `.rs-searchbox-input::placeholder { color: transparent }`(네이티브
   * placeholder 비키기)였다. 입력이 span 이 되면서 그 안내문은 span 의 글자라
   * 같은 일을 `color` 로 한다. */
  html.js-demo body.ia-v2 .hdemo-win-main .rs-searchbox-input {
    color: transparent;
  }

  /* [2026-09-24 Kevin] 데모 창 컴포저는 작동하지 않는 소품 - 아래 주석의 두 규칙
   * (`:focus ~` · `:not(:placeholder-shown) ~` 로 오버레이 비키기, `:focus::
   * placeholder` 되돌리기)은 **걷었다**. 방문자가 쓰는 상자가 아니므로 비킬
   * 일이 없고, span 에는 `:placeholder-shown` 이 영영 안 서서 `:not(...)` 이
   * 늘 참 -> 오버레이가 재생 내내 사라지는 결함이 된다. 원 주석은 이력으로 둔다:
   *
   * 방문자가 이 상자를 쓰기 시작하면 오버레이는 즉시 비킨다. OFF 팔에서는
   * `placeholder-rotate.mjs` 가 이 판정을 지는데(포커스·값 둘 다 매 이벤트마다
   * 다시 본다) ON 팔에는 모듈이 없다 - 그대로 두면 8초 재생이 끝난 뒤 방문자가
   * 친 글자 위에 안내문이 겹쳐 그려진다(그 모듈이 존재하는 이유가 된 바로 그
   * 이중 렌더다). 오버레이가 입력의 **뒤 형제**라 인접 결합자 하나로 닿는다.
   * 오버레이가 비키는 순간 네이티브 placeholder 를 투명에서 되돌린다 - 포커스만
   * 하고 안 친 상태에서 글자 층이 0 이 되면 안 된다. */

  html.js-demo body.ia-v2 .hdemo-composer-track {
    display: block;
  }

  /* 컴포저 수준 요소들은 `style.css` 의 `.hdemo-*` 목록 밖이라 여기서
   * 마스터 시계를 직접 준다. `--hdemo-total-s` 는 `about.html` 이 그리드
   * 스코프에 찍은 값(8s)을 상속한다. */
  html.js-demo body.ia-v2 .hero-search-col,
  html.js-demo body.ia-v2 .search-preview.hdemo,
  html.js-demo body.ia-v2 .rs-searchbox-submit,
  html.js-demo body.ia-v2 .hdemo-blk,
  html.js-demo body.ia-v2 .hdemo-win-head,
  html.js-demo body.ia-v2 .hdemo-win-today,
  html.js-demo body.ia-v2 .hdemo-win-sessions-in,
  html.js-demo body.ia-v2 .hdemo-win-empty,
  html.js-demo body.ia-v2 .rs-searchbox-placeholder {
    animation-duration: var(--hdemo-total-s, 8s);
    animation-timing-function: linear;
    animation-iteration-count: 1;
    animation-fill-mode: both;
  }

  /* 기존 `.hdemo-*` 요소는 `style.css` 가 `infinite` 를 준다 - ON 은 1회만
   * 재생하고 최종 프레임에 머문다. 명시도 (0,3,x) 가 (0,2,x) 를 이긴다.
   *
   * 사보타주: 이 블록을 지우면 8s 뒤 루프가 다시 돌아 컴포저가 화면
   * 중앙으로 튀어 오른다(헤드리스 t=9s 오라클이 red). */
  html.js-demo body.ia-v2 .hdemo-set,
  html.js-demo body.ia-v2 .hdemo-user,
  html.js-demo body.ia-v2 .hdemo-status,
  html.js-demo body.ia-v2 .hdemo-answer,
  html.js-demo body.ia-v2 .hdemo-input-track,
  html.js-demo body.ia-v2 .hdemo-char,
  html.js-demo body.ia-v2 .hdemo-char::after {
    animation-iteration-count: 1;
  }

  /* 포커스 스냅. 방문자가 입력창(또는 스레드 CTA)에 포커스하는 순간 E
   * 애니메이션을 전부 걷는다 -> 정적 값 = 최종 프레임이 선다. 컴포저는
   * 도킹 자리에, 말풍선·답변은 가시, 진행 표시와 가짜 글자는 부재,
   * placeholder 오버레이는 `placeholder-rotate.mjs` 의 판정대로.
   *
   * 이것이 "데모가 방문자와 경쟁하지 않는다" 의 마지막 장치다 - 1회 재생
   * 이라 2.4초면 어차피 끝나지만, 그 안에 쓰기 시작한 방문자는 기다릴
   * 이유가 없다. 마우스 클릭도 포커스를 만들므로 같은 경로다.
   *
   * [2026-09-24 Kevin] 데모 창 컴포저는 작동하지 않는 소품 - 컴포저에는 이제
   * 포커스가 들어갈 요소가 없다. 스냅은 창의 다른 포커스 대상(사이드바 줄 ·
   * 스레드 CTA · 근거 접기)과 히어로 [시작하기] 에서 그대로 발화한다. */
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hero-search-col,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .search-preview.hdemo,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .rs-searchbox-submit,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .rs-searchbox-placeholder,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-win-head,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-win-today,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-win-sessions-in,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-blk,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-user,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-answer {
    animation: none;
  }

  /* 빈 화면은 스냅에서 **꺼진다** - 방문자가 입력창에 손을 대는 순간 화면은
   * 최종 프레임(대화 한 판)이어야 하고, 거기에 빈 새 대화 안내는 없다.
   * 위 블록(애니메이션 제거 = 정적값 복귀)과 달리 정적값이 `display: none`
   * 이므로 여기서 한 번 더 끊어야 한다. */
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-win-empty {
    display: none;
  }

  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-composer-track,
  html.js-demo body.ia-v2 .about-hero-grid.hdemo-e:focus-within .hdemo-status {
    display: none;
  }
}

/* [리뷰 #21 2026-09-23] "지금 살펴볼 사건"·"둘러보기" 스크롤 리빌 섹션이
 * 스크롤 전에 공백으로 캡처된다(빠른 스크롤·인쇄·공유 미리보기에서도).
 * 게이트 정본(`style.css` `html.js-reveal [data-reveal]{opacity:0;
 * transform:translateY(…)}`)은 **무접촉**이다 - OFF 정본이라 거기 손대면
 * "OFF 빌드 바이트 무변경" 오라클이 그 자리에서 죽는다(ia-v2.css 파일
 * 머리말의 규율 그대로).
 *
 * 그래서 ia_v2 ON 스코프에서만 리뷰 1안("opacity 대신 translateY 만")을
 * 편다: `opacity` 성분만 무력화하고 `transform` 은 그대로 둔다 - 콘텐츠는
 * 항상 읽을 수 있는 채로 살짝 내려와 있다가 스크롤로 제자리에 온다(모션은
 * 산다, 공백만 죽는다).
 *
 * 명시도 계산(둘 다 클래스 2 + 요소 1 + 속성 1 = 동률) - 이 파일이
 * `style.css` **뒤**에 실리므로(base.html `<link>` 순서) 동률이면 이 규칙이
 * 이긴다. `!important` 도 새 상수도 없다. */
body.ia-v2 [data-reveal] {
  opacity: 1;
}

/* ══ [QUIET-LEDGER 2026-10-23] 헤더 K8 내비 (style.css 같은 이름 블록과 한 벌. 버튼 r8·표제 자간은 원 규칙에서 고쳤다) ══
   헤더 = 워드마크 | 텍스트 내비 4개 | [사요에게 묻기]. 내비는 필·틴트 없는 텍스트이고 채운 면은
   버튼 하나다. 활성 면 = 잉크 + 600 + 2px 밑줄(색 단독 금지). 640px 이하에서는 내비가 둘째 줄로
   내려가 4개가 한 줄에 선다(가로 스크롤 없음). 버튼은 r8(K1 - 필은 검색창·필터에만). */
body.ia-v2 .site-header-lite .site-nav-text {
  display: flex; align-items: center; gap: var(--space-1);
  margin-left: auto;
}
body.ia-v2 .site-header-lite .site-nav-text a {
  position: relative; display: inline-flex; align-items: center;
  min-height: 36px; padding: 0 var(--space-3);
  color: var(--sub); font-size: 14px; font-weight: 500; line-height: 1;
  text-decoration: none; white-space: nowrap;
  transition: color .15s ease;
}
body.ia-v2 .site-header-lite .site-nav-text a:hover { color: var(--ink); }
body.ia-v2 .site-header-lite .site-nav-text a[aria-current="page"] { color: var(--ink); font-weight: 600; }
body.ia-v2 .site-header-lite .site-nav-text a[aria-current="page"]::after {
  content: ""; position: absolute; left: var(--space-3); right: var(--space-3); bottom: 2px;
  height: 2px; background: var(--ink); border-radius: 1px;
}
body.ia-v2 .site-header-lite .site-nav-text a:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-xs); }
body.ia-v2 .site-header-lite .site-nav-text + .site-header-actions { margin-left: var(--space-3); }

@media (max-width: 640px) {
  body.ia-v2 .site-header-lite { height: auto; }
  body.ia-v2 .site-header-lite .site-header-inner { flex-wrap: wrap; row-gap: 0; padding-bottom: 0; }
  body.ia-v2 .site-header-lite .site-name { order: 1; }
  body.ia-v2 .site-header-lite .site-header-actions,
  body.ia-v2 .site-header-lite .site-nav-text + .site-header-actions { order: 2; margin-left: auto; min-height: 56px; }
  body.ia-v2 .site-header-lite .site-nav-text {
    order: 3; flex: 1 0 100%; margin: 0 0 0 calc(-1 * var(--space-3));
    border-bottom: 0;
  }
  body.ia-v2 .site-header-lite .site-nav-text a { min-height: 44px; font-size: 14px; }
  body.ia-v2 .site-header-lite .site-nav-text a[aria-current="page"]::after { bottom: 6px; }
}
