/* 영상탭 하단 도크 + [＋ 노드] 격자 팝오버 — 2026-09-21 오너 2차 피드백 #2·#3·#5 · 코덱스 상의 §2 DOCK REDESIGN.
 *
 * 오너 원문(2차):
 *   #2 *"도크 [＋] 가 '노드'를 뜻하는지 알 수 없다. 호버로 제자리에서 넓어지는 동작은 마우스가 올린 자리가 바뀌어 나쁘다
 *        → … 버튼은 '＋ 노드' 라벨, 펼침은 버튼 위로 뜨는 팝오버/그리드, 버튼 자리 고정."*
 *   #3 *"도크 자체의 색·UI 가 안 예쁘고 정리된 느낌이 없다."*
 *   #5 *"[정렬] 은 상단 바가 아니라 하단 도크(되돌리기·다시 실행·맞춤·조작법 옆)에."*
 * 오너 원문(3차 · 2026-09-21 저녁):
 *   *"[＋ 노드] 메뉴의 설명들은 좀 못알아듣겠어서 문구 조정 필요."* → 문구는 `videoLabToolbar.ADD_HINT` 가 소유한다.
 *   여기(CSS)의 3차 몫은 **타일이 카드 머리와 같은 색 언어를 쓰게** 하는 것이었다: 종류색 아이콘 상자 + 세 단 글자.
 * 오너 원문(4차 · 2026-09-21 밤):
 *   *"메뉴 독도 좀 예뻤으면 좋겠고, [＋ 노드] 메뉴 누르면 배경도 너무 어두워서 안 어울리고 애니메이션도 예쁘게 떴으면 좋겠고."*
 *   → 아스트라(코덱스 gpt-6-astra) 명세 §5·§6·§7 · 계약서 4(`CONTRACT4.md`).
 *
 * ⚠️ **영상탭 캔버스만** 건드린다 — 모든 선택자가 `body.shell-v2` + `.vlab-dock`/`.vlab-menu--add-grid` 아래다.
 *    다른 탭은 한 픽셀도 안 바뀐다(이 파일은 그 셋 밖의 클래스를 적지 않는다).
 *    ⚠️ 예외 **하나**: 파일 끝의 ①-C(`.vlab-canvas.is-hand-tool` 커서) — 도크의 화면 도구가 만들어 내는 결과라
 *       그 컨트롤 옆에 두었다. `.vlab-canvas` 역시 영상탭에만 있는 클래스라 위 불변식은 그대로다.
 * ⚠️ 이 파일은 **v2/canvas.css 를 순서로 덮는다**(index.html 에서 canvas-contrast.css 바로 뒤) — 같은 특이도면 뒤가 이긴다.
 *    canvas.css 의 옛 도크 규칙(글라스 · 알약 · `.vlab-addstrip`)을 여기서 갈아 끼운다.
 *
 * 🔴🔴 **4차가 찾아낸 진짜 원인 — 팝오버가 "너무 어두웠던" 것은 색 선택이 아니라 특이도였다.**
 *    `liquid-glass-overlays.css` 가 `body.shell-v2 .vlab-menu`(0,2,1)에 `background: var(--lg-sheen), var(--lg-tint-thick)`
 *    (= **검정 70% 틴트**)를 칠하고, 그 파일은 index.html 에서 **이 파일보다 뒤**에 실린다(`liquidGlassLayer.test.mjs` ⑤ 가
 *    "글라스가 v2 전 파일 뒤" 를 잠근다). 3차까지 여기서 적어 둔 `background: var(--vl-node-body)` 는 **같은 (0,2,1)** 이라
 *    순서로 졌다 — 팝오버는 카드 표면이 아니라 **검정 유리**로 떠 있었고, 그게 오너가 본 그 어두움이다.
 *    → 4차는 팝오버를 `.vlab-menu.vlab-menu--add-grid`(0,3,1)로 적어 **특이도로** 이긴다. 우클릭 메뉴도 같은 함정이라
 *      `canvas-contrast.css` ⑤-B 에서 함께 고쳤다(그쪽은 `[class]` 한 단으로 올렸다 — 그 자리에 이유를 적었다).
 *
 * 🔴 **도크는 반대로 유리를 되입는다**(4차 · 아스트라 §5 *"Use liquid glass here to connect the canvas to the app shell"*).
 *    2차에 유리를 벗긴 근거(*"흐린 유리 위에 흐린 글자라 도크와 캔버스의 경계가 없었다"*)는 **재질이 아니라 대비**의
 *    문제였다 — 유리를 쓰면서 1px 테두리·윗변 반사광·또렷한 글자(#d7dfec)를 얹으면 막대는 막대로 보인다.
 *    카드는 **여전히 불투명**하다(유리는 떠 있는 컨트롤만 · "glass can not sample other glass").
 * 🔴🔴 **4차-B(오너 즉석 피드백 · 워킹트리 화면을 보고)**: *"도크가 너무 크다 → 다른 페이지들의 검정 테마와 맞춘다."*
 *    → 높이 64→**52** · 반경 22→**16** · 컨트롤 44→**36** · 구분선 24→**20**.
 *    → 표면은 아스트라의 밝은 회청색(rgba(48,55,68,.88))을 버리고 **셸 크롬과 같은 재질**을 읽는다:
 *      `--lg-sheen` + `--lg-tint-thick`(검정 70% — 토큰 주석이 *"본문 위에 바로 붙는 바(스크림 없음)"* 라고
 *      스스로 지목한 그 자리다) + `--lg-filter` + `--lg-specular` + 테두리 `--lg-edge`. 사이드바·상단바와 같은 값이다.
 *    ⚠️ **하드코딩한 rgba 대신 토큰을 읽는 것이 요점이다** — `liquid-glass-tokens.css` 가
 *       `@supports not (backdrop-filter)` 와 `prefers-reduced-transparency`·`prefers-contrast`·`forced-colors` 에서
 *       그 토큰들을 **불투명 폴백으로 갈아 끼우므로**(반투명만 남고 블러가 없는 상태가 가장 나쁘다) 이 파일이
 *       도크 폴백을 따로 쓸 필요가 없다. 팝오버는 표면이 그대로(오너 지적 없음)라 그쪽 폴백만 파일 끝에 남는다.
 * 🔴 **누를 때 줄어들지 않는다**(button.css:217 `scale(0.96)`) → 4차에 **아스트라의 0.97 로 되살렸다**: 0.03 배의
 *    눌림은 막대를 흔들지 않고 "눌렸다" 만 말한다(2차의 신고는 button.css 의 0.96 이 36px 막대를 흔든 것이었다).
 * ★★★ **5차(오너 4차 판정 *"도크도 퍼렇고 우리 서비스 톤앤매너와 너무 다르다"*)**: 막대 재질(`--lg-*`)은
 *   이미 중립이라 그대로 두고, **퍼런 두 곳만** 걷었다:
 *     ① 글자 `#d7dfec`(청회색) → `var(--ink-80)` · 못 누르는 것은 `var(--ink-48)` · 포커스 테는 `--accent-focus`.
 *     ② **[＋ 노드] 파란 틴트 알약 → 흰 알약**(`#fff`/`#000` · 앱 주 CTA 와 같은 얼굴 · ＋ 45° 회전은 유지).
 *   팝오버 표면도 청회색(rgba(58,66,81,.96))을 걷고 **중립 검정 유리**(rgba(18,18,18,.92))로 바꿨다.
 * ★ **색은 토큰 하나에서 온다**(3차): `canvas-contrast.css` ① 블록이 `.vlab-dock` 을 스코프에 넣어
 *   `--vl-node-title/-meta`·`--vl-kind-*` 가 여기까지 온다 — 고칠 곳은 그 한 자리다.
 * ⚠️ 자간은 토큰만 쓴다(`--ls-fine`·`--ls-footnote`) — v2/typeScale.test.mjs 가 본문 단(≤15px) 원시 자간을 잡는다.
 * ⚠️ 컨트롤 높이는 사다리 위(36·44·52) — 같은 가드의 ④가 잠근다. 4차-B 의 단추는 **36** 이고 막대는 **52** 다.
 * ⚠️ `--lg-*` 토큰은 `body.shell-v2` 에 정의돼 상속으로 여기까지 온다(liquid-glass-tokens.css:14) — 그 파일이
 *    index.html 에서 **뒤에** 실려도 무관하다: 커스텀 프로퍼티는 순서가 아니라 캐스케이드 전체에서 해석된다.
 * ⚠️ `.btn` 의 알약은 벗지 않는다 — 아스트라의 14px 대신 `--r-pill` 을 쓴다(같은 가드 ③ 이 래칫으로 잠근다 ·
 *    DESIGN.md *"Pill or full circle is the brand vocabulary"*). 36px 단추의 알약 반경은 18 이라 명세의 의도(둥근 사각)와 가깝다.
 * ⚠️ 애니메이션은 **transform·opacity 만**이고 `animation-fill-mode` 는 **`backwards`** 다 —
 *    `both|forwards` 는 끝난 뒤에도 transform 을 붙잡아 그 요소를 backdrop root 로 남기고 블러를 **에러 없이** 죽인다
 *    (`liquidGlassLayer.test.mjs` ④-b 가 글라스가 닿는 선택자에서 그 둘을 금지한다 — `.vlab-menu` 가 그 목록에 있다). */

/* ── ① 막대 ───────────────────────────────────────────────────────────────
 *   [＋ 노드] │ [정렬] │ [↶][↷] [맞춤][조작법]
 *   52 높이 · 7 패딩 · 4 간격 · 16 반경 · 셸 크롬 유리(위 머리주석 🔴🔴). */
body.shell-v2 .vlab-dock {
  gap: 4px;
  height: 52px;
  bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  /* ⚠️ 7px + 테두리 1px = **바깥에서 컨트롤까지 8px**(명세값)이다. 이 저장소는 `* { box-sizing: border-box }`
     (glass.css:10)라 8 을 그대로 적으면 52 − 16 − 2 = 34 가 되어 36px 단추의 아래위가 2px 잘린다. */
  padding: 7px;
  border-radius: 16px;
  border: 1px solid var(--lg-edge);
  background: var(--lg-sheen), var(--lg-tint-thick);
  -webkit-backdrop-filter: var(--lg-filter);
  backdrop-filter: var(--lg-filter);
  color: var(--ink-80);
  /* 윗변 반사광(셸과 같은 `--lg-specular`) + 떠 있는 것의 드롭(`--lg-drop-2`).
     ⚠️ `--lg-rim` 은 안 쓴다 — 위 `border` 가 이미 같은 `--lg-edge` 한 겹이고, 둘을 겹치면 테가 두 줄이 된다. */
  box-shadow: var(--lg-specular), var(--lg-drop-2);
}
body.shell-v2 .vlab-dock > * { gap: 4px; }
/* 구분선 1×20 — 막대 안의 묶음 사이(`.vlab-toolbar-sep`)와 막대 구획 사이(`::before`)가 같은 자를 쓴다.
   ⚠️ videolab.css 는 14px·알파 8% 다 — 52px 막대에서는 너무 짧고 옅어 묶음이 갈려 보이지 않았다(오너 #3 "정리된 느낌이 없다").
   ★ 4차: 색을 셸의 **`--lg-divider`**(흰 10%)로 바꿨다(3차의 불투명 `--vl-node-border`) — 유리 위의 불투명
     헤어라인은 재질을 끊는다(그 토큰 주석이 같은 결함을 적어 뒀다). 길이는 4차-B 에 24 → **20**(막대가 52 로 낮아졌다). */
body.shell-v2 .vlab-dock .vlab-toolbar-sep,
body.shell-v2 .vlab-dock > * + *::before {
  flex: none;
  width: 1px;
  height: 20px;
  margin: 0 8px;
  background: var(--lg-divider);
}
/* 단추 — 36 높이 · 아이콘만 있는 것은 36 정사각(4차-B · 오너 *"도크가 너무 크다"*).
   ⚠️ 아스트라의 44 는 `pointer: coarse` 의 최소 과녁이고(apple-rules ②), 마우스 화면에서는 사다리의 36 이
      셸 크롬의 값이다 — 손가락 화면은 `canvas-contrast.css` ⑦ 의 `@media (pointer: coarse)` 가 따로 맡는다.
   ⚠️ 아이콘은 18 · 라벨 13/600 그대로다(높이만 내렸다 — 막대가 아이콘으로 읽히는 줄이라).
   ⚠️ 기하·글자는 **모든** 단추가 함께 읽는다. 채움을 비우는 것은 **조용한 단추(ghost)만**이다 —
      아래 🔴 참고: 이 막대에는 [저장](ghost) 말고도 [남은 생성 취소](danger)·[전체 실행](primary)이 서고,
      그 둘은 자기 채움이 곧 뜻이다(돈을 쓰는 버튼 · 멈추는 버튼). */
body.shell-v2 .vlab-dock .btn {
  position: relative;
  isolation: isolate;
  min-height: 36px;
  height: 36px;
  padding-inline: 12px;
  border-radius: var(--r-pill);
  font-size: var(--fs-fine);
  line-height: 18px;
  font-weight: 600;
  letter-spacing: var(--ls-fine);
  transition: transform 100ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* 🔴 **채움을 비우는 것은 `.btn--ghost` 뿐이다.** 3차까지는 이 파일이 배경을 아예 안 적어서 문제가 없었는데,
   4차에 유리 막대가 되면서 조용한 단추의 회색 채움을 걷어야 했다 — 그때 `.btn` 전체에 `background: transparent` 를
   걸면 **[전체 실행]이 투명 고스트가 되고 [취소]의 빨강이 사라진다**(그 둘은 유일하게 돈·중지를 말하는 단추다).
   ⚠️ `:not()` 으로 가르지 않는다 — `:not(.btn--primary):not(.btn--danger)` 는 (0,5,1)이 되어 아래
      `.vlab-add-trigger`(0,3,1)의 파란 틴트를 이겨 버린다(특이도를 함부로 올리면 다음 규칙이 못 덮는다). */
body.shell-v2 .vlab-dock .btn--ghost {
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--ink-80);
}
body.shell-v2 .vlab-dock .btn--ghost:hover:not(:disabled) { background: transparent; color: var(--ink); }
/* 못 누르는 되돌리기·다시실행 — **흐리지 않고** 제 색(앱 잉크 사다리의 셋째 단)을 적는다(아스트라 §5). */
body.shell-v2 .vlab-dock .btn--ghost:disabled { opacity: 1; color: var(--ink-48); cursor: default; }
/* 호버·펼침 겹 — 채움색을 보간하지 않는다(유리 위에서 색을 보간하면 그 프레임마다 유리가 다시 합성된다). */
body.shell-v2 .vlab-dock .btn::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.08);
  opacity: 0;
  transition: opacity 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.shell-v2 .vlab-dock .btn:hover:not(:disabled)::before,
body.shell-v2 .vlab-dock .btn[aria-expanded="true"]::before { opacity: 1; }
/* 아이콘 18 — button.css 의 sm(14)보다 두 단 크다(막대가 아이콘으로 읽히는 줄이라) */
body.shell-v2 .vlab-dock .btn .btn__icons svg { width: 18px; height: 18px; stroke-width: 1.75; }
body.shell-v2 .vlab-dock .btn:focus-visible { outline: 2px solid var(--accent-focus); outline-offset: 2px; }
/* 누름 — 0.97 배(아스트라 §5). 2차에 끈 것은 button.css 의 0.96 이었다(위 머리주석 🔴). */
body.shell-v2 .vlab-dock .btn:active:not(:disabled) { transform: scale(0.97); }
/* 되돌리기·다시실행·맞춤·조작법은 36 정사각 */
body.shell-v2 .vlab-dock .btn--icon-only { width: 36px; padding-inline: 0; }
/* [＋ 노드] — 112px 고정 **흰 알약**(5차 · 4차의 파란 틴트를 걷었다). 폭이 고정이라 **자리가 움직이지 않는다**(오너 #2).
   ★ 이 막대에서 가장 많이 누르는 문이고, 이 앱에서 "가장 많이 누르는 문" 의 얼굴은 흰 알약이다
     (framer.css `.btn--primary` · 오너 4차 *"우리 서비스 톤앤매너"*). 옆의 [전체 실행]과 얼굴이 같은 것은
     의도다 — 둘 다 이 화면의 주 행동이고, 뜻은 라벨이 가른다.
   ⚠️ 호버·펼침 겹은 **검정**이다(흰 채움 위에서 흰 알파는 보이지 않는다). */
body.shell-v2 .vlab-dock .vlab-add-trigger {
  width: 112px;
  justify-content: center;
  background: #ffffff;
  box-shadow: none;
  color: #000000;
}
/* 🔴 5차-I(오너 2026-09-21 *"흰 생성버튼에 마우스를 올렸을 때 사라져버리는 느낌"*) — **흰 알약은 호버에서
   어두워지지 않는다.** 검정 겹은 **열림·누름**의 몫으로 남기고(그 둘은 "눌려 들어간다" 가 맞다), 호버는
   흰 후광 링 + 바깥으로 퍼지는 빛으로 **떠오르게** 한다(근거·값은 `canvas-contrast.css` 의 `--vl-primary-lift`).
   ⚠️ 순서가 전부다: 위 `.vlab-dock .btn:hover…::before{opacity:1}`(같은 (0,5,1))을 **뒤에서** 덮고,
      그 다음 줄의 `[aria-expanded]` 가 다시 겹을 켠다 — 열린 채로 손이 올라가도 눌린 얼굴이 유지된다. */
body.shell-v2 .vlab-dock .vlab-add-trigger:hover:not(:disabled) {
  background: #ffffff;
  color: #000000;
  box-shadow: var(--vl-primary-lift);
}
body.shell-v2 .vlab-dock .vlab-add-trigger::before { background: rgba(0, 0, 0, 0.10); }
body.shell-v2 .vlab-dock .vlab-add-trigger:hover:not(:disabled)::before { opacity: 0; }
body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"]::before { opacity: 1; }
/* ★★ 5차-B(오너 *"애니메이션도 좀 더 애플 느낌으로 촤라락"*) — 이 단추만 **제 모션**을 갖는다:
 *   · 누름 `scale(.94)` 90ms(막대의 기본 .97 보다 깊게 — 여기가 이 화면에서 가장 많이 누르는 문이다)
 *   · 열린 동안 **1px 떠 있다**(`translateY(-1px)`) — 팝오버가 이 단추에서 자란다는 것을 자리로 말한다
 *   🔴 특이도 순서가 전부다: `.vlab-dock .btn:active:not(:disabled)`(0,4,1)가 위에 있으므로
 *      열림 + 누름을 **함께** 적은 (0,5,1) 규칙이 **맨 뒤**에 와야 열린 채로 눌러도 떠 있는 것이 안 무너진다. */
/* ⚠️ 호버 후광(5차-I)도 여기서 함께 전환한다 — `transition` 은 마지막 선언 하나가 전부를 덮으므로 위에 따로 적으면 지워진다. */
body.shell-v2 .vlab-dock .vlab-add-trigger { transition: transform 90ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 120ms cubic-bezier(0.2, 0.8, 0.2, 1); }
body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"] { transform: translateY(-1px); }
body.shell-v2 .vlab-dock .vlab-add-trigger:active:not(:disabled) { transform: scale(0.94); }
body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"]:active:not(:disabled) { transform: translateY(-1px) scale(0.94); }
/* ＋ 가 **45° 돌아 ×가 된다**(4차 · 아스트라 §5) — 열림/닫힘이 같은 자리에서 한 획으로 말해진다.
   ⚠️ DOM 담당(B)이 ＋ 아이콘 svg 를 `<span class="vlab-dock-plus">` 로 감싼다 — 그 span 이 없으면 아무 일도 안 난다
      (아이콘 자신을 돌리면 `.btn__icons` 의 정렬이 함께 돈다). */
body.shell-v2 .vlab-dock .vlab-dock-plus {
  display: inline-flex;
  /* 45° 로 **스프링**해서 넘어간다(5차-B) — 살짝 지나쳤다 돌아오는 그 반 바퀴가 "열렸다" 를 말한다. */
  transition: transform 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"] .vlab-dock-plus { transform: rotate(45deg); }

/* 옛 호버 띠는 더 이상 서지 않는다(`buildAddFab` 삭제) — 남은 규칙이 되살아나지 않게 여기서도 닫아 둔다 */
body.shell-v2 .vlab-addstrip { display: none; }

/* ── ①-B 화면 도구 토글 [커서]/[손] ───────────────────────────────────────
 *   2026-09-22 오너(1차): *"하단 독바에 핸들(캔버스를 잡고 이동)이랑 커서 토글을 만들자 두개의 선택지가 왔다갔다하게."*
 * 🔴 **같은 날 2차(오너 #1)**: *"이거 전체를 클릭영역으로 만들자 왼쪽오른쪽 클릭 나눠서 하지않게 그게 편할듯."*
 *   → DOM 이 바뀌었다: 공용 `segmented()`(role=radiogroup · 칸마다 `<button>`)가 아니라
 *     **알약 자신이 버튼 하나**(`videoLabToolbar.toolModeToggle` · `role="switch"`)이고 두 칸은 `<span aria-hidden>` 그림이다.
 *   ⚠️ **크기·재질은 한 값도 안 바뀌었다** — 칸 32 · 트랙 36 · 흰 6%/14% 그대로다(2차의 호버 8% 는 3차에 걷었다 ·
 *     아래 ★★★ ②). 바뀐 것은 *무엇이 클릭과 포커스를 먹는가* 뿐이라, 아래 규칙도 대상만 `.seg-opt` →
 *     `.vlab-tool-seg-btn` 으로 옮겨 적었다.
 *   ⚠️ 클래스 이름(`.seg`/`.seg-opt`)은 **일부러 그대로 둔다**: 모양의 정본은 여전히 `trendControls.css` 의 그 둘이고
 *     (index.html 에서 이 파일보다 **앞**에 실린다 · 27행 vs 69행), 이름을 갈면 그 기본값을 통째로 다시 적어야 한다.
 *     이름은 *모양의 어휘*이고 뜻은 role 이 진다 — 그 둘이 갈리는 것을 여기 적어 둔다.
 * ⚠️ **사다리 위의 값만 쓴다**: 칸 32 · 트랙 36(= 2 + 32 + 2). `typeScale.test.mjs` ④ 가 `.seg` 를 컨트롤로 보고
 *    28·32·36·40·44·48·52·56 만 통과시킨다 — 30 을 적으면 그 가드가 운다(실제로 첫 판이 30 이었다).
 *    36 은 이 막대의 단추 높이와 **같은 값**이다(머리주석 ⚠️ "컨트롤 높이는 사다리 위") — 두 컨트롤의 아래위가 맞는다.
 * ⚠️ 트랙 배경은 **흰 6%**다. `trendControls.css` 의 `var(--surface-1)` 은 밝은 판용이라 검정 유리 막대 위에서
 *    회색 덩어리로 뜬다(같은 함정을 ② 타일의 ★5차가 적어 뒀다).
 * ★★★ **3차(같은 날 저녁 · 오너 *"호버했을때 아무 변화도없는게 안헷갈릴거같아. 아니면 포커스가 들어오는게아니라
 *   사이즈변화나 다른게 된다던지. 그리고 클릭해서 바뀔때 원이 옆으로 이동하는형태의 애니메이션이 예쁠듯.
 *   다른데 라디오 참고해봐"*)** — 두 가지가 바뀌었다.
 *   ① **고른 칸을 채우던 것 → 미끄러지는 원**(`.vlab-tool-seg-thumb`). 2차까지는 `.seg-opt.on` 의 배경을 한 번에
 *      갈아 끼웠다(보간 없음) — 자리는 맞았지만 **어디서 어디로 갔는지**를 아무것도 말하지 않았다. 이제 트랙 안의
 *      원 하나가 `transform: translateX()` 로 옮겨 간다. 움직이는 것은 **transform 하나**다: 이 막대는 유리
 *      (`backdrop-filter`)라 채움·테두리를 보간하면 프레임마다 유리가 다시 합성된다(② 타일과 같은 규율).
 *   ② **호버가 칸을 밝히던 것을 걷었다.** 2차의 그 규칙(안 고른 칸이 흰 8% 로 떠오른다)은 *"저쪽이 이미 골라졌나"* 로
 *      읽혔고 그것이 오너 신고 그대로다. 오너의 1순위는 *"아무 변화도 없는 것"*, 2순위가 *"사이즈 변화"* 라
 *      **어느 쪽이 골라질지에 대해 아무 말도 하지 않는** 쪽을 골랐다: 원 자신이 1.05 배로 아주 살짝 커질 뿐이다.
 *      원은 이미 고른 쪽에 서 있으므로, 커져도 *"저쪽으로 넘어간다"* 로는 읽힐 수 없다.
 *      🔴 **안 고른 칸을 밝히는 규칙을 되살리지 말 것** — 그것이 오너가 헷갈린다고 신고한 바로 그 장치다.
 *   ⚠️ 원의 채움(흰 14%)·트랙(흰 6%)은 **한 값도 안 바뀌었다** — 옮긴 것은 그 14% 를 *누가 드는가* 뿐이라
 *      고름의 세기(`.btn` 호버 겹 8% 보다 한 단 위)는 그대로다.
 *   ⚠️ 아이콘은 **opacity 로만** 밝기를 바꾼다(색 보간도 같은 합성 비용이다). `--ink`(흰 85%)에 0.75 를 곱하면
 *      흰 64% 로, 어두운 유리 위에서 지금까지 쓰던 `--ink-48`(이 막대에서는 `liquid-glass-chrome.css` 가
 *      `#adadad` 로 덮는다 ≒ 흰 65%)과 같은 자리다 — 안 고른 칸의 밝기는 눈으로 안 바뀐다.
 * ⚠️ `.ctl-field` 는 세로 flex 다(`trendControls.css`:13) — 라벨을 안 주므로 자식이 알약 하나뿐이고,
 *    막대 안에서 늘어나지 않게 `flex: none` 만 준다.
 * ⚠️ 글자가 없어 자간·크기 선언이 **없다**(typeScale ① 은 본문 단 자간만 본다) — 뜻은 `aria-label` 과 툴팁이 진다. */
body.shell-v2 .vlab-dock .vlab-tool-seg { flex: none; }
body.shell-v2 .vlab-dock .vlab-tool-seg .seg {
  /* 미끄러지는 원의 **기준 상자**다 — 원은 절대배치라 두 칸의 flex 자리를 한 픽셀도 안 먹는다(트랙 폭 70 그대로).
     ⚠️ z-index 를 주지 않는다: 쌓임 맥락을 만들면 이 알약이 유리 막대 위에서 제 층으로 떨어져 나간다. */
  position: relative;
  gap: 2px;
  height: 36px;
  padding: 2px;
  background: rgba(255, 255, 255, 0.06);
  overflow: visible;   /* 좁혀지지 않는 두 칸이다 — 가로 스크롤(trendControls 의 `auto`)이 필요 없다 */
  /* `.seg` 가 이제 `<button>` 이다 — `trendControls.css` 의 `.seg` 는 `<div>` 를 전제로 쓰여 단추 기본값을 안 걷는다.
     ⚠️ 이 넷이 없으면 브라우저 기본 테두리·회색 바탕·11px 글꼴이 알약 위에 그대로 남는다(칸 색은 그 위에 얹힌다). */
  appearance: none;
  border: 0;
  font: inherit;
  cursor: pointer;
}
/* 누름 — 막대의 기본 0.97(위 `.btn:active`)과 같은 값. `.btn` 이 아니라 이 규칙이 따로 필요하다.
 * ★ 이것이 2차(오너 #1)의 몫이다: **알약 하나가 통째로** 줄었다 펴지는 것이 "여기 전체가 한 단추" 라는 말을
 *   손에 돌려준다. 옛 판에는 누름 응답이 아예 없었고(칸은 `.btn` 이 아니다), 그 침묵이 *"왼쪽·오른쪽을 따로 누른다"* 는
 *   인상을 거들었다. ⚠️ 칸 하나만 줄이지 말 것 — 그러면 나눠진 인상이 도로 살아난다.
 * ⚠️ 3차: 이 0.97 은 **알약의** transform 이라 원의 transform 과 싸우지 않는다(다른 요소다). 눌린 채 호버면
 *   원은 1.05, 알약은 0.97 이라 화면에서는 약 1.02 — 원이 알약 밖으로 나가지 않는다(33.6 × 0.97 = 32.6 < 36). */
body.shell-v2 .vlab-dock .vlab-tool-seg-btn:active { transform: scale(0.97); }

/* 미끄러지는 원 — 32 정사각(칸과 같은 크기)에 알약 반경이라 원이다. 채움은 2차까지 `.seg-opt.on` 이 들던 흰 14% 그대로다.
 * ⚠️ **이동 거리 34px 은 기하에서 나온다**: 칸 32 + 칸 사이 간격 2. 왼쪽 자리 `left: 2px` 는 트랙 패딩 2 와 같은 값이다.
 *    칸 크기·간격·패딩 중 하나라도 바꾸면 이 두 숫자를 함께 고쳐라(세 값이 이 둘로 합쳐진다 · 아래 테스트가 관계를 잰다).
 * ⚠️ **자리의 정본은 `aria-checked`** 다 — `.on`(칸 그림의 어휘)과 따로 두지 않고 **보조기술이 듣는 값**을 그대로 쓴다.
 *    그래야 들리는 상태와 보이는 자리가 갈릴 수 없다. 둘 다 `toolModeToggle` 이 같은 `hand` 한 개에서 쓴다.
 * 🔴 **transform 의 순서가 뜻을 바꾼다**: `translateX() scale()` 이어야 이동량이 배율에 안 곱해진다. `scale()` 이 앞이면
 *    34px 이 35.7px 이 되어 **호버하는 동안만 원이 1.7px 더 간다**(손을 떼면 되돌아온다 — 눈에는 떨림으로 보인다).
 * ⚠️ 이징은 **표준 `cubic-bezier(0.2, 0.8, 0.2, 1)`** 다. 이 막대의 스프링(0.34, 1.56, 0.64, 1)이 더 "딱 붙는" 느낌을
 *    주지만 되튐이 약 9% 라 34px 이동에서 3px 을 더 간다 — 트랙 안쪽 여유가 패딩 2px 뿐이라 원이 알약 테두리 밖으로
 *    튀어나온다(팝오버는 제 그림자 안에서 되튀므로 같은 스프링이 그쪽에서는 맞다).
 * ⚠️ 120ms 는 이 막대의 사다리 값이다(90·100·120·220·260). 아이콘 밝기와 **같은 길이**라 원이 닿는 그 순간에
 *    두 아이콘의 교대가 끝난다 — 길이가 갈리면 "먼저 밝아지고 나중에 도착" 으로 읽힌다. */
body.shell-v2 .vlab-dock .vlab-tool-seg-thumb {
  position: absolute;
  left: 2px;
  top: 2px;
  width: 32px;
  height: 32px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.14);
  /* 비행 키프레임의 **출발점**이다(아래 ★★★ 4차). `--vlab-seg-x` 를 못 쓴다 — 끄는 쪽(off)은
     `aria-checked="false"` 위에서 도느라 그 변수가 이미 0 이다. 🔴 아래 `--vlab-seg-x: 34px` 과 **같은 값**이어야 하고,
     테스트가 그 둘이 같은지 잰다(어긋나면 원이 없던 자리에서 출발한다). */
  --vlab-seg-travel: 34px;
  transform: translateX(var(--vlab-seg-x, 0px)) scale(var(--vlab-seg-s, 1));
  transition: transform 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;   /* 원은 그림이다 — 누름·호버 판정은 알약이 통째로 받는다(2차) */
}
body.shell-v2 .vlab-dock .vlab-tool-seg-btn[aria-checked="true"] .vlab-tool-seg-thumb { --vlab-seg-x: 34px; }
/* 🔴 호버는 **어느 쪽이 골라질지 말하지 않는다**(위 ★★★ ②) — 원이 1.05 배가 될 뿐이다(32 → 33.6 · 트랙 36 안에 든다).
   안 고른 칸을 밝히는 규칙을 여기 다시 적지 말 것. 그것이 3차 신고의 원인이다. */
body.shell-v2 .vlab-dock .vlab-tool-seg-btn:hover .vlab-tool-seg-thumb { --vlab-seg-s: 1.05; }

/* ★★★ **4차(2026-09-22 밤 · 오너 *"이것도 애니메이션있게 재밌게 움직였으면 좋겠어. 리서치해보고 적용해봐."*)** —
 *   원이 **날아가고 · 가는 쪽으로 늘어났다 · 닿아서 한 번 눌렸다 편다**(스쿼시 & 스트레치).
 * 🔴🔴 **먼저 고친 것은 "재미" 가 아니라 "아예 안 돈다" 였다.** 3차가 적어 둔 위 `transition: transform 120ms` 는
 *   **이 막대에서 한 번도 돈 적이 없다.** 고르면 `videoLabView.setToolMode` → `renderToolbar()` 이고
 *   `renderBoardToolbar` 의 첫 줄이 `bar.innerHTML = ''` 다 — 원은 *옮겨 가는* 것이 아니라 **지워지고 새로 태어나**
 *   이미 도착점에 서 있다. 갓 붙은 요소에는 이전 값이 없어 전이가 걸리지 않는다(CSS 전이의 정의 그대로).
 *   헤드리스 실측: 재조립 경로에서 `thumb.getAnimations().length === 0`, 속성만 갈았을 때는 1.
 *   → 그래서 움직임을 **전이가 아니라 `@keyframes`** 로 적는다. *붙는 순간 한 번 도는 것*이 이 DOM 의 생리다.
 *   ⚠️ 대신 키프레임은 **붙을 때마다** 돈다 — 폴 렌더(생성 중 상태 갱신)에서도 돌면 원이 가만히 있다가 혼자 튄다.
 *      그래서 방아쇠는 CSS 가 아니라 `videoLabToolbar.toolModeToggle` 이 쥔다(`data-slide` 는 **값이 바뀐 렌더**에만 붙는다).
 *   🔴 **자리의 정본은 여전히 `aria-checked` 하나다.** `data-slide` 는 자리를 한 픽셀도 정하지 않는다 —
 *      마지막 키프레임이 곧 쉬는 자리(`--vlab-seg-x`·`--vlab-seg-s`)라, 붙든 안 붙든 원이 서는 곳은 같다.
 * ★ **리서치에서 고른 것**: 스쿼시 & 스트레치 — 진행 방향으로 늘이고 **부피를 보존**한다(늘어난 만큼 납작해지고,
 *   닿는 순간 반대로 눌린다). iOS 스위치의 "껌" 느낌 · 세그먼트 컨트롤의 liquid thumb 가 같은 수법이다.
 *   프레이머모션 쪽 세그먼트가 흔히 쓰는 **스프링**은 이 트랙에서 못 쓴다 — 3차 주석 그대로, 되튐이 `translateX` 를
 *   밀어 원을 알약 밖으로 내보낸다. 그래서 **되튐을 이동이 아니라 배율에 담았다**(그 둘은 여유가 완전히 다르다).
 * 🔴🔴 **"원이 알약 밖으로 안 나간다" 증명**(트랙 36 · 원 32 · 안쪽 여유 2px). 알약은 반지름 18 의 캡슐이고
 *   원의 중심은 늘 y = 18 위, 중심 x 는 18(왼쪽 칸) → 52(오른쪽 칸)로 간다. 52 는 오른쪽 캡 호의 **중심과 같은 자리**다.
 *   왼쪽 칸에서 c 만큼 간 순간의 반너비 a(= 16 × scaleX)·반높이 b(= 16 × scaleY)가 지켜야 할 것은 셋이다:
 *     `b ≤ 18` · `a ≤ c + 18` · `a ≤ (34 − c) + 18`   (끝의 둘은 양쪽 캡 호까지의 거리다)
 *     · 0%·100%(양 끝, 배율 1 · 호버 1.05) → a = b = 16.8 ≤ 18 — **여유 1.2px · 3차와 한 픽셀도 다르지 않다**
 *     · 30%(비행 중) 1.14/0.92 → a = 18.24 지만 그때 c 는 21.08(on)·12.92(off) 라 허용치가 30.92 다 ✔ · b = 14.72 ✔
 *     · 55%(닿는 순간) 1.05/0.97 → c = 34, a = 16.8 ≤ 18 ✔(여유 1.2px) · b = 15.52 ✔
 *     · 76%(착지 눌림) 0.94/1.04 → a = 15.04 ✔ · b = 16.64 ≤ 18 ✔(여유 1.36px)
 *   ★ 요점: **늘어나는 것은 트랙 한가운데뿐이고 양 끝에서는 배율이 1 로 돌아온다** — 그래서 가장 좁은 자리(양 끝)의
 *     최대 반경이 3차보다 크지 않다. 되튐을 `translateX` 에 담았다면 34px 이동에 9% 만 얹혀도 3px 을 더 가
 *     **바로 그 가장 좁은 자리에서** 테두리를 넘었을 것이다(3차가 스프링을 버린 이유가 정확히 이것이다).
 *   ⚠️ 도는 동안은 호버 배율(`--vlab-seg-s`)이 **곱해지지 않는다** — 애니메이션이 기본 규칙의 transform 을 통째로
 *      이긴다. 위 수치에 1.05 를 또 곱하지 않는 이유고, 그 값을 쓰는 것은 마지막 키프레임 하나뿐이다.
 * 🔴 **`translateX` 가 언제나 맨 앞이다**(3차의 함정 그대로) — 스쿼시가 붙어도 `scale()` 은 뒤다.
 *    뒤집으면 이동량이 배율에 곱해져 늘어난 프레임마다 원이 1~2px 을 더 간다.
 * ⚠️ **220ms 은 사다리 값**(90·100·120·220·260 — ＋ 아이콘의 반 바퀴와 같은 값)이지만 **원이 닿는 것은 55% = 121ms**,
 *    곧 3차의 120ms 그대로다. 뒤의 99ms 는 *제자리에서* 눌렸다 펴지는 것뿐이라 자주 누르는 도구의 체감은 안 느려진다.
 *    ⚠️ 채우기(fill)를 주지 않는다 — 끝나면 기본 규칙이 그대로 이어받고(마지막 키프레임 = 쉬는 자리) 호버 전이가 되살아난다.
 * ⚠️ 두 방향을 따로 적는다. `animation-direction: reverse` 로 되돌아오는 길을 때우면 **착지 눌림이 출발점에** 와서
 *    거꾸로 보인다(눌림은 닿는 쪽에서 일어나는 일이다). */
body.shell-v2 .vlab-dock .vlab-tool-seg-btn[data-slide="on"] .vlab-tool-seg-thumb { animation: vlab-seg-fly-on 220ms; }
body.shell-v2 .vlab-dock .vlab-tool-seg-btn[data-slide="off"] .vlab-tool-seg-thumb { animation: vlab-seg-fly-off 220ms; }
/* ⚠️ 키프레임이 건드리는 것은 **transform 하나**다(+ 칸마다의 이징) — 유리 막대 위에서 채움·색을 보간하면
   프레임마다 유리가 다시 합성된다(이 절의 규율 · 테스트가 키프레임 안의 속성을 센다). */
@keyframes vlab-seg-fly-on {
  0%   { transform: translateX(0px) scale(1, 1); animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  30%  { transform: translateX(calc(var(--vlab-seg-travel) * 0.62)) scale(1.14, 0.92); animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1); }
  55%  { transform: translateX(var(--vlab-seg-travel)) scale(1.05, 0.97); animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }
  76%  { transform: translateX(var(--vlab-seg-travel)) scale(0.94, 1.04); animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1); }
  100% { transform: translateX(var(--vlab-seg-x, 0px)) scale(var(--vlab-seg-s, 1)); }
}
@keyframes vlab-seg-fly-off {
  0%   { transform: translateX(var(--vlab-seg-travel)) scale(1, 1); animation-timing-function: cubic-bezier(0.3, 0, 0.3, 1); }
  30%  { transform: translateX(calc(var(--vlab-seg-travel) * 0.38)) scale(1.14, 0.92); animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1); }
  55%  { transform: translateX(0px) scale(1.05, 0.97); animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); }
  76%  { transform: translateX(0px) scale(0.94, 1.04); animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1); }
  100% { transform: translateX(var(--vlab-seg-x, 0px)) scale(var(--vlab-seg-s, 1)); }
}
body.shell-v2 .vlab-dock .vlab-tool-seg .seg-opt {
  /* 🔴 자리를 가져야 **원 위**에 그려진다: 원(절대배치)과 칸(상대배치)이 다 자리를 가진 형제면 그림 순서는 DOM 순서이고,
     원이 먼저 붙는다(`toolModeToggle`). 이 한 줄을 빼면 원이 아이콘 둘을 덮어 알약이 빈 원 하나로 보인다. */
  position: relative;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  /* 🔴 `trendControls.css` 의 `.seg-opt.on { background: var(--surface-3) }`(0,2,0)를 이 (0,5,1)로 덮는다 —
     안 덮으면 고른 칸에 밝은 판용 회색 채움이 원 밑에 겹쳐 선다(3차에 `.on` 의 채움을 걷었으므로 막을 것이 없다). */
  background: transparent;
  color: var(--ink);
  opacity: 0.75;       /* 흰 85% × 0.75 = 흰 64% ≒ 여태 쓰던 `--ink-48`(#adadad) — 색이 아니라 밝기로 같은 자리를 낸다 */
  transition: opacity 120ms cubic-bezier(0.2, 0.8, 0.2, 1);   /* 🔴 색·채움은 보간하지 않는다(유리 재합성) */
  pointer-events: none;  /* 칸은 그림이다 — 누름·호버 판정은 알약(버튼)이 통째로 받는다(2차) */
}
body.shell-v2 .vlab-dock .vlab-tool-seg .seg-opt svg { width: 18px; height: 18px; stroke-width: 1.75; }
body.shell-v2 .vlab-dock .vlab-tool-seg .seg-opt.on { opacity: 1; }
body.shell-v2 .vlab-dock .vlab-tool-seg .vlab-tool-seg-btn:focus-visible {
  outline: 2px solid var(--accent-focus);
  outline-offset: 2px;
  box-shadow: none;    /* trendControls 의 `--ring-focus` 링을 걷는다 — 이 막대의 포커스는 `.btn` 과 같은 얼굴이다 */
}

/* ── ①-C 손 도구가 켜졌을 때의 커서 ──────────────────────────────────────
 * 🔴 **`.is-pan-mode`(Space·`H` 누름 유지)와 다른 클래스여야 한다.** 그쪽은 `videolab.css`:867 에서
 *    `.vlab-node *` 까지 `cursor: grab !important` 로 덮는다 — 누르고 있는 동안은 노드 드래그가 물러나므로
 *    그게 참이지만, **머무는 손 도구에서는 노드가 그대로 끌린다**(오너가 원한 것은 캔버스를 잡는 수단이지
 *    편집 잠금이 아니다). 같은 클래스를 재활용하면 카드 위에서 커서가 거짓말을 한다.
 * ⚠️ 그래서 선택자를 **빈 캔버스에만** 건다(`.vlab-canvas` 자신). 카드·포트·그룹 제목의 커서는 제 규칙 그대로다.
 * ⚠️ 끄는 중(`is-panning` · `graphViewport` 가 붙인다)은 `videolab.css`:442 가 이미 `grabbing` 이다 —
 *    여기서 또 적지 않는다(두 곳이면 커서가 거짓말을 한다).
 * ⚠️ 이 블록은 이 파일에서 `.vlab-dock`/`.vlab-menu` 밖을 적는 **유일한 자리**다(머리주석 ⚠️ 의 예외).
 *    도크의 그 컨트롤이 만들어 내는 결과라 여기 적었고, `.vlab-canvas` 는 영상탭에만 있는 클래스이므로
 *    "다른 탭은 한 픽셀도 안 바뀐다" 는 그대로다. */
body.shell-v2 .vlab-canvas.is-hand-tool { cursor: grab; }

/* ── ② [＋ 노드] 격자 팝오버 ──────────────────────────────────────────────
 *   막대 **위로** 12px 띄워 뜬다(자리는 `videoLabToolbar.placeAddGrid` 가 잰다 — 미니맵 레인 248px 을 피한다).
 *   496 폭 · 12 패딩 · 두 열 · 8 간격 · 타일 최소 80(아이콘 상자 32 + 이름 13 + 설명 두 줄).
 *   🔴 선택자가 `.vlab-menu.vlab-menu--add-grid`(0,3,1)인 이유는 이 파일 머리주석의 🔴🔴 — 뒤에 실리는 글라스 층의
 *      검정 70% 틴트를 **특이도로** 이겨야 한다(같은 (0,2,1)로 적으면 순서로 진다 = 오너가 본 그 어두움).
 *   ⚠️ 판은 캔버스보다 **밝고** 카드보다 **한 단 더 밝다**(아스트라 §6) — 떠 있는 층일수록 밝다는 한 방향을 지킨다.
 *   ⚠️ 백드롭(`.vlab-menu-backdrop`)은 아무것도 안 칠한다(videolab.css:1815) — 배경을 어둡게 덮지 않는다(아스트라 §6). */
body.shell-v2 .vlab-menu.vlab-menu--add-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  gap: 8px;
  box-sizing: border-box;
  width: 496px;
  max-width: calc(100vw - 32px);
  padding: 12px;
  border-radius: 20px;
  /* ★ 4차-B: 테두리·윗변 하이라이트만 **도크와 같은 값**으로 맞췄다(`--lg-edge` · `--lg-specular`).
     도크가 셸 크롬 재질(검정 유리)로 내려가면서 이 판과 재질이 갈렸는데, 표면은 오너 지적이 없어 그대로 두고
     **빛만** 한 벌로 묶는다 — 두 판이 같은 빛을 받으면 재질이 달라도 한 가족으로 읽힌다.
     ⚠️ 5차에 판이 **검정 유리**가 되면서 테두리를 `--lg-edge-hover`(흰 18%)로 한 단 올렸다 — 판과 캔버스가
        둘 다 어두우면(1.04:1) 실루엣을 **테두리가** 져야 한다(3차 모달의 *"윤곽선이 안 보여"* 와 같은 판정). */
  border: 1px solid var(--lg-edge-hover);
  background: #14141c;   /* 블러 없는 환경의 폴백 */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  box-shadow:
    var(--lg-specular),
    0 12px 32px rgba(0, 0, 0, 0.30),
    0 32px 80px rgba(0, 0, 0, 0.24);
  /* 트리거의 가로 중심이 메뉴 아래변에 투영된 자리에서 자란다(B 가 `--origin-x` 를 인라인으로 준다). */
  transform-origin: var(--origin-x, 56px) 100%;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body.shell-v2 .vlab-menu.vlab-menu--add-grid {
    /* ★ 5차: 청회색(rgba(58,66,81,.96))을 걷고 **중립 검정 유리**로. 캔버스 위 합성 #14141c 이고
       제목 18.32:1 · 설명 11.40:1 이다. 블러·채도는 셸 토큰(`--lg-filter`)을 읽어 도크와 한 값으로 묶었다. */
    background: var(--lg-sheen), rgba(18, 18, 18, 0.92);
    -webkit-backdrop-filter: var(--lg-filter);
    backdrop-filter: var(--lg-filter);
  }
}
/* 미니맵을 뺀 폭이 496 + 여백에 못 미치면 **한 열 + 스크롤**로 접는다(도크는 절대 넓어지지 않는다).
   ⚠️ `is-narrow` 는 JS(`videoLabToolbar.openAddGrid`)가 레인 폭을 재서 붙인다 — 화면 폭만 보는 미디어 쿼리로는
      미니맵 레인을 알 수 없다. 아스트라 §6 의 440px 갈래는 그 계산에 이미 들어 있다. */
body.shell-v2 .vlab-menu.vlab-menu--add-grid.is-narrow {
  grid-template-columns: minmax(0, 1fr);
  width: min(320px, calc(100vw - 32px));
  max-height: 60dvh;
  overflow-y: auto;
}
/* 타일 — **테두리가 없다**(4차 · 아스트라 §6 *"no individual tile borders"*). 3차는 타일마다 1px 테가 있어
   여덟 개가 격자무늬로 읽혔다. 지금은 흰 알파 채움 한 장이고, 아이콘 상자만 종류색을 입어
   "이 타일이 무슨 카드를 만드나" 를 말한다.
   ★ 5차: 판이 어두워져 채움을 **흰 5%**(호버 합성 ≈9% · 누름 ≈13%)로 올렸다 — 3.5% 는 검정 유리 위에서 안 보였다. */
body.shell-v2 .vlab-add-tile {
  --vl-kind-color: var(--vl-neutral, #8a8a8a);
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  box-sizing: border-box;
  min-width: 0;
  min-height: 80px;
  padding: 12px;
  border: 0;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.05);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
}
/* 호버·누름은 겹 한 장의 opacity — 유리 판 위에서 채움색을 보간하지 않는다(아스트라 §7). */
body.shell-v2 .vlab-add-tile::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  border-radius: inherit;
  background: rgba(255, 255, 255, 0.04);
  opacity: 0;
  transition: opacity 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
body.shell-v2 .vlab-add-tile:hover:not(:disabled)::before { opacity: 1; }
body.shell-v2 .vlab-add-tile:active:not(:disabled)::before { background: rgba(255, 255, 255, 0.08); opacity: 1; }
/* 타일마다 종류색 — DOM 담당(`videoLabToolbar.ADD_KIND`)이 타일 버튼에 `data-kind` 를 붙인다.
   ⚠️ `add-group`·`import-episode` 에는 `data-kind` 가 **없다**(노드 종류가 아니다) → 위 기본값 중립으로 남는다.
   ⚠️ 지금 팝오버가 내는 표식은 일곱이다(text·image·clip·board·angle·character·location). `context`·`cut` 줄은
      **아직 안 쓰인다** — 종류 목록(`graphContextMenu.ADD_ENTRIES`)에 그 둘이 들어오는 날 색이 저절로 맞게 미리 적어 둔다
      (여덟 종류가 `videolab.css` 의 `--vl-kind-*` 와 한 벌이라는 것이 이 표의 뜻이다). */
body.shell-v2 .vlab-add-tile[data-kind="text"] { --vl-kind-color: var(--vl-kind-text); }
body.shell-v2 .vlab-add-tile[data-kind="context"] { --vl-kind-color: var(--vl-kind-context); }
body.shell-v2 .vlab-add-tile[data-kind="image"] { --vl-kind-color: var(--vl-kind-image); }
body.shell-v2 .vlab-add-tile[data-kind="character"] { --vl-kind-color: var(--vl-kind-character); }
body.shell-v2 .vlab-add-tile[data-kind="location"] { --vl-kind-color: var(--vl-kind-location); }
body.shell-v2 .vlab-add-tile[data-kind="board"] { --vl-kind-color: var(--vl-kind-board); }
body.shell-v2 .vlab-add-tile[data-kind="clip"],
body.shell-v2 .vlab-add-tile[data-kind="cut"] { --vl-kind-color: var(--vl-kind-clip); }
body.shell-v2 .vlab-add-tile[data-kind="angle"] { --vl-kind-color: var(--vl-kind-angle); }
body.shell-v2 .vlab-add-tile--wide { grid-column: 1 / -1; }
/* ★★ 유틸 줄(`다른 회차에서 가져오기`) — **만드는 선택들과 경쟁하지 않는다**(4차 · 아스트라 §6):
 *   구분선 아래 전폭 48px 한 줄 · 채움 없음 · 아이콘 상자 없이 아이콘만.
 *   ⚠️ 클래스는 B 가 붙인다(`.vlab-add-tile--util` · 기존 `--wide` 도 함께) · 구분선은 B 가 `.vlab-add-sep` 로 끼운다. */
body.shell-v2 .vlab-add-sep {
  grid-column: 1 / -1;
  height: 1px;
  margin: 4px 0;
  background: rgba(255, 255, 255, 0.10);
}
body.shell-v2 .vlab-add-tile--util {
  align-items: center;
  min-height: 48px;
  padding: 0 12px;
  background: transparent;
  color: var(--ink-80);
}
body.shell-v2 .vlab-add-tile--util .vlab-add-tile-ico {
  width: 18px;
  height: 18px;
  border-radius: 0;
  background: none;
  color: var(--ink-48);
}
body.shell-v2 .vlab-add-tile--util .vlab-add-tile-ico svg { width: 18px; height: 18px; }
body.shell-v2 .vlab-add-tile--util .vlab-add-tile-label { font-weight: 600; }
/* 아이콘 상자 32×32 · 반경 10 — 카드 머리의 "종류색 아이콘" 과 **같은 말**을 작은 정사각으로 한다(아스트라 §6). */
body.shell-v2 .vlab-add-tile-ico {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--vl-kind-color) 14%, transparent);
  color: var(--vl-kind-color);
}
/* ⚠️ `videoLabToolbar` 가 `icon(name, { size: 20 })` 으로 그린다 — 상자가 32 가 되면서 18 이 명세값이다. */
body.shell-v2 .vlab-add-tile-ico svg { width: 18px; height: 18px; stroke-width: 1.75; }
body.shell-v2 .vlab-add-tile-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
body.shell-v2 .vlab-add-tile-label {
  font-size: var(--fs-fine);
  line-height: 18px;
  font-weight: 650;
  letter-spacing: var(--ls-fine);
  color: var(--ink);
}
/* 한 줄 설명 — 이름이 말하지 않는 "그래서 무엇이 생기나" 한 마디(3차에 문구가 길어졌다 · `ADD_HINT`).
   ★ 3차: **자르지 않고 접는다.** 2차는 `nowrap + …` 이라 긴 설명이 잘렸는데, 오너가 못 알아들은 그 설명을
     고쳐 놓고 다시 자르면 고친 값이 화면에 안 나온다. 타일 높이는 `min-height` 라 접혀도 줄이 어긋나지 않고
     (격자가 `align-items: stretch` 로 한 줄의 높이를 맞춘다), 낱말은 `keep-all` 로 안 쪼갠다.
   ★ 4차: 색을 한 단 올렸다 — 판이 밝아져 3차의 메타색이 유리 위에서 흐려졌다.
   ★ 5차: 청회색(#bcc7d9)을 걷고 앱 `--ink-80` 으로 — 검정 유리 판 위 11.40:1. */
body.shell-v2 .vlab-add-tile-hint {
  font-size: var(--fs-footnote);
  line-height: 16px;
  letter-spacing: var(--ls-footnote);
  color: var(--ink-80);
  white-space: normal;
  word-break: keep-all;
  overflow-wrap: break-word;
}
body.shell-v2 .vlab-add-tile:focus-visible { outline: 2px solid var(--accent-focus); outline-offset: 2px; }
/* 막힌 종류는 흐리게 + **이유를 타일에 적는다**(툴팁만 두면 키보드·터치는 영영 못 본다) */
body.shell-v2 .vlab-add-tile:disabled { cursor: not-allowed; background: transparent; }
body.shell-v2 .vlab-add-tile:disabled .vlab-add-tile-label,
body.shell-v2 .vlab-add-tile:disabled .vlab-add-tile-ico { opacity: 0.45; }

/* ── ③ 등장·퇴장 — 애플식 스프링 캐스케이드(5차-B) ───────────────────────
 *   오너: *"애니메이션도 좀 더 애플 느낌으로 촤라락 되면 예쁘겠다."*
 *   판 260ms + 타일 220ms 를 40 + n×22ms 씩 늦춰 훑는다(9장이면 마지막 타일이 ~216ms 에 시작해 총 ~436ms).
 *   쓰는 이징은 **하나**다: `cubic-bezier(.34,1.56,.64,1)` — 끝값을 살짝 지나쳤다 돌아오는 스프링.
 *   🔴 **opacity 는 스프링을 타지 않는다.** 그 곡선은 1 을 넘는 구간이 있고 `opacity` 는 1 에서 잘리므로
 *      판이 "번쩍" 하고 멈춘 것처럼 보인다. 그래서 판의 등장은 **애니메이션 둘**이다 —
 *      transform 은 260ms 스프링 · opacity 는 **앞 120ms 만** `ease-out`(들어오는 동안 이미 다 보인다).
 *      타일은 220ms 안에서 둘을 함께 타도 눈에 안 걸린다(작고 짧다).
 *   🔴 `animation-fill-mode` 는 **`backwards`** 다 — `both|forwards` 는 끝난 뒤에도 transform 을 붙잡아
 *      그 요소를 backdrop root 로 남기고 유리의 블러를 **에러 없이** 죽인다(`liquidGlassLayer.test.mjs` ④-b 가
 *      `.vlab-menu` 계열에서 그 둘을 금지한다). 판·타일의 끝 상태가 기본값과 같으므로 보이는 결과는 같다.
 *      ⚠️ 지연 구간에서 타일이 **안 보여야** 하는 일도 이 `backwards` 가 한다(시작 상태를 미리 고정한다).
 *   🔴 퇴장은 **animation 이 아니라 transition** 이다: 끝 상태가 기본값과 달라 `backwards` 로는 마지막 프레임에
 *      원래 자리로 튀고, `forwards` 는 위 가드가 금지한다. `.is-closing` 이 끝 상태를 **선언**하고 transition 이
 *      그리로 간다. ⚠️ **120ms 다** — B 의 unmount 지연이 120ms 라 그보다 길면 판이 도중에 사라진다.
 *   ⚠️ 높이·블러를 애니메이션하지 않는다. transform·opacity 만이다. */
@keyframes vlab-add-grid-in {
  from { transform: translateY(14px) scale(0.92); }
  to { transform: translateY(0) scale(1); }
}
@keyframes vlab-add-grid-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes vlab-add-tile-in {
  from { opacity: 0; transform: translateY(12px) scale(0.96); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
body.shell-v2 .vlab-menu--add-grid.is-entering {
  animation:
    vlab-add-grid-in 260ms cubic-bezier(0.34, 1.56, 0.64, 1) backwards,
    vlab-add-grid-fade 120ms ease-out backwards;
}
body.shell-v2 .vlab-menu--add-grid.is-entering .vlab-add-tile {
  animation: vlab-add-tile-in 220ms cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
  animation-delay: calc(40ms + var(--i, 0) * 22ms);
}
body.shell-v2 .vlab-menu--add-grid.is-closing {
  pointer-events: none;
  opacity: 0;
  transform: translateY(6px) scale(0.97);
  transition: opacity 120ms cubic-bezier(0.4, 0, 1, 1), transform 120ms cubic-bezier(0.4, 0, 1, 1);
}
body.shell-v2 .vlab-menu--add-grid.is-closing .vlab-add-tile { animation: none; }

@media (prefers-reduced-motion: reduce) {
  body.shell-v2 .vlab-dock .btn,
  body.shell-v2 .vlab-dock .btn::before,
  body.shell-v2 .vlab-dock .vlab-dock-plus,
  body.shell-v2 .vlab-add-tile::before { transition: none; }
  body.shell-v2 .vlab-dock .btn:active:not(:disabled),
  body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"] .vlab-dock-plus { transform: none; }
  body.shell-v2 .vlab-menu--add-grid.is-entering,
  body.shell-v2 .vlab-menu--add-grid.is-entering .vlab-add-tile { animation: none; }
  body.shell-v2 .vlab-menu--add-grid.is-closing { transition: none; }
  body.shell-v2 .vlab-dock .vlab-add-trigger { transition: none; }
  body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"],
  body.shell-v2 .vlab-dock .vlab-add-trigger:active:not(:disabled),
  body.shell-v2 .vlab-dock .vlab-add-trigger[aria-expanded="true"]:active:not(:disabled) { transform: none; }
  /* ①-B 화면 도구 — **미끄러짐만** 끈다. 원은 여전히 고른 쪽에 있어야 하고, 바뀔 때 순간이동할 뿐이다.
     🔴 `.vlab-tool-seg-thumb` 에 `transform: none` 을 적지 말 것 — 자리(translateX)까지 지워 **안 고른 칸 위에**
        원이 눌러앉는다(그러면 토글이 늘 거짓말을 한다). 호버 배율만 1 로 되돌린다. */
  body.shell-v2 .vlab-dock .vlab-tool-seg-thumb,
  body.shell-v2 .vlab-dock .vlab-tool-seg .seg-opt { transition: none; }
  body.shell-v2 .vlab-dock .vlab-tool-seg-btn:hover .vlab-tool-seg-thumb { --vlab-seg-s: 1; }
  body.shell-v2 .vlab-dock .vlab-tool-seg-btn:active { transform: none; }
  /* 4차의 비행도 여기서 끈다. 🔴 끄는 것은 **애니메이션 하나**다 — 기본 규칙의 transform 은 손대지 않으므로
     원은 곧바로 고른 칸에 서 있다(위 `transform: none` 금지와 같은 이유). `data-slide` 는 그대로 붙지만 아무 일도 안 한다. */
  body.shell-v2 .vlab-dock .vlab-tool-seg-btn[data-slide] .vlab-tool-seg-thumb { animation: none; }
}

/* ── ④ 유리를 못 쓰는 환경 — 불투명으로 되돌린다 ──────────────────────────
 *   🔴 가장 나쁜 상태는 *반투명인데 블러가 없는 것*이다(뒤 카드의 글자가 판을 뚫고 보인다).
 *      `liquid-glass-tokens.css` 가 같은 세 조건에서 `--lg-filter: none` + 불투명 틴트로 갈아 끼우는 것과 같은 규율이다.
 *   ⚠️ 파일 맨 아래여야 위 `@supports` 블록을 순서로 이긴다(같은 특이도다). */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more), (forced-colors: active) {
  /* ⚠️ 도크는 여기 없다 — 셸 크롬 토큰(`--lg-tint-thick`·`--lg-filter`)을 읽으므로
     `liquid-glass-tokens.css` 가 같은 세 조건에서 이미 불투명으로 갈아 끼운다(고칠 곳은 그 한 자리다). */
  body.shell-v2 .vlab-menu.vlab-menu--add-grid {
    background: #14141c;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ── ⑨ 캔버스가 떠 있는 동안은 글라스를 **불투명 재질**로(2026-09-28 오너 신고 *"깜빡거리는 문제 계속있는데 생성중 아니어도
 *     그런거같기도하고"*) ─────────────────────────────────────────────────────────────────────────────────────
 * 🔴 실측(이 앱을 목 모드로 헤드리스 크롬 · 1600×1000 · DPR 2 · 완료 클립(<video>) 20 + 그림 12 · 생성 중 0 · 스테이징
 *    DkvfaGuyk69fTpkDP0Vw 와 같은 글라스 7종): 합성기의 프레임 그리기(DirectRenderer::DrawFrame)가 조작 없이 **40ms/s**,
 *    팬 33 · 팝업 43. 글라스(backdrop-filter)를 끄면 **4 · 3.5 · 6** 으로 10배 줄었다. 상단바·도크를 **하나씩** 끄면 줄지 않는다 —
 *    화면에 블러가 하나라도 있으면 합성기가 그 밑을 매 프레임 다시 읽어 흐리는 경로로 간다. 캔버스는 팬·줌·재생마다
 *    그 밑이 바뀌는 화면이라 이 비용이 프레임을 밀어 깜빡임으로 보인다(`<video>` 를 빼도·월드 will-change 를 빼도
 *    이 값은 안 줄었다 — 앞의 것은 40 → 27, 뒤의 것은 팬 GPU 229 → 683 으로 오히려 나빠졌다).
 * ★ 방법은 새로 만들지 않는다 — `liquid-glass-tokens.css` 의 **투명도 감소 폴백**(`--lg-filter: none` + 불투명 틴트)을
 *   이 화면에만 건다. 전 글라스 표면이 그 토큰을 읽으므로 셸 파일(알토 영역)을 한 줄도 안 고친다.
 * ⚠️ 틴트 값은 폴백의 #0e0e0e 가 아니라 **오늘 캔버스 위에서 보이던 색**이다(보기를 그대로 두기 위해): 캔버스 바탕 #141414 위
 *    검정 알파 합성 — regular .62 → #080808 · thin .52 → #0a0a0a · thick .70 → #060606 · 모달(스크림 #090909 .48 위 .50) → #070707.
 *    블러는 어두운 격자를 평균낼 뿐이라 이 합성색과 눈으로 구분되지 않는다.
 * ⚠️ `vlab-board-open` 은 보드 화면일 때만 body 에 붙는다(`videoLabView.renderNow`) — 목록·타임라인·다른 탭은 오늘 글라스 그대로다.
 * ⚠️ 토큰을 안 읽고 블러를 직접 적은 표면(스크림 `blur(2px)` · 빈 캔버스 안내 · 조작법)은 여기서 이름으로 끈다. */
body.shell-v2.vlab-board-open {
  --lg-filter: none;
  --lg-tint-thin: #0a0a0a;
  --lg-tint-regular: #080808;
  --lg-tint-thick: #060606;
  --lg-tint-overlay: #070707;
}
body.shell-v2.vlab-board-open .gs-overlay,
body.shell-v2.vlab-board-open .fb-overlay,
body.shell-v2.vlab-board-open .gd-overlay,
body.shell-v2.vlab-board-open #cm-overlay,
body.shell-v2.vlab-board-open .sm-overlay,
body.shell-v2.vlab-board-open .kp-overlay,
body.shell-v2.vlab-board-open .vlab-guide-card,
body.shell-v2.vlab-board-open .vlab-keyhelp {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
