/* ==========================================================================
   Pibo UI Kit  —  openpibo-os.pibo / openpibo-os.pibrain 공용
   --------------------------------------------------------------------------
   빌드 없음. <link> 한 줄로 쓴다. 기존 노랑 분위기를 그대로 유지하되,
   '노랑을 아껴 써서' 강조가 살아나게 한 것이 이 키트의 전부다.

     <link rel="stylesheet" href="../static/pibo-ui.css?ver=YYMMDDvN">

   원본은 design/pibo-ui.css 하나뿐이다. 각 앱의 static/ 으로는
   design/sync.sh 가 복사한다. static/ 쪽을 직접 고치지 말 것.
   ========================================================================== */

/* ── 글꼴: Pretendard (SIL OFL 1.1, fonts/LICENSE.txt) ─────────────────────
   v2 글꼴 스택 맨 앞이 "Pretendard" 인데 파일을 안 싣고 있어서, 학교 윈도 노트북에서는
   맑은 고딕·Arial 로 떨어졌다. 파이보가 직접 보낸다(인터넷 불필요).
   원본 배포판의 KS X 1001 subset 을 **고치지 않고** 그대로 쓴다 — 우리가 subset 을 새로 만들면
   OFL 의 Reserved Font Name 때문에 이름을 Pretendard 로 둘 수 없다.
   파일은 두 굵기(보통 Regular 400 · 굵게 Bold 700)만 싣는다(260929). CSS 의 500 이하는 Regular, 600 이상은 Bold 로
   그려진다(범위로 선언 — 브라우저가 가짜 굵기를 만들지 않는다). 다섯 벌(1.35MB)에서 두 벌(약 0.54MB)로.
   파일은 IDE(80) 에만 있고 도구·분류기 서버는 IDE 주소로 넘긴다(run_tools / run_classify 의 SharedFonts) —
   세 앱이 한 벌을 같이 캐시한다 */
@font-face { font-family: "Pretendard"; font-weight: 100 549; font-style: normal; font-display: swap;
  src: local("Pretendard Regular"), url("fonts/Pretendard-Regular.subset.woff2") format("woff2"); }
@font-face { font-family: "Pretendard"; font-weight: 550 900; font-style: normal; font-display: swap;
  src: local("Pretendard Bold"), url("fonts/Pretendard-Bold.subset.woff2") format("woff2"); }

:root {
  /* ── 브랜드 색 ── 기존 값 그대로. 바꾸지 말 것 ───────────────────────── */
  --yellow:        #f4de3d;
  --dark-yellow:   #f4d85a;
  --light-yellow:  rgb(248, 242, 172);
  --teal:          #13bfd5;   /* tools 에 rgb(18,189,212) 로 1 만큼 어긋나 있었다 */
  --dark-teal:     #11abbf;
  --red:           #f16449;
  --dark-red:      #d85a41;
  --light-green:   #edf5e0;
  --mid-gray:      #6c6c6c;

  /* ── 의미 토큰 ── 컴포넌트는 위 브랜드 색을 직접 쓰지 않고 이걸 쓴다 ── */
  --pb-bg:            #ffffff;
  --pb-surface:       #f7f7f7;   /* 툴바·패널 바닥 */
  --pb-surface-2:     #ededed;   /* 한 단계 더 눌린 면 */
  --pb-border:        #cfcfcf;
  --pb-border-strong: #9a9a9a;
  --pb-text:          #1a1a1a;
  --pb-text-dim:      var(--mid-gray);
  --pb-text-on-dark:  #ffffff;

  --pb-brand:         var(--yellow);        /* 헤더 = 정체성 */
  --pb-brand-2:       var(--dark-yellow);
  --pb-accent:        var(--yellow);        /* 주 동작 = 노랑. 화면에 하나만 */
  --pb-accent-edge:   #c9ab00;
  --pb-danger:        var(--red);
  --pb-danger-edge:   var(--dark-red);
  --pb-info:          var(--teal);
  --pb-ok:            var(--light-green);
  --pb-focus:         #0b63c4;               /* 흰 배경 대비 6.4:1 */

  /* ── 치수 ── 태블릿 기준. 터치 타겟 44px 밑으로 내리지 말 것 ────────── */
  --pb-tap:      44px;
  --pb-header-h: 40px;
  --pb-radius:   10px;
  --pb-radius-s:  7px;
  --pb-gap:      10px;
  --pb-pad:      14px;

  /* ── 글자 ──
     앱(ide/tools/classifier)이 원래 쓰던 스택과 같다. 키트 컴포넌트가 앱 본문과
     다른 글꼴로 튀지 않게 한다.
     한때 system-ui 를 앞에 둔 스택을 .pb(=body) 에 걸었더니 파일 목록·경로·상태바까지
     앱 전체 글꼴과 크기가 바뀌었다(16px Helvetica/맑은 고딕 -> 15px system-ui).
     글꼴을 바꾸려면 여기 값만 고친다. PiBrain 이 다르면 이 변수만 덮어쓴다. */
  --pb-font: Helvetica, "맑은 고딕", "malgun gothic", "Apple SD Gothic Neo",
             "Apple SD 산돌고딕 Neo", "Microsoft NeoGothic", "Droid sans", sans-serif;
  --pb-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --pb-fs-s:  13px;
  --pb-fs:    15px;
  --pb-fs-l:  17px;

  --pb-shadow:   0 1px 2px rgba(0,0,0,.14);
  --pb-shadow-2: 0 3px 10px rgba(0,0,0,.18);
}

/* ==========================================================================
   기본
   ========================================================================== */
/* box-sizing 은 키트 컴포넌트에만 건다. `.pb *` 로 걸면 이 키트를 붙이는 것만으로
   기존 레이아웃이 틀어질 수 있다. (이 리포의 세 앱은 이미 전역으로 border-box 를
   쓰고 있어 무해하지만, PiBrain 쪽은 확인 전이다.) */
.pb-header, .pb-header__brand, .pb-iconbar, .pb-iconbtn,
.pb-toolbar, .pb-btn, .pb-seg, .pb-seg > button,
.pb-field, .pb-panel, .pb-note, .pb-badge, .pb-term, .pb-statusbar {
  box-sizing: border-box;
}

/* .pb 는 범위 표시일 뿐 앱 본문을 꾸미지 않는다. body 에 붙이므로 여기서 글꼴·크기·색을
   정하면 앱 전체가 바뀐다. 키트 컴포넌트는 각자 글꼴을 지정한다. */
.pb {
  -webkit-text-size-adjust: 100%;
}
.pb-header, .pb-iconbtn, .pb-banner, .pb-toast, .pb-modal, .pb-runstat, .pb-statusbar {
  font-family: var(--pb-font);
}
.pb-header { color: var(--pb-text); }

/* 앱이 bare `button { ... }` 을 꾸며 둬도(IDE 는 노랑 배경·min-width 80px·그림자·
   hover 시 filter:brightness·active 시 transform) 키트 버튼은 영향받지 않게 끊는다.
   컴포넌트 규칙보다 앞에 둬서 각 컴포넌트의 min-width 등은 뒤에서 다시 잡힌다. */
.pb-btn, .pb-iconbtn, .pb-seg > button {
  min-width: 0; margin: 0; filter: none; transform: none;
  transition: background-color .12s ease, border-color .12s ease;
  -webkit-appearance: none; appearance: none;
}
.pb-btn:hover, .pb-iconbtn:hover, .pb-seg > button:hover,
.pb-btn:active, .pb-iconbtn:active, .pb-seg > button:active { filter: none; }
.pb-iconbtn:active, .pb-seg > button:active { transform: none; }

/* 키보드 이동에만 링이 뜬다. 마우스 클릭에는 안 뜬다 */
.pb :focus-visible {
  outline: 3px solid var(--pb-focus);
  outline-offset: 2px;
  border-radius: var(--pb-radius-s);
}

/* ==========================================================================
   헤더  —  노랑은 여기'만'. 브랜드 띠다
   ========================================================================== */
.pb-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--pb-gap);
  background: linear-gradient(var(--pb-brand), var(--pb-brand-2));
  /* 40px. 버튼(32px)은 아이콘·라벨을 가로 한 줄로 둔다.
     예전 54px(아이콘 위·라벨 아래)는 작업 화면을 너무 먹었다 */
  padding: 0 var(--pb-pad);
  height: var(--pb-header-h); min-height: var(--pb-header-h);
  flex: 0 0 auto;
  border-bottom: 1px solid rgba(0,0,0,.18);
}
.pb-header__brand {
  font-weight: 700; font-size: 15px; letter-spacing: .2px; white-space: nowrap;
  display: flex; align-items: center; gap: 6px;
}

/* 헤더 안의 아이콘 버튼 — 아이콘만 두지 말고 글자 라벨을 같이 둔다.
   태블릿에는 hover 가 없어서 title 툴팁이 뜨지 않는다. 라벨은 폭이 모자랄 때만 접는다 */
/* <nav> 이 아니라 <div> 를 쓸 것. tools/static/index.css:322 에 bare `nav { position: fixed }`
   가 있어서 <nav class="pb-iconbar"> 로 쓰면 헤더 밖으로 튀어나간다. */
.pb-iconbar { display: flex; align-items: center; gap: 2px; flex-wrap: nowrap; min-width: 0; }
.pb-iconbtn {
  display: flex; flex-direction: row; align-items: center; justify-content: center;
  gap: 6px; flex: 0 0 auto;
  min-width: 32px; height: 32px; padding: 0 9px;
  border: 0; background: transparent; color: inherit; box-shadow: none;
  border-radius: var(--pb-radius-s);
  font: inherit; font-size: var(--pb-fs-s); text-decoration: none; white-space: nowrap;
  cursor: pointer;
}
/* 아이콘 자리를 고정한다. JS 가 채우는 아이콘(전체화면 등)은 크기가 제각각이다 */
.pb-iconbtn > :first-child { display: inline-flex; align-items: center; justify-content: center;
                             width: 16px; height: 16px; font-size: 14px; line-height: 1; }
.pb-iconbtn__label { font-size: var(--pb-fs-s); font-weight: 600; line-height: 1; letter-spacing: -.1px; }
.pb-iconbtn:hover  { background: rgba(0,0,0,.08); }
.pb-iconbtn:active { background: rgba(0,0,0,.15); }
.pb-iconbtn--danger { color: #7a1d0c; }
/* 되돌릴 수 없는 버튼 묶음 앞에 가는 구분선 */
.pb-iconbtn:not(.pb-iconbtn--danger) + .pb-iconbtn--danger { margin-left: 9px; position: relative; }
.pb-iconbtn:not(.pb-iconbtn--danger) + .pb-iconbtn--danger::before {
  content: ""; position: absolute; left: -6px; top: 8px; bottom: 8px;
  border-left: 1px solid rgba(0,0,0,.2);
}
/* 언어 선택·로고 */
.pb-header .pb-field--sm { height: 28px; margin-left: 6px; }
.pb-header #logo_bt { display: flex; align-items: center; margin-left: 6px; }
.pb-header #logo_bt img { height: 16px !important; }
/* 헤더 안의 세그먼트(분류기 탭 등) */
.pb-header .pb-seg { height: 30px; }
.pb-header .pb-seg > button { font-size: var(--pb-fs-s); padding: 0 12px; }

/* ==========================================================================
   버튼  —  위계는 딱 네 단계. 더 만들지 말 것
     accent  주 동작. 한 화면에 하나 (실행)
     default 보통 동작 (저장·불러오기)
     ghost   보조·토글 (글자크기·대비)
     danger  되돌릴 수 없는 동작 (초기화·종료)
   ========================================================================== */
.pb-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: var(--pb-tap); min-width: var(--pb-tap); padding: 0 16px;
  border: 1px solid var(--pb-border); border-radius: var(--pb-radius);
  background: var(--pb-bg); color: var(--pb-text);
  font: 700 var(--pb-fs)/1 var(--pb-font);
  cursor: pointer; white-space: nowrap;
  box-shadow: var(--pb-shadow);
}
.pb-btn:hover:not(:disabled)  { background: #fafafa; border-color: var(--pb-border-strong); }
.pb-btn:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }

.pb-btn--accent {
  background: var(--pb-accent); border-color: var(--pb-accent-edge);
  padding: 0 22px; font-size: var(--pb-fs-l);
}
.pb-btn--accent:hover:not(:disabled) { background: var(--dark-yellow); }

.pb-btn--danger { background: var(--pb-danger); border-color: var(--pb-danger-edge); color:#fff; }
.pb-btn--danger:hover:not(:disabled) { background: var(--dark-red); }

.pb-btn--ghost {
  background: transparent; border-color: transparent; box-shadow: none;
  color: var(--pb-text-dim); font-weight: 600; padding: 0 12px;
}
.pb-btn--ghost:hover:not(:disabled) { background: var(--pb-surface-2); border-color: transparent; }
.pb-btn--ghost[aria-pressed="true"] { background: var(--light-yellow); color: var(--pb-text); }

.pb-btn:disabled { opacity: 1; background: var(--pb-bg); color: #b9b9b9;
                   border-color: #e6e6e6; box-shadow: none; cursor: not-allowed; }

/* ==========================================================================
   툴바 / 구분선
   ========================================================================== */
.pb-toolbar {
  display: flex; align-items: center; gap: var(--pb-gap); flex-wrap: wrap;
  background: var(--pb-surface); border-bottom: 1px solid #e0e0e0;
  padding: 8px var(--pb-pad);
}
.pb-div { width: 1px; height: 28px; background: #dcdcdc; margin: 0 4px; flex: none; }

/* 세그먼트 토글 (블록 / 파이썬) */
.pb-seg { display: inline-flex; height: calc(var(--pb-tap) + 2px);  /* 테두리 1px×2 만큼 키워
                                                         안쪽 버튼이 정확히 44px 되게 한다 */
          border: 1px solid var(--pb-border); border-radius: var(--pb-radius);
          overflow: hidden; box-shadow: var(--pb-shadow); }
.pb-seg > button {
  border: 0; background: var(--pb-bg); color: var(--pb-text-dim);
  font: 700 var(--pb-fs)/1 var(--pb-font); padding: 0 18px; cursor: pointer;
}
.pb-seg > button + button { border-left: 1px solid var(--pb-border); }
.pb-seg > button[aria-selected="true"] { background: var(--pb-accent); color: var(--pb-text); }

/* ==========================================================================
   입력
   ========================================================================== */
.pb-field {
  height: var(--pb-tap); padding: 0 12px;
  border: 1px solid var(--pb-border); border-radius: var(--pb-radius);
  background: var(--pb-bg); color: var(--pb-text);
  font: var(--pb-fs)/1 var(--pb-font);
}
.pb-field:focus { border-color: var(--pb-focus); }
/* 헤더 안에 들어가는 작은 입력 (언어 선택 등). 헤더 안에서는 28px 로 더 줄인다 */
.pb-field--sm { height: 32px; padding: 0 8px; font-size: var(--pb-fs-s);
                border-radius: var(--pb-radius-s); }
/* 세 앱 모두 `select, input { font-size: 16px !important }` 를 전역으로 갖고 있어
   헤더 언어 선택만 글자가 커졌다. 헤더 안에서는 키트 크기로 되돌린다 */
.pb-header .pb-field--sm { font-size: var(--pb-fs-s) !important; }

.pb-label { display: block; font-size: var(--pb-fs-s); font-weight: 700;
            color: var(--pb-text-dim); margin-bottom: 4px; }

/* ==========================================================================
   패널 · 배지 · 알림
   ========================================================================== */
.pb-panel { background: var(--pb-bg); border: 1px solid #e3e3e3;
            border-radius: var(--pb-radius); padding: var(--pb-pad); }
.pb-panel--sunken { background: var(--pb-surface); box-shadow: inset 0 1px 3px rgba(0,0,0,.06); }

.pb-badge { display: inline-flex; align-items: center; gap: 5px;
            height: 24px; padding: 0 9px; border-radius: 12px;
            font: 700 var(--pb-fs-s)/1 var(--pb-font);
            background: var(--pb-surface-2); color: var(--pb-text); }
.pb-badge--on     { background: var(--pb-ok); }
.pb-badge--off    { background: var(--pb-surface-2); color: var(--pb-text-dim); }
.pb-badge--danger { background: var(--pb-danger); color: #fff; }

/* 알림 — 색만으로 구분하지 않는다. 왼쪽 굵은 띠 + 아이콘 자리 */
.pb-note { border-left: 4px solid var(--pb-border-strong);
           background: var(--pb-surface); padding: 10px 14px;
           border-radius: 0 var(--pb-radius-s) var(--pb-radius-s) 0; font-size: var(--pb-fs); }
.pb-note--info   { border-left-color: var(--dark-teal); }
.pb-note--warn   { border-left-color: var(--pb-accent-edge); background: #fdf8dd; }
.pb-note--danger { border-left-color: var(--pb-danger-edge); background: #fdece8; }
.pb-note b:first-child { display: block; margin-bottom: 2px; }

/* 터미널 · 코드 */
.pb-term { font: var(--pb-fs-s)/1.5 var(--pb-mono);
           background: #1e1e1e; color: #e8e8e8;
           border-radius: var(--pb-radius-s); padding: 10px 12px;
           white-space: pre-wrap; overflow: auto; }

/* 상태바 */
.pb-statusbar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
                background: linear-gradient(var(--pb-brand-2), var(--pb-brand));
                border-top: 1px solid rgba(0,0,0,.18);
                padding: 4px var(--pb-pad); }   /* 글자 크기·굵기는 앱의 상태바가 정한다 */

/* ==========================================================================
   셸 레이아웃  —  VS Code 식. 페이지는 스크롤하지 않고 패널 안에서만 스크롤한다.

     <body class="pb pb-shell">
       <header class="pb-header">...</header>
       <main>  <div class="pb-panes"> ...pb-pane... </div>  </main>
       <footer class="pb-statusbar">...</footer>

   기존처럼 calc(100vh - 130px) 같은 숫자를 패널마다 손으로 맞추지 않는다.
   그 방식은 헤더 높이가 바뀔 때마다 전부 틀어지고, 폭이 모자라면
   flex-wrap 으로 패널이 아래로 떨어진다.
   ========================================================================== */
.pb-shell {
  display: flex;
  flex-direction: column;
  height: 100dvh;          /* dvh 라야 모바일 주소창에 안 잘린다 */
  overflow: hidden;        /* 페이지 자체는 스크롤하지 않는다 */
  margin: 0;
}
/* 높이가 흐르려면 사슬 전체에 min-height:0 이 필요하다. flex 자식의 기본
   min-height 는 auto 라 내용만큼 부풀고, 그러면 overflow 가 안 먹는다. */
.pb-shell > main { flex: 1 1 auto; min-height: 0; display: flex; }
.pb-shell > header, .pb-shell > footer { flex: none; }

.pb-panes {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
  flex-wrap: nowrap;       /* 절대 줄바꿈하지 않는다 */
}
.pb-pane {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: auto;
}
.pb-pane--fixed { flex: 0 0 auto; }   /* 폭은 인라인 style 이나 앱 CSS 에서 */
.pb-pane--grow  { flex: 1 1 0%; }     /* 남는 폭을 먹는다. 0% 라야 내용이 폭을 못 밀어낸다 */

/* 패널 경계. 드래그는 앱 JS 가 붙인다 */
.pb-resizer {
  flex: 0 0 8px;
  cursor: col-resize;
  background: linear-gradient(90deg, transparent 3px, var(--pb-border) 3px,
                                      var(--pb-border) 5px, transparent 5px);
  touch-action: none;
}
.pb-resizer:hover, .pb-resizer[data-dragging] {
  background: linear-gradient(90deg, transparent 2px, var(--pb-focus) 2px,
                                      var(--pb-focus) 6px, transparent 6px);
}

/* 접힌 패널 */
.pb-pane[hidden] { display: none; }
.pb-pane[hidden] + .pb-resizer,
.pb-resizer:first-child { display: none; }

/* ==========================================================================
   움직임 원칙: 150~200ms, transform/opacity 만. 의미가 있을 때만.
   동작 줄이기 설정이면 전부 끈다 (맨 아래 prefers-reduced-motion).
   ========================================================================== */
@keyframes pb-fade   { from { opacity: 0 } to { opacity: 1 } }
@keyframes pb-pop    { from { opacity: 0; transform: translateY(6px) scale(.97) } to { opacity: 1; transform: none } }
@keyframes pb-up     { from { opacity: 0; transform: translateY(12px) } to { opacity: 1; transform: none } }
@keyframes pb-down   { from { opacity: 0; transform: translateY(-8px) } to { opacity: 1; transform: none } }
@keyframes pb-slide  { from { opacity: 0; transform: translateX(-10px) } to { opacity: 1; transform: none } }
@keyframes pb-pulse  { 0%,100% { box-shadow: 0 0 0 0 rgba(241,100,73,.55) } 50% { box-shadow: 0 0 0 5px rgba(241,100,73,0) } }
@keyframes pb-blink  { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
@keyframes pb-flash  { from { background: var(--light-yellow) } to { background: transparent } }

/* display 를 지정한 키트 컴포넌트가 hidden 속성을 이기지 않게 */
.pb-btn[hidden], .pb-iconbtn[hidden], .pb-runstat[hidden], .pb-banner[hidden] { display: none !important; }

/* 작은 버튼 (배너·팝업 안) */
.pb-btn--sm { height: 32px; min-width: 32px; padding: 0 12px; font-size: var(--pb-fs-s); border-radius: var(--pb-radius-s); }
/* 바쁨 표시: 아이콘만 스피너로 바뀐다(JS). 누를 수 없게만 한다 */
[aria-busy="true"] { pointer-events: none; cursor: progress; }

/* ── 알림(toast) ──────────────────────────────────────────────────────── */
.pb-toasts {
  position: fixed; left: 50%; bottom: 52px; transform: translateX(-50%);
  z-index: 1000; display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none; max-width: calc(100vw - 32px);
}
.pb-toast {
  pointer-events: auto; cursor: default;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 16px; border-radius: 999px;
  background: #2b2b2b; color: #fff; box-shadow: var(--pb-shadow-2);
  font: 600 var(--pb-fs)/1.2 var(--pb-font);
  animation: pb-up .18s ease-out;
}
.pb-toast--ok    i { color: #8fe3a4; }
.pb-toast--info  i { color: var(--teal); }
.pb-toast--warn  i { color: var(--yellow); }
.pb-toast--error   { background: #7a1d0c; }
.pb-toast--out   { opacity: 0; transform: translateY(8px); transition: opacity .16s, transform .16s; }

/* ── 배너 (헤더 바로 아래 칸) ─────────────────────────────────────────── */
.pb-banner {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px var(--pb-pad);
  font: 600 var(--pb-fs)/1.35 var(--pb-font);
  border-bottom: 1px solid rgba(0,0,0,.12);
  animation: pb-down .18s ease-out;
  flex: none;
}
.pb-banner--warn  { background: #fdf3c4; color: #5a4300; }
.pb-banner--error { background: #fde3dd; color: #7a1d0c; }
.pb-banner--info  { background: #e3f6f9; color: #0b4d57; }
.pb-banner__text { flex: 1 1 240px; }
.pb-banner__actions { display: flex; gap: 8px; }
.pb-banner i.fa-plug-circle-xmark { animation: pb-blink 1.4s ease-in-out infinite; }
/* 연결이 끊긴 동안은 로봇에게 보내는 버튼을 못 누르게 한다 (앱이 data-needs-robot 을 붙인다) */
body[data-offline] [data-needs-robot] { opacity: .45; pointer-events: none; }

/* ── 팝업: 이 계열 앱들이 공통으로 쓰는 alert/confirm/prompt 팝업에 등장 효과 ─
   (클래스 이름이 세 앱 모두 같다. display:none -> flex 로 뜰 때마다 다시 재생된다) */
.pb .alert-popup, .pb .prompt-popup, .pb .confirm-popup, .pb .wifi-popup, .pb .pb-modal {
  animation: pb-fade .15s ease-out;
}
.pb .alert-popup-content, .pb .prompt-popup-content, .pb .confirm-popup-content,
.pb .wifi-popup-content, .pb .pb-modal__box {
  animation: pb-pop .17s ease-out;
}

/* ── 모달 (키트 자체) ───────────────────────────────────────────────── */
.pb-modal {
  position: fixed; inset: 0; z-index: 900;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,.42); padding: 16px;
}
.pb-modal[hidden] { display: none; }
.pb-modal__box {
  background: #fff; border-radius: 12px; box-shadow: var(--pb-shadow-2);
  width: min(760px, 100%); max-height: min(80vh, 720px);
  display: flex; flex-direction: column; overflow: hidden;
}
.pb-modal__head { display: flex; align-items: center; gap: 8px; padding: 12px 16px;
                  font: 700 var(--pb-fs-l)/1.2 var(--pb-font); border-bottom: 1px solid #eee; }
.pb-modal__head > span { flex: 1; }
.pb-modal__body { flex: 1; min-height: 0; overflow: auto; padding: 0; }
.pb-modal__body pre { margin: 0; padding: 14px 16px; font: 13px/1.55 var(--pb-mono);
                      white-space: pre; tab-size: 4; background: #fbfbf6; min-height: 100%; }
.pb-modal__foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 16px;
                  border-top: 1px solid #eee; }

/* ── 실행 상태 칩 ─────────────────────────────────────────────────────── */
.pb-runstat { display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
              height: 22px; padding: 0 10px; border-radius: 11px;
              font: 700 12px/1 var(--pb-font); white-space: nowrap; }
.pb-runstat[hidden] { display: none; }
.pb-runstat--running { background: #fde3dd; color: #8a2a17; }
.pb-runstat--running::before { content: ''; width: 8px; height: 8px; border-radius: 50%;
                               background: var(--red); animation: pb-blink 1s ease-in-out infinite; }
.pb-runstat--done  { background: var(--light-green); color: #2e5a1c; }
.pb-runstat--error { background: #fde3dd; color: #7a1d0c; }
/* 실행 중에는 정지 버튼이 숨쉬듯 빛난다 */
body[data-running] #stop:not(:disabled) { animation: pb-pulse 1.4s ease-in-out infinite; }

/* 접힌 패널을 펼칠 때 */
.pb-pane[data-force-show="1"] { animation: pb-slide .17s ease-out; }
/* 방금 생긴·지금 연 항목 강조 */
.pb-flash { animation: pb-flash 1.2s ease-out; }

/* ==========================================================================
   좁은 화면
   ========================================================================== */
/* 라벨은 폭이 모자랄 때만 접는다. 기준은 IDE 실측 — 라벨을 다 펴면 1024px 에서
   한국어 872px / 영어 927px 이 필요하다(파일 토글 포함). 900px 대에는 터미널 토글이 더 붙는다 */
@media (max-width: 999px) {
  .pb-header { padding-left: 8px; padding-right: 8px; }
  .pb-iconbtn { padding: 0 8px; }
  .pb-iconbtn__label { display: none; }
}
@media (max-width: 720px) {
  .pb-btn { padding: 0 12px; }
  .pb-btn--accent { padding: 0 16px; }
  .pb-header #logo_bt { display: none; }
  /* 폰 폭에서는 버튼 줄을 가로로 밀어 본다. 헤더 높이는 그대로 */
  .pb-header { gap: 8px; }
  .pb-iconbar { overflow-x: auto; scrollbar-width: none; }
  .pb-iconbar::-webkit-scrollbar { display: none; }
}

/* 동작 줄이기 설정을 존중한다 */
@media (prefers-reduced-motion: reduce) {
  .pb-btn:active:not(:disabled) { transform: none; }
  .pb *, .pb *::before, .pb *::after, .pb-toast, .pb-banner, .pb-modal, .pb-modal__box {
    animation: none !important; transition: none !important;
  }
}

/* ==========================================================================
   다듬기 — body.pb-refresh (ide · tools · classifier 가 켠다)
   ==========================================================================
   색은 그대로다(노랑 · 청록 · 빨강 · 연노랑). 촌스러워 보이던 건 색이 아니라 그리는
   방식이었다. 여기서는 모양만 바꾼다. 앱 CSS 를 고치지 않고 위에서 덮으므로 body 의
   클래스 하나를 빼면 예전 모양으로 돌아간다. PiBrain 은 켜기 전까지 영향이 없다.

     1. 버튼   진한 아래 그림자(0 4px)·알약 → 얇은 테두리·옅은 그림자·둥근 사각형
               (tools 왼쪽 메뉴처럼 nav 안의 버튼은 뺀다 — 아이콘 탭이다)
     2. 입력칸 연노랑 알약 → 흰 바탕·회색 테두리. 누르면 청록 링
     3. 슬라이더·체크박스 브라우저 기본 파랑 → 청록
     4. 표     연두 칸마다 테두리(엑셀) → 흰 바탕에 줄 구분선
     5. 노랑은 헤더 한 곳에. 상태바·표 머리·빈 미리보기 칸은 무채색 */

body.pb-refresh { accent-color: var(--teal); }      /* 슬라이더 · 체크박스 · 진행바 */

/* 1. 버튼 — 키트 버튼과 IDE 의 [블록|파이썬] 전환은 뺀다 */
body.pb-refresh button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button),
body.pb-refresh input[type="submit"],
body.pb-refresh label[for="upload"],
body.pb-refresh label[for="v_import_motion"] {
  border: 1px solid rgba(0, 0, 0, .14);
  border-radius: 9px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .10);
}
body.pb-refresh button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button):hover:not(:disabled),
body.pb-refresh input[type="submit"]:hover:not(:disabled),
body.pb-refresh label[for="upload"]:hover,
body.pb-refresh label[for="v_import_motion"]:hover {
  filter: brightness(.96);
  box-shadow: 0 2px 5px rgba(0, 0, 0, .14);
}
body.pb-refresh button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button):active:not(:disabled),
body.pb-refresh input[type="submit"]:active:not(:disabled),
body.pb-refresh label[for="upload"]:active,
body.pb-refresh label[for="v_import_motion"]:active {
  transform: translateY(1px);
  top: 0;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .18);
}
body.pb-refresh .upload_label:hover { box-shadow: 0 2px 5px rgba(0, 0, 0, .14); }

/* IDE [블록|파이썬] — 알약 두 개 → 키트 세그먼트와 같은 모양 */
body.pb-refresh div[name="codetype"] {
  border: 1px solid var(--pb-border);
  border-radius: 9px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}
body.pb-refresh div[name="codetype"] > button { border-radius: 0; }
body.pb-refresh div[name="codetype"] > button + button { border-left: 1px solid var(--pb-border); }

/* 2. 입력칸 — 키트 입력(.pb-field)과 터미널은 그대로 */
body.pb-refresh input:not([type]):not(.pb-field),
body.pb-refresh input[type="text"]:not(.pb-field),
body.pb-refresh input[type="number"]:not(.pb-field),
body.pb-refresh input[type="password"]:not(.pb-field),
body.pb-refresh input[type="search"]:not(.pb-field),
body.pb-refresh select:not(.pb-field),
body.pb-refresh textarea:not(.pb-field):not(.result):not(.terminal) {
  background: #fff;
  border: 1px solid var(--pb-border);
  border-radius: 8px;
  transition: border-color .12s ease, box-shadow .12s ease;
}
body.pb-refresh input:not([type]):not(.pb-field):focus,
body.pb-refresh input[type="text"]:not(.pb-field):focus,
body.pb-refresh input[type="number"]:not(.pb-field):focus,
body.pb-refresh input[type="password"]:not(.pb-field):focus,
body.pb-refresh input[type="search"]:not(.pb-field):focus,
body.pb-refresh select:not(.pb-field):focus,
body.pb-refresh textarea:not(.pb-field):not(.result):not(.terminal):focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(19, 191, 213, .22);
  outline: none;
}
/* 슬라이더 — 앱의 input 규칙(연노랑 바탕·테두리)이 여기에도 걸려 있었다. accent-color 만 주면
   브라우저가 안 채운 쪽을 진한 회색으로 칠해 무거워 보여서 트랙·손잡이를 직접 그린다.
   손잡이는 20px — 태블릿에서 손가락으로 잡을 수 있는 크기 */
body.pb-refresh input[type="range"] {
  -webkit-appearance: none; appearance: none;
  background: transparent; border: 0; border-radius: 0; box-shadow: none;
  height: 22px; padding: 0; cursor: pointer;
}
body.pb-refresh input[type="range"]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px; background: #d4d4d4;
}
body.pb-refresh input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; margin-top: -8px; border-radius: 50%;
  background: var(--teal); border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
body.pb-refresh input[type="range"]::-moz-range-track { height: 4px; border-radius: 2px; background: #d4d4d4; }
body.pb-refresh input[type="range"]::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--teal); border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
}
body.pb-refresh input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(19, 191, 213, .3); }

/* 3. 표 — IDE 파일 목록 · tools 모션 표 */
body.pb-refresh .fm-table-wrap,
body.pb-refresh .motion-table-wrap { border: 1px solid var(--pb-border); border-radius: 10px; }
body.pb-refresh .fm-table-wrap table { border-collapse: collapse; }
body.pb-refresh .fm-table-wrap tbody { background: #fff; }
body.pb-refresh .fm-table-wrap td { border-bottom: 1px solid #eee; padding-top: 3px; padding-bottom: 3px; }
body.pb-refresh .fm-table-wrap tr:hover td { background: var(--pb-surface); }
body.pb-refresh #fm_table tr.is-current td { background: var(--light-yellow); }
body.pb-refresh thead,
body.pb-refresh #motor_table thead td:nth-child(n + 2) { background: var(--pb-surface-2); font-weight: 700; }
body.pb-refresh tbody,
body.pb-refresh #motor_table tbody td:first-child { background: #fff; }

/* 4. 무채색으로 — 빈 미리보기 칸 · 테두리 · 상태바 */
body.pb-refresh img.preview-img { background-color: var(--pb-surface); border: 1px solid var(--pb-border); }
body.pb-refresh .result,
body.pb-refresh .terminal,
body.pb-refresh .blocklyDiv { border-color: var(--pb-border); border-radius: 10px; }
body.pb-refresh footer.pb-statusbar {
  background: var(--pb-surface);
  border-top: 1px solid var(--pb-border);
  box-shadow: none;
  color: var(--pb-text-dim);
}

/* 5. 헤더 — 그라데이션 대신 한 색, 아래는 얇은 선 */
body.pb-refresh .pb-header {
  background: var(--pb-brand);
  box-shadow: none;
  border-bottom: 1px solid rgba(0, 0, 0, .10);
}

/* tools 왼쪽 메뉴 라벨 — 기본은 숨기고 pb-v2 층이 보이게 한다 */
.pb-navlabel { display: none; }

/* ==========================================================================
   v2 — body.pb-v2  (도구·분류기 화면. 260929 부터 이것 하나 — 예전 v1 은 지웠다)
   ==========================================================================
   도구·분류기용. 템플릿 <body> 에 pb-refresh 와 같이 붙어 있고, 그 위에 얹힌다.
   IDE 는 자체 화면(ide/templates/index.html + ide/static/v2/)이라 이 층을 쓰지 않는다.
   색 값은 ide/static/v2/ide.css 와 같다 — 한쪽을 고치면 같이 고칠 것.
   상단바 노랑 · 주 동작 파랑 · 정지·지울 것 빨강 (자세한 건 루트 CLAUDE.md '화면 v2')

   화면 밝기 — html[data-theme] (pibo-ui.js 가 쿠키 pibo_theme 로 정한다. 기본 soft)
     light  흰 바탕
     soft   흰색 대신 옅은 회색. 오래된 노트북 패널에서 흰 바탕이 눈부신 걸 줄인다
     dark   #1d2025 계열 (순수 검정은 안 쓴다) */
body.pb-v2 {
  /* 색 — 시안 B (IDE 의 ide/static/v2/ide.css 와 같은 값)
     상단바 노랑 · 주 동작 파랑(--v2-dark) · 정지·지울 것 빨강 · accent 파랑 */
  --v2-frame:     #fbc92d;
  --v2-frame-2:   rgba(61, 47, 18, .10);
  --v2-frame-3:   rgba(61, 47, 18, .16);
  --v2-frame-line:rgba(61, 47, 18, .12);
  --v2-frame-ink: #3d2f12;   /* 상단바 글씨 — 짙은 갈색 */
  --v2-seg-on:    #fffdf6;   /* 상단바 탭 선택 — 흰 알약 */
  --v2-seg-on-ink:#3d2f12;
  --v2-seg-on-sh: 0 1px 3px rgba(61, 47, 18, .22), 0 0 0 .5px rgba(61, 47, 18, .14);
  --v2-btn-sh:    inset 0 1px 0 rgba(255, 255, 255, .22), 0 1px 2px rgba(37, 99, 235, .35), 0 0 0 .5px rgba(29, 79, 216, .55);   /* 주 동작 질감 (macOS 느낌) */
  --v2-frame-dim: #2f3542;
  --v2-rail:      #eceff3;   /* tools 왼쪽 메뉴 */
  --v2-dark:      #2563eb;   /* 주 동작(파랑) — IDE v2 의 실행·저장과 같다 */
  --v2-dark-hov:  #1d4fd8;
  --v2-on-dark:   #ffffff;
  --v2-ring:      rgba(37, 99, 235, .28);
  --v2-ring-soft: rgba(37, 99, 235, .22);
  /* soft (기본) */
  --v2-canvas:  #eceef4;   /* 바닥 */
  --v2-top:     #f5f6fa;
  --v2-surface: #f5f6fa;   /* 카드 */
  --v2-raise:   #fdfdff;   /* 떠 있는 것 · 입력칸 */
  --v2-sunken:  #eceef4;
  --v2-hover:   #e5e8ef;
  --v2-press:   #d9dde7;
  --v2-line:    #dcdfe7;
  --v2-line-2:  #c4cad6;
  --v2-ink:     #1a1e29;
  --v2-ink-2:   #474f62;
  --v2-ink-3:   #6b7386;
  --v2-yellow:  #f7b924;
  --v2-yellow-2:#e8a90c;
  --v2-yellow-t:#fdf0cc;
  --v2-on-yellow:#1a1e29;
  --v2-accent:  #2563eb;
  --v2-accent-t:#e3ecfd;
  --v2-accent-t2:#d6daf8;
  --v2-accent-ink:#2c3596;
  --v2-red:     #df4a4f;
  --v2-red-t:   #fbe4e5;
  --v2-red-t2:  #f6d2d4;
  --v2-red-ink: #9f1f25;
  --v2-term:    #151924;
  --v2-term-ink:#e3e6ee;
  --v2-shadow:  rgba(20, 24, 40, .18);
  --v2-scrim:   rgba(16, 20, 32, .5);
}
html[data-theme="light"] body.pb-v2 {
  --v2-frame: #ffcb2f; --v2-rail: #f1f3f6;
  --v2-canvas: #f1f3f8; --v2-top: #ffffff; --v2-surface: #ffffff; --v2-raise: #ffffff;
  --v2-sunken: #f5f6fa; --v2-hover: #f1f3f7; --v2-press: #e6e9f0;
  --v2-line: #e5e8ef; --v2-line-2: #d0d5df;
  --v2-ink-2: #525a6d; --v2-ink-3: #7a8294;
  --v2-accent-t: #e8eafc;
}
html[data-theme="dark"] body.pb-v2 {
  --v2-frame: #1b1e24; --v2-frame-2: rgba(255, 255, 255, .07); --v2-frame-3: rgba(255, 255, 255, .12); --v2-frame-line: #0d0f12;
  --v2-frame-ink: #eceef2; --v2-frame-dim: #c3c9d2; --v2-rail: #16191e;
  --v2-seg-on: #eceef2; --v2-seg-on-ink: #1b1e24; --v2-seg-on-sh: none;
  --v2-btn-sh: inset 0 1px 0 rgba(255, 255, 255, .3), 0 1px 2px rgba(0, 0, 0, .45);
  --v2-dark: #f2c94c; --v2-dark-hov: #e6bb34; --v2-on-dark: #1b1d20;
  --v2-canvas: #151922; --v2-top: #161a22; --v2-surface: #1a1e27; --v2-raise: #242935;
  --v2-sunken: #1f232d; --v2-hover: #282e3a; --v2-press: #313847;
  --v2-line: #2b303c; --v2-line-2: #3b4252;
  --v2-ink: #e4e7ef; --v2-ink-2: #adb4c4; --v2-ink-3: #858da0;
  --v2-yellow: #f5c04a; --v2-yellow-2: #e8b030; --v2-yellow-t: #3a311b; --v2-on-yellow: #1a1d24;
  --v2-accent: #8e98ff; --v2-accent-t: #262c52; --v2-accent-t2: #303769; --v2-accent-ink: #c4c9ff;
  --v2-red: #e8676b; --v2-red-t: #3e2226; --v2-red-t2: #4b282c; --v2-red-ink: #ffb0b2;
  --v2-term: #11141b;
  --v2-shadow: rgba(0, 0, 0, .45); --v2-scrim: rgba(0, 0, 0, .6);
  color-scheme: dark;
}
body.pb-v2 {
  /* 키트 토큰을 v2 값으로 */
  --pb-bg: var(--v2-raise);  --pb-surface: var(--v2-sunken);  --pb-surface-2: var(--v2-press);
  --pb-border: var(--v2-line-2);  --pb-border-strong: var(--v2-ink-3);
  --pb-text: var(--v2-ink);  --pb-text-dim: var(--v2-ink-2);
  --pb-brand: var(--v2-top);  --pb-brand-2: var(--v2-top);
  --pb-accent: var(--v2-yellow);  --pb-accent-edge: var(--v2-yellow-2);
  --pb-danger: var(--v2-red);  --pb-danger-edge: var(--v2-red);
  --pb-info: var(--v2-accent);  --pb-focus: var(--v2-accent);
  --pb-radius: 10px;  --pb-radius-s: 8px;
  --pb-shadow: 0 1px 2px rgba(0, 0, 0, .06);
  --pb-font: "Pretendard", -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo",
             "Noto Sans KR", "Malgun Gothic", "맑은 고딕", "Segoe UI", Roboto, sans-serif;
  /* 앱 CSS 가 직접 쓰는 옛 색 이름도 v2 값으로 */
  --light-yellow: var(--v2-yellow-t);
  --light-green: var(--v2-sunken);

  background: var(--v2-canvas);
  color: var(--v2-ink);
  font-family: var(--pb-font);
  accent-color: var(--v2-accent);
}
body.pb-v2 h1, body.pb-v2 h2, body.pb-v2 h3, body.pb-v2 h4, body.pb-v2 h5, body.pb-v2 h6,
body.pb-v2 header, body.pb-v2 button, body.pb-v2 input, body.pb-v2 select, body.pb-v2 textarea,
body.pb-v2 label, body.pb-v2 i.fa > span { font-family: var(--pb-font); }
body.pb-v2 [hidden] { display: none !important; }   /* 앱 CSS 의 display:flex 가 hidden 을 이기던 곳(녹음 파형 등) */
body.pb-v2 ::selection { background: var(--v2-ring-soft); }

/* ── 헤더 (틀) ──────────────────────────────────────────────────────────── */
body.pb-v2 .pb-header {
  height: 44px; min-height: 44px;              /* IDE v2 상단바와 같은 높이 */
  background: var(--v2-frame);
  border-bottom: 1px solid var(--v2-frame-line);
  color: var(--v2-frame-ink);
}
body.pb-v2 .pb-header__brand { font-size: 15px; gap: 8px; letter-spacing: -.1px; }
body.pb-v2 .pb-header__brand > i:first-child {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px;
  background: transparent; color: var(--v2-frame-ink); font-size: 16px;
}
/* THE MAKER 로고는 노랑 바탕에서 안 보인다. IDE v2 처럼 헤더에서 뺀다 */
body.pb-v2 .pb-header #logo_bt { display: none; }
body.pb-v2 .pb-header .pb-iconbtn { color: var(--v2-frame-dim); }
body.pb-v2 .pb-header .pb-iconbtn:hover { background: var(--v2-frame-2); color: var(--v2-frame-ink); }
body.pb-v2 .pb-header .pb-iconbtn:active { background: var(--v2-frame-3); }
body.pb-v2 .pb-iconbtn[aria-pressed="true"] { background: var(--v2-accent-t); color: var(--v2-accent-ink); }
body.pb-v2 .pb-iconbtn--danger:hover { background: var(--v2-red-t); color: var(--v2-red-ink); }
body.pb-v2 .pb-iconbtn:not(.pb-iconbtn--danger) + .pb-iconbtn--danger::before { border-color: var(--v2-frame-3); }
body.pb-v2 .pb-header .pb-field--sm {
  border-color: var(--v2-frame-3); background: var(--v2-frame-2); color: var(--v2-frame-ink);
}
body.pb-v2 .pb-header :focus-visible { outline-color: var(--v2-yellow); }
/* 헤더 안의 탭(분류기) */
body.pb-v2 .pb-header .pb-seg { background: var(--v2-frame-2); }
body.pb-v2 .pb-header .pb-seg > button { color: var(--v2-frame-dim); }
body.pb-v2 .pb-header .pb-seg > button:hover:not([aria-selected="true"]) { background: var(--v2-frame-3); color: var(--v2-frame-ink); }
body.pb-v2 .pb-header .pb-seg > button[aria-selected="true"],
body.pb-v2 .pb-header .cls-tabs > button[aria-selected="true"] { background: var(--v2-seg-on); color: var(--v2-seg-on-ink); box-shadow: var(--v2-seg-on-sh); }

/* ── 세그먼트: 눌린 홈 위에 떠 있는 알약 ─────────────────────────────────── */
body.pb-v2 .pb-seg,
body.pb-v2 div[name="codetype"] {
  background: var(--v2-sunken); border: 0; border-radius: 9px; padding: 3px; gap: 2px;
  box-shadow: none; overflow: visible;
}
body.pb-v2 .pb-seg > button,
body.pb-v2 div[name="codetype"] > button {
  background: transparent; color: var(--v2-ink-2); border: 0; border-radius: 7px;
  box-shadow: none; font-weight: 600;
}
body.pb-v2 .pb-seg > button + button,
body.pb-v2 div[name="codetype"] > button + button { border-left: 0; }
body.pb-v2 .pb-seg > button:hover:not([aria-selected="true"]),
body.pb-v2 div[name="codetype"] > button:hover:not(.checked) { background: var(--v2-hover); color: var(--v2-ink); }
body.pb-v2 .pb-seg > button[aria-selected="true"],
body.pb-v2 .cls-tabs > button[aria-selected="true"],
body.pb-v2 div[name="codetype"] > button.checked {
  background: var(--v2-raise); color: var(--v2-ink); font-weight: 700;
  box-shadow: 0 1px 2px rgba(0,0,0,.14), 0 0 0 1px rgba(0,0,0,.04);
}
body.pb-v2 .pb-header .pb-seg { height: 32px; }
body.pb-v2 div[name="codetype"] > button { width: auto; min-width: 64px; padding: 0 14px; }

/* ── 키트 버튼 ──────────────────────────────────────────────────────────── */
body.pb-v2 .pb-btn { background: var(--v2-raise); color: var(--v2-ink); border-color: var(--v2-line-2); box-shadow: var(--pb-shadow); font-weight: 600; }
body.pb-v2 .pb-btn:hover:not(:disabled) { background: var(--v2-hover); border-color: var(--v2-line-2); }
/* 주 동작은 먹색(IDE v2 의 실행·저장과 같다). 노랑은 상단바에만 */
body.pb-v2 .pb-btn--accent { background: var(--v2-dark); border-color: transparent; color: var(--v2-on-dark); font-weight: 700; box-shadow: var(--v2-btn-sh); }
body.pb-v2 .pb-btn:active:not(:disabled) { filter: brightness(.93); }
body.pb-v2 .pb-btn--accent:hover:not(:disabled) { background: var(--v2-dark-hov); border-color: transparent; }
body.pb-v2 .pb-btn--accent > i { color: inherit; }
body.pb-v2 .pb-btn--danger { background: var(--v2-red); border-color: transparent; color: #fff; }
body.pb-v2 .pb-btn--ghost,
body.pb-v2 .pb-btn--ghost:hover:not(:disabled) { box-shadow: none; border-color: transparent; background: transparent; }
body.pb-v2 .pb-btn--ghost:hover:not(:disabled) { background: var(--v2-hover); }
body.pb-v2 .pb-btn--ghost[aria-pressed="true"] { background: var(--v2-accent-t); color: var(--v2-accent-ink); }
body.pb-v2 .pb-btn:disabled { background: var(--v2-sunken); color: var(--v2-ink-3); border-color: var(--v2-line); opacity: .7; }

/* ── 앱이 직접 그리던 버튼 (tools). 기본은 보조 버튼 ─────────────────────── */
body.pb-v2 button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button),
body.pb-v2 input[type="submit"],
body.pb-v2 label[for="upload"],
body.pb-v2 label[for="v_import_motion"] {
  background: var(--v2-raise) !important;
  color: var(--v2-ink);
  border: 1px solid var(--v2-line-2);
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
  font-size: 14px; font-weight: 600;
  filter: none;
}
body.pb-v2 button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button):hover:not(:disabled),
body.pb-v2 input[type="submit"]:hover:not(:disabled),
body.pb-v2 label[for="upload"]:hover,
body.pb-v2 label[for="v_import_motion"]:hover {
  background: var(--v2-hover) !important; filter: none;
}
body.pb-v2 button:not(.pb-btn):not(.pb-iconbtn):not(.pb-seg > button):not([name="codetype"] > button):not(nav button):active:not(:disabled) {
  transform: translateY(1px); box-shadow: none;
}
body.pb-v2 main button:not(.pb-btn):not(.pb-iconbtn) > i,
body.pb-v2 main label[for="upload"] > i { color: var(--v2-ink-2); }

/* 앱의 청록 버튼(.btn-teal) → 옅은 인디고 (tools 의 프레임 버튼들) */
body.pb-v2 button.btn-teal {
  background: var(--v2-accent-t) !important; color: var(--v2-accent-ink);
  border-color: transparent; box-shadow: none;
}
body.pb-v2 button.btn-teal:hover:not(:disabled) { background: var(--v2-accent-t2) !important; border-color: transparent; }
body.pb-v2 button.btn-teal > i { color: inherit; }
/* 빨강 버튼 → 옅은 빨강 */
body.pb-v2 button.btn-red {
  background: var(--v2-red-t) !important; color: var(--v2-red-ink);
  border-color: transparent; box-shadow: none;
}
body.pb-v2 button.btn-red:hover:not(:disabled) { background: var(--v2-red-t2) !important; border-color: transparent; }
body.pb-v2 button.btn-red > i { color: inherit; }
body.pb-v2 button.disabled, body.pb-v2 button:disabled { opacity: .45; filter: none; }

/* v1 IDE 에 이 층이 걸린 경우(정적 서버로 index.html 을 직접 연 때)의 주 동작 */
body.pb-v2 #execute {
  background: var(--v2-yellow) !important; color: var(--v2-on-yellow);
  border-color: transparent; font-weight: 700;
}
body.pb-v2 #execute:hover:not(:disabled) { background: var(--v2-yellow-2) !important; }
body.pb-v2 #execute > i { color: var(--v2-on-yellow); }
body.pb-v2[data-running] #stop:not(.disabled):not(:disabled) { background: var(--v2-red) !important; color: #fff; opacity: 1; }
body.pb-v2[data-running] #stop:not(.disabled):not(:disabled) > i { color: #fff; }

/* ── 입력칸 ─────────────────────────────────────────────────────────────── */
body.pb-v2 input:not([type]):not(.pb-field),
body.pb-v2 input[type="text"]:not(.pb-field),
body.pb-v2 input[type="number"]:not(.pb-field),
body.pb-v2 input[type="password"]:not(.pb-field),
body.pb-v2 input[type="search"]:not(.pb-field),
body.pb-v2 select:not(.pb-field),
body.pb-v2 textarea:not(.pb-field):not(.result):not(.terminal),
body.pb-v2 .pb-field {
  background: var(--v2-raise); border-color: var(--v2-line-2); border-radius: 8px; color: var(--v2-ink);
}
body.pb-v2 textarea:disabled:not(.result):not(.terminal) { background: var(--v2-sunken); color: var(--v2-ink); opacity: 1; }
body.pb-v2 input:focus:not([type="range"]), body.pb-v2 select:focus, body.pb-v2 textarea:focus:not(.result):not(.terminal),
body.pb-v2 .pb-field:focus {
  border-color: var(--v2-accent) !important;
  box-shadow: 0 0 0 3px var(--v2-ring) !important; outline: none;
}
body.pb-v2 input::placeholder { color: var(--v2-ink-3); }
body.pb-v2 input[type="range"]::-webkit-slider-runnable-track { background: var(--v2-line-2); }
body.pb-v2 input[type="range"]::-webkit-slider-thumb { background: var(--v2-accent); border-color: var(--v2-raise); }
body.pb-v2 input[type="range"]::-moz-range-track { background: var(--v2-line-2); }
body.pb-v2 input[type="range"]::-moz-range-thumb { background: var(--v2-accent); border-color: var(--v2-raise); }

/* ── 패널: 바닥 위 카드 ───────────────────────────────────────────────────── */
body.pb-v2 .pb-panes { background: var(--v2-canvas); padding: 8px; }
body.pb-v2 .pb-pane {
  background: var(--v2-surface);
  border: 1px solid var(--v2-line);
  border-radius: 12px;
}
body.pb-v2 .pb-pane[hidden] { display: none; }
body.pb-v2 .pb-resizer { flex-basis: 8px; background: transparent; }
body.pb-v2 .pb-resizer:hover, body.pb-v2 .pb-resizer[data-dragging] {
  background: linear-gradient(90deg, transparent 3px, var(--v2-accent) 3px, var(--v2-accent) 5px, transparent 5px);
}

/* ── tools: 왼쪽 메뉴(옅은 레일) + 카드 ─────────────────────────────────────── */
body.pb-v2 main > nav { background: var(--v2-rail); border-right: 1px solid var(--v2-line); width: 64px; }
body.pb-v2 nav ul { margin: 0; padding: 6px 0; display: flex; flex-direction: column; align-items: center; gap: 4px; }
body.pb-v2 nav button {
  width: 52px; padding: 8px 0 6px; border-radius: 10px; opacity: 1;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  color: var(--v2-ink-3); font: 600 10.5px/1 var(--pb-font);
}
body.pb-v2 nav button::after { opacity: .5; transform: scale(.8); }   /* 검은 PNG 아이콘 */
html[data-theme="dark"] body.pb-v2 nav button::after { filter: invert(1); }
body.pb-v2 nav button > .pb-navlabel { order: 2; }
body.pb-v2 nav button:hover { background: var(--v2-hover); color: var(--v2-ink); }
body.pb-v2 nav button:hover::after { opacity: .8; }
body.pb-v2 nav button.menu-selected { background: var(--v2-raise); color: var(--v2-ink); position: relative; box-shadow: 0 1px 2px rgba(0,0,0,.1); }
body.pb-v2 nav button.menu-selected::after { opacity: 1; }
body.pb-v2 main > div.content.pb-pane { border: 0; border-radius: 0; background: var(--v2-canvas); padding: 10px 12px 16px; }
body.pb-v2 .pb-navlabel { display: block; }

/* tools 카드(section) — 그림자 대신 선, 제목은 형광펜 대신 굵은 글자 */
body.pb-v2 article { gap: 10px; }
body.pb-v2 article > section {
  background: var(--v2-surface); color: var(--v2-ink);
  border: 1px solid var(--v2-line); border-radius: 12px; box-shadow: none;
  padding: 14px 16px; min-height: 0;
}
body.pb-v2 .mark {
  background: none; font-size: 15px; font-weight: 800; margin: 2px 0 10px;
  display: flex; align-items: center; gap: 8px;
}
body.pb-v2 .mark::before {
  content: ""; width: 4px; height: 16px; border-radius: 2px; background: var(--v2-yellow-2); flex: none;
}
body.pb-v2 article section header { font-size: 13px; font-weight: 700; color: var(--v2-ink-2); margin: 10px 0 6px; }
body.pb-v2 article section .label { color: var(--v2-ink-2); }
body.pb-v2 .pibo-section > img { filter: opacity(.55); }
/* 어둡게에서도 로봇은 흰색 그대로(실물 색). 전엔 뒤집어 검게 보였다 */
html[data-theme="dark"] body.pb-v2 .pibo-section > img { filter: opacity(.9); }
body.pb-v2 #v_img { border-radius: 10px; background: var(--v2-sunken); }
body.pb-v2 .motion-input-label .label { color: var(--v2-ink); }
body.pb-v2 .toggle-switch { background: var(--v2-line-2); }

/* ── 표 ─────────────────────────────────────────────────────────────────── */
body.pb-v2 .fm-table-wrap,
body.pb-v2 .motion-table-wrap { border-color: var(--v2-line); background: var(--v2-raise); }
body.pb-v2 .fm-table-wrap td { border-bottom-color: var(--v2-line); }
body.pb-v2 .fm-table-wrap tr:hover td { background: var(--v2-hover); }
body.pb-v2 #fm_table tr.is-current td { background: var(--v2-yellow-t); }
body.pb-v2 thead,
body.pb-v2 #motor_table thead td:nth-child(n + 2) { background: var(--v2-sunken); color: var(--v2-ink-2); }
body.pb-v2 tbody,
body.pb-v2 #motor_table tbody td:first-child,
body.pb-v2 #motor_table tbody td:nth-child(n + 2) { background: var(--v2-raise); color: var(--v2-ink); }
body.pb-v2 #motor_table tbody td:first-child { background: var(--v2-sunken); }

/* ── 편집기 · 터미널 ────────────────────────────────────────────────────── */
body.pb-v2 .blocklyDiv { border-color: var(--v2-line); }
body.pb-v2 .result,
body.pb-v2 .terminal {
  background: var(--v2-term) !important; color: var(--v2-term-ink) !important;
  border: 1px solid var(--v2-term) !important; border-radius: 10px;
  font-family: var(--pb-mono) !important; font-size: 13.5px !important; line-height: 1.55;
  padding: 10px 12px;
}
body.pb-v2 .result::-webkit-scrollbar-thumb,
body.pb-v2 .terminal::-webkit-scrollbar-thumb { background: #3a4048; }
body.pb-v2 .result::-webkit-scrollbar-track,
body.pb-v2 .terminal::-webkit-scrollbar-track { background: transparent; }
body.pb-v2 img.preview-img { background-color: var(--v2-sunken); border-color: var(--v2-line); border-radius: 10px; }

/* ── 상태바 ─────────────────────────────────────────────────────────────── */
body.pb-v2 footer.pb-statusbar {
  background: var(--v2-frame); border-top: 1px solid var(--v2-frame-line); box-shadow: none;
  color: var(--v2-frame-dim); min-height: 26px; height: auto; padding-top: 3px; padding-bottom: 3px;
  font-size: 12.5px !important;
}
body.pb-v2 footer.pb-statusbar * { font-size: 12.5px !important; }

/* ── 대화상자 (키트 · tools 의 alert/confirm/prompt) ──────────────────────── */
body.pb-v2 .pb-modal__box,
body.pb-v2 .alert-popup-content, body.pb-v2 .prompt-popup-content, body.pb-v2 .confirm-popup-content,
body.pb-v2 .usedata-popup-content, body.pb-v2 .wifi-popup-content {
  background: var(--v2-raise); color: var(--v2-ink);
  border: 1px solid var(--v2-line); border-radius: 16px;
  box-shadow: 0 20px 48px var(--v2-shadow);
}
body.pb-v2 .alert-popup-content p, body.pb-v2 .prompt-popup-content p, body.pb-v2 .confirm-popup-content p { color: var(--v2-ink); }
body.pb-v2 .alert-popup, body.pb-v2 .prompt-popup, body.pb-v2 .confirm-popup { background-color: var(--v2-scrim); }
body.pb-v2 .pb-modal__head, body.pb-v2 .pb-modal__foot { background: var(--v2-raise); border-color: var(--v2-line); }
body.pb-v2 #alertOkBtn, body.pb-v2 #confirmOkBtn, body.pb-v2 #promptOkBtn {
  background: var(--v2-dark) !important; color: var(--v2-on-dark); border-color: transparent; box-shadow: var(--v2-btn-sh);
}
body.pb-v2 #alertOkBtn:hover, body.pb-v2 #confirmOkBtn:hover, body.pb-v2 #promptOkBtn:hover { background: var(--v2-dark-hov) !important; }
body.pb-v2 #confirmCancelBtn, body.pb-v2 #promptCancelBtn { color: var(--v2-ink); }

/* ── 분류기가 직접 쓰던 흰색 ────────────────────────────────────────────── */
body.pb-v2 .store-card, body.pb-v2 .code { background: var(--v2-raise); color: var(--v2-ink); border-color: var(--v2-line); }
body.pb-v2 .confusion th { background: var(--v2-surface); color: var(--v2-ink-2); }
body.pb-v2 .saved .fa-circle-check { color: var(--v2-accent); }
/* [꾹 눌러 모으기] 누르는 동안은 빨강(모으는 중) — 위의 먹색 주 동작 규칙보다 앞서야 한다 */
body.pb-v2 .cls-hold.is-on { background: var(--v2-red); border-color: transparent; color: #fff; }

/* ── 다듬기 (260924): 꽉 찬 파랑은 한 구역에 하나, 수치는 한 색 ─────────────
   분류기 학습 화면에 파란 판이 [꾹 눌러 모으기]×종류 수 + [AI 가르치기] + [저장] 으로
   네다섯 개 떠 있었고, 수치는 노랑(장수·막대·결과)·청록(표)·베이지가 섞여 있었다.
   꽉 찬 파랑은 [AI 가르치기]·[저장] 만 남기고 나머지는 옅은 파랑, 수치는 파랑 한 가지로 */
body.pb-v2 {
  --v2-tint:     var(--v2-accent-t);
  --v2-tint-hov: #d4e1fb;
  --v2-tint-ink: #1d4ed8;   /* 옅은 파랑 위 글씨 — #e3ecfd 대비 5.6:1 */
  --v2-meter:    #b9c1ce;   /* 막대의 나머지 값 */
}
html[data-theme="dark"] body.pb-v2 { --v2-tint-hov: var(--v2-accent-t2); --v2-tint-ink: var(--v2-accent-ink); --v2-meter: #4a5264; }
body.pb-v2 .cls-hold:not(.is-on),
body.pb-v2 .store-card .pb-btn--accent {
  background: var(--v2-tint); color: var(--v2-tint-ink); border-color: transparent; box-shadow: none;
}
body.pb-v2 .cls-hold:not(.is-on):hover:not(:disabled),
body.pb-v2 .store-card .pb-btn--accent:hover:not(:disabled) { background: var(--v2-tint-hov); border-color: transparent; }
/* [AI 가르치기] 는 크기 대신 자리로 눈에 띈다. 64px 판이 화면을 누르고 있었다 */
body.pb-v2 #train_btn { min-height: 48px; height: 48px; font-size: 16px; }

body.pb-v2 .cls-count, body.pb-v2 .store-acc { background: var(--v2-sunken); color: var(--v2-ink-2); }
body.pb-v2 .bar-track { height: 10px; background: var(--v2-sunken); }
body.pb-v2 .bar-fill { background: var(--v2-meter); }
body.pb-v2 .bar.is-best .bar-fill { background: var(--v2-accent); }
body.pb-v2 .confusion td.hit  { background: rgba(37, 99, 235, calc(var(--a) * .26)); color: var(--v2-tint-ink); }
body.pb-v2 .confusion td.miss { background: rgba(223, 74, 79, calc(var(--a) * .3 + .06)); }
body.pb-v2 .answer { background: var(--v2-tint); color: var(--v2-tint-ink); }
body.pb-v2 .answer.is-unsure { background: var(--v2-sunken); color: var(--v2-ink-3); }
body.pb-v2 .model-pick[aria-selected="true"] { background: var(--v2-tint); border-color: rgba(37, 99, 235, .45); }
html[data-theme="dark"] body.pb-v2 .model-pick[aria-selected="true"] { border-color: var(--v2-accent); }
body.pb-v2 #thr, body.pb-v2 #train_prog progress { accent-color: var(--v2-accent); }

/* 상단바의 언어 고르기: 날것 select 판 대신 IDE 상단바 버튼처럼 */
body.pb-v2 .pb-header select.pb-field--sm {
  background-color: transparent; border-color: transparent; font-weight: 600; cursor: pointer;
}
body.pb-v2 .pb-header select.pb-field--sm:hover { background-color: var(--v2-frame-2); }
body.pb-v2 .pb-header select.pb-field--sm option { background: var(--v2-raise); color: var(--v2-ink); }

/* ── 도구 [동작] 정리 (260928) ──────────────────────────────────────────────
   'off 1' 표시, 로봇 그림 위에 겹친 모터 칸, ⋮ 로 잘린 버튼 줄, 비어 있는 500px 표,
   '모두 지우기' 두 개를 정리했다. id 는 그대로라 index.js 는 상태 칩(data-label) 한 곳만 바뀌었다 */

/* 상단바 로봇 상태: 칩 하나. 기다리는 동안 점이 깜빡인다 */
body.pb-v2 #onoff_count, body.pb-v2 #onoff_val > i { display: none; }
body.pb-v2 #onoff_val[data-label] {
  display: inline-flex; align-items: center; gap: 6px; height: 24px; margin-left: 10px; padding: 0 10px;
  border-radius: 12px; background: var(--v2-frame-2); color: var(--v2-frame-ink); font-size: 12px; font-weight: 600;
}
body.pb-v2 #onoff_val[data-label]::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--v2-frame-dim); }
body.pb-v2 #onoff_val[data-label]::after { content: attr(data-label); }
body.pb-v2 #onoff_val[data-state="on"]::before { background: #1f9d62; box-shadow: 0 0 0 2px rgba(31, 157, 98, .25); }
body.pb-v2 #onoff_val[data-state="off"]::before { animation: pb-conn-blink 1.2s ease-in-out infinite; }
@keyframes pb-conn-blink { 50% { opacity: .25; } }

/* 왼쪽 윗줄: 떠 있던 [원래자세] 와 float 두 덩이를 한 줄로 */
body.pb-v2 .pibo-section > div:first-child {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; position: relative; z-index: 2;
}
body.pb-v2 .pibo-section > div:first-child > div { float: none !important; display: flex; align-items: center; gap: 6px; }
body.pb-v2 #init_bt { position: static; }
body.pb-v2 #m_time_val { width: 72px; text-align: right; }
body.pb-v2 .motion-unit { margin-left: -2px; color: var(--v2-ink-3); font-size: 13px; }

/* ── [동작] 사진 위 번호 점 + 옆 조작 패널 (260928) ─────────────────────────────
   사진에 모터 칸 10개를 겹쳐 두던 것을 번호 점(현재 각도)으로 바꾸고, 누른 모터 하나를 옆 패널에서
   크게 조절한다(슬라이더 · −5 −1 +1 +5 · 숫자). 예전 칸(.motion-input-group)은 값 저장소로만 남긴다 */
body.pb-v2 .motion-input-group { display: none !important; }
body.pb-v2 .mbody { display: flex; align-items: center; justify-content: center; gap: 14px; min-height: 0; min-width: 0; }
/* 그림 폭은 index.js 의 fitStage 가 가운데 줄 높이·폭에 맞춰 준다(기본 320px) */
body.pb-v2 .mstage { position: relative; flex: none; width: 320px; }
body.pb-v2 .mstage > img { display: block; width: 100%; height: auto; margin: 0; filter: opacity(.6); }
html[data-theme="dark"] body.pb-v2 .mstage > img { filter: opacity(.9); }
body.pb-v2 #article_motion .mstage .mdot {
  position: absolute; transform: translate(-50%, -50%); display: inline-flex; align-items: center; gap: 5px;
  height: 30px; padding: 0 9px 0 3px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  background: var(--v2-raise) !important; color: var(--v2-ink) !important; border: 2px solid var(--v2-accent) !important;
  box-shadow: 0 2px 6px rgba(20, 24, 40, .18) !important; font-size: 13px !important; font-weight: 700;
}
body.pb-v2 #article_motion .mstage .mdot > b {
  display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%;
  background: var(--v2-accent); color: #fff; font-size: 12px;
}
body.pb-v2 #article_motion .mstage .mdot .mdot__v { font-variant-numeric: tabular-nums; min-width: 1.6em; text-align: right; }
body.pb-v2 #article_motion .mstage .mdot:hover { transform: translate(-50%, -50%) scale(1.06); }
body.pb-v2 #article_motion .mstage .mdot[aria-pressed="true"] { background: var(--v2-accent) !important; color: #fff !important; }
body.pb-v2 #article_motion .mstage .mdot[aria-pressed="true"] > b { background: #fff; color: var(--v2-accent); }
html[data-theme="dark"] body.pb-v2 #article_motion .mstage .mdot[aria-pressed="true"] { color: #10131a !important; }
/* 왼쪽 칸은 사진+패널 크기만큼만(예전 모터 칸 때문에 걸려 있던 min-width 590 해제) → 오른쪽 표 폭이 는다 */
body.pb-v2 #article_motion > .pibo-section { min-width: 0; width: auto; }
/* 저장 칸 머리줄의 내보내기·가져오기·모두 지우기는 보조 동작이라 늘 아이콘만(이름은 title).
   글자를 붙이면 영문에서 1200~1440px 까지 줄이 넘쳤다 */
body.pb-v2 .mlib__tools > button > span, body.pb-v2 .mlib__tools > label > span { display: none; }
body.pb-v2 .mlib__tools > button, body.pb-v2 .mlib__tools > label { width: 32px; padding: 0 !important; justify-content: center; }
@media (max-width: 1199px) and (min-width: 901px) {
  body.pb-v2 #motor_table td { padding: 3px 1px; font-size: 12.5px; }
  /* 표 버튼 줄이 좁다: [이 줄 지우기](⌫)·[표 비우기](휴지통)는 아이콘만, 이름은 title */
  body.pb-v2 #article_motion #ms_delete > span, body.pb-v2 #article_motion #init_frame_bt > span { display: none; }
}
@media (max-width: 1439px) and (min-width: 901px) {
  /* 줄을 고르면 [이 줄 지우기] 가 붙어 영문에서 최대 22px 넘쳤다 → 버튼 여백만 줄인다(글자는 그대로) */
  body.pb-v2 #article_motion .motion-record > div:first-child { gap: 4px; }
  body.pb-v2 #article_motion .motion-record > div:first-child > button { padding-left: 8px !important; padding-right: 8px !important; }
}
@media (max-width: 1299px) and (min-width: 901px) {
  body.pb-v2 .mhead__keys { display: none; }
  /* 태블릿 가로(1024~1280): 카드 폭이 좁아 사진이 폭에 걸려 가운데 줄의 절반~⅔ 만 찼다 →
     조작 패널을 사진 아래로 내리고 두 칸(각도 | 시간·추가·수정)으로 편다. 사진 크기는 fitStage 가 남는 높이로 */
  body.pb-v2 .mbody { flex-direction: column; justify-content: flex-start; gap: 12px; }
  body.pb-v2 .mside { width: 100% !important; align-self: stretch !important; }
  body.pb-v2 .mpanel { display: grid !important; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 16px; row-gap: 8px; align-items: center; }
  body.pb-v2 .mpanel > :not(.mpanel__add) { grid-column: 1; }
  body.pb-v2 .mpanel__add { grid-column: 2; grid-row: 1 / span 5; align-self: stretch; justify-content: center;
    margin: 0; padding: 0 0 0 16px; border-top: 0; border-left: 1px solid var(--v2-line); }
}
body.pb-v2 .mside { flex: none; width: 196px; min-width: 0; display: flex; flex-direction: column; gap: 10px; }
/* 조작 패널·장면 만들기·저장 칸은 같은 상자 모양 */
body.pb-v2 .mpanel, body.pb-v2 .mlib {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px; border-radius: 12px; background: var(--v2-raise); border: 1px solid var(--v2-line);
}
body.pb-v2 .mpanel__name { font-size: 15px; font-weight: 800; color: var(--v2-ink); }
body.pb-v2 .mpanel__val { display: flex; align-items: baseline; gap: 4px; }
body.pb-v2 .mpanel__val > input {
  width: 100%; height: 46px; padding: 0 8px; text-align: center; font-size: 26px; font-weight: 800;
  font-variant-numeric: tabular-nums; border-radius: 10px;
}
body.pb-v2 .mpanel__val > span { font-size: 20px; font-weight: 700; color: var(--v2-ink-3); }
body.pb-v2 .mpanel > input[type="range"] { width: 100%; accent-color: var(--v2-accent); height: 28px; }
body.pb-v2 .mpanel__steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
body.pb-v2 #article_motion .mpanel__steps > button {
  height: 36px; padding: 0 !important; font-size: 15px !important; font-weight: 700; font-variant-numeric: tabular-nums;
}
body.pb-v2 .mpanel__lim { font-size: 12px; color: var(--v2-ink-3); text-align: center; font-variant-numeric: tabular-nums; }
@media (max-width: 900px) {
  body.pb-v2 .mbody { justify-content: center; margin: 10px 0; } body.pb-v2 .mside { width: 240px; }
  body.pb-v2 .mlib { margin-top: 10px; }
  body.pb-v2 #article_motion > .pibo-section { width: 100%; }
  body.pb-v2 .mstage { width: min(42vw, 340px); }
}

/* 조작 패널 아래: 시간 · [추가·수정] · 한 줄 안내. 각도를 맞추고 바로 아래에서 누른다(260928).
   따로 상자로 두었을 땐 내용은 한 줄인데 상자만 커서(140px) 패널 안으로 넣었다 */
body.pb-v2 .mpanel__add { display: flex; flex-direction: column; gap: 8px; margin-top: 2px; padding-top: 12px; border-top: 1px solid var(--v2-line); }
body.pb-v2 .mpanel__time { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--v2-ink-2); }
body.pb-v2 .mpanel__time > span:first-child { flex: none; width: 30px; }
body.pb-v2 #ms_time_slot { display: contents; }
body.pb-v2 .mpanel__add #m_time_val { flex: 1 1 0; min-width: 0; width: auto; height: 38px; text-align: right; font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }
body.pb-v2 .mpanel__add .motion-unit { margin: 0; flex: none; }
body.pb-v2 #article_motion .mpanel__add #add_frame_bt {
  width: 100%; height: 42px; justify-content: center; font-size: 15px !important; font-weight: 800;
  background: var(--v2-dark) !important; color: var(--v2-on-dark) !important; border-color: transparent !important; box-shadow: var(--v2-btn-sh) !important;
}
body.pb-v2 #article_motion .mpanel__add #add_frame_bt:hover { background: var(--v2-dark-hov) !important; }
body.pb-v2 #article_motion .mpanel__add #add_frame_bt > i { color: inherit; }
body.pb-v2 .mpanel__hint { font-size: 11.5px; color: var(--v2-ink-3); text-align: center; }
/* [이 줄 지우기]: 표를 고르면 표 버튼 줄의 [표 비우기] 옆에 나온다. 판 없는 빨간 글씨 */
body.pb-v2 #article_motion #ms_delete {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto; white-space: nowrap;
  background: transparent !important; border-color: transparent !important; box-shadow: none !important; color: var(--v2-red-ink) !important;
}
body.pb-v2 #article_motion #ms_delete[hidden] { display: none; }
body.pb-v2 #article_motion #ms_delete:hover { background: var(--v2-red-t) !important; }
body.pb-v2 #article_motion #ms_delete:not([hidden]) + #init_frame_bt { margin-left: 0; }
body.pb-v2 #m_time_val.is-bumped { animation: pb-bump .6s ease-out; }
@keyframes pb-bump { from { box-shadow: 0 0 0 3px var(--v2-accent); } to { box-shadow: 0 0 0 3px transparent; } }
/* 윗줄(왼쪽): [원래자세] · 키 안내 — 오른쪽 [반복·실행·정지] 줄과 높이를 맞춘다 */
body.pb-v2 .mhead { display: flex; align-items: center; gap: 14px; min-width: 0; white-space: nowrap; }
body.pb-v2 #article_motion .mhead > #init_bt { position: static !important; height: 34px; padding: 0 12px; font-size: 13px; }
body.pb-v2 .mhead__keys { margin-left: auto; font-size: 11.5px; color: var(--v2-ink-3); overflow: hidden; text-overflow: ellipsis; }
@media (hover: none) { body.pb-v2 .mhead__keys { display: none; } }   /* 태블릿엔 키보드가 없다 */
/* 고른 표 줄 */
body.pb-v2 #motor_table tbody tr[data-sel] td { background: var(--v2-accent-t) !important; color: var(--v2-accent-ink, var(--v2-accent)); font-weight: 700; }
body.pb-v2 #motor_table tbody tr[data-sel] td:first-child { box-shadow: inset 3px 0 0 var(--v2-accent); }

/* 모터 칸: 로봇 그림 위에서도 읽히게 작은 카드로. 자리(행 간격)는 v1 그대로 (v2 에서는 위 번호 점으로 바뀌어 숨김) */
body.pb-v2 .motion-input-row > div {
  background: var(--v2-raise); background: color-mix(in srgb, var(--v2-raise) 90%, transparent);
  border: 1px solid var(--v2-line); border-radius: 10px; padding: 4px 8px 0; box-shadow: 0 1px 2px rgba(20, 24, 40, .06);
}
body.pb-v2 .pibo-section { padding-bottom: 28px; }   /* 카드가 된 발 칸(M0·M6)이 아래에서 잘리지 않게 */
body.pb-v2 .motion-input-label { gap: 6px; }
body.pb-v2 .motion-input-label span { font-size: 12.5px; font-weight: 600; letter-spacing: 0; color: var(--v2-ink-2); white-space: nowrap; }
body.pb-v2 .motion-input-label input { width: 50px; height: 24px; padding: 0 4px; text-align: right; font-size: 13px; }
body.pb-v2 .motion-input-row input[type="range"] { width: 100%; margin: 2px 0 0; accent-color: var(--v2-accent); }

/* 오른쪽 버튼 줄: ⋮·'-' 대신 간격으로 묶는다 */
body.pb-v2 .pb-sep { display: none; }
body.pb-v2 .motion-record > div:first-child,
body.pb-v2 .motion-name-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
body.pb-v2 .motion-record > div:first-child #play_cycle_val { width: 64px; margin-right: 4px; }
body.pb-v2 #init_frame_bt { margin-left: auto; }
/* 지우는 버튼은 판 없이 빨간 글씨(되돌릴 수 없는 것만 빨강) */
body.pb-v2 #init_frame_bt, body.pb-v2 #reset_motion_bt {
  background: transparent !important; border-color: transparent !important; box-shadow: none !important; color: var(--v2-red-ink);
}
body.pb-v2 #init_frame_bt:hover, body.pb-v2 #reset_motion_bt:hover { background: var(--v2-red-t) !important; }
/* [등록하기] 가 이 화면의 주 동작 — 빨간 판(btn-red) 대신 파랑 */
body.pb-v2 #add_motion_bt {
  background: var(--v2-dark) !important; color: var(--v2-on-dark); border-color: transparent; box-shadow: var(--v2-btn-sh); font-weight: 700;
}
body.pb-v2 #add_motion_bt:hover:not(:disabled) { background: var(--v2-dark-hov) !important; }
body.pb-v2 .motion-name-wrap { margin: 0; }
body.pb-v2 .motion-name-wrap > span[data-key="motion_name"] { display: none; }   /* 이름표 대신 입력칸 자리표시 */
body.pb-v2 .motion-name-wrap #motion_name_val { flex: 1 1 120px; min-width: 80px; width: auto !important; }
body.pb-v2 .motion-name-wrap > button { flex: none; white-space: nowrap; }

/* 저장 칸(내 동작, 260928): 표 아래 한 덩어리. 머리줄(제목·파일·내보내기·가져오기·모두 지우기) · 이름 줄 · 저장된 이름 칩 · 예제 */
body.pb-v2 .mlib { position: relative; gap: 8px; min-height: 0; }
body.pb-v2 .mlib__head { display: flex; align-items: center; gap: 8px; min-width: 0; }
body.pb-v2 .mlib__head > .motion-saved-title { white-space: nowrap; }
body.pb-v2 .mlib__head > input#motion-path-disabled {
  flex: 0 1 auto; width: 9.5em; min-width: 0; border: 0 !important; background: transparent !important; padding: 0; height: auto;
  font-family: var(--pb-mono); font-size: 12px; color: var(--v2-ink-3);
}
body.pb-v2 .mlib__tools { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: none; }
body.pb-v2 .mlib__tools > button, body.pb-v2 .mlib__tools > label { height: 30px; padding: 0 8px; font-size: 12.5px; white-space: nowrap; }
body.pb-v2 .mlib__tools > label { display: inline-flex; align-items: center; gap: 2px; line-height: 1; }
body.pb-v2 .mlib__chips { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 6px; flex: 1 1 auto; min-height: 28px; overflow-y: auto; }
body.pb-v2 #article_motion .mlib .mchip {
  height: 28px; padding: 0 12px !important; border-radius: 999px; font-size: 13px !important; font-weight: 600;
  background: var(--v2-sunken) !important; color: var(--v2-ink) !important; border: 1px solid var(--v2-line) !important; box-shadow: none !important;
}
body.pb-v2 #article_motion .mlib .mchip:hover { background: var(--v2-hover) !important; }
body.pb-v2 #article_motion .mlib .mchip[aria-pressed="true"] { background: var(--v2-accent-t) !important; color: var(--v2-accent) !important; border-color: var(--v2-accent) !important; }
body.pb-v2 .mlib__empty { font-size: 12.5px; color: var(--v2-ink-3); line-height: 28px; }
body.pb-v2 #article_motion .motion-path { display: none; }   /* 남은 건 예전 목록 textarea(값만 계속 받는다) */
/* [예제 동작 N개 ▾]: 저장 칸 머리줄 버튼, 누르면 목록이 저장 칸 위로 뜬다(표를 밀지 않는다) */
body.pb-v2 .mlib .motion-samples { margin: 0; }
body.pb-v2 .mlib .motion-samples > b {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 6px); z-index: 5; margin: 0;
  max-height: 50vh; overflow-y: auto; padding: 12px; border-radius: 12px;
  background: var(--v2-raise); border: 1px solid var(--v2-line); box-shadow: 0 8px 24px rgba(20, 24, 40, .18);
}
body.pb-v2 .mlib .motion-samples[data-open] #motion_samples_toggle > i { transform: rotate(-90deg); }

/* 표: 늘 500px 이던 것을 내용만큼으로(최대는 예전 그대로 500px — 42vh 로 줄였더니 노트북에서 12줄밖에
   안 보이고 아래가 비었다). 비어 있으면 안내 한 줄 */
body.pb-v2 .motion-table-wrap { height: auto; min-height: 150px; max-height: 500px; overflow-y: auto; border-radius: 10px; }
body.pb-v2 #motor_table thead td { position: sticky; top: 0; z-index: 1; font-size: 12.5px; font-weight: 600; color: var(--v2-ink-2); background: var(--v2-sunken); }
body.pb-v2 #motor_table tbody tr { cursor: pointer; }
body.pb-v2 #motor_table td { padding: 3px 4px; font-size: 13.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* 로봇 칸은 늘 왼쪽에 붙어 있다(그림을 보며 모터를 맞추는 화면). 오른쪽(표·저장)만 스크롤되고,
   화면보다 크면 index.js 의 fitRobot 이 zoom 으로 줄인다. 오른쪽은 남는 폭을 다 쓴다.
   (한때 1200px 아래에서 오른쪽을 로봇 아래로 내렸는데, 표를 보려면 로봇이 화면 밖으로 나가서 되돌렸다)
   900px 이하(태블릿 세로)만 위아래로 쌓는다 */
/* 줄 맞춤(901px 이상): 두 카드를 같은 폭·화면 높이(--motion-h)로 놓고, 안을 같은 세 줄로 나눈다.
     윗줄 40px    [원래자세 · 키 안내]    ↔  [반복 · 실행 · 정지 · 표 비우기]
     가운데 1fr   사진 + 조작 패널        ↔  표(여기만 스크롤)
     아랫줄 140px (사진이 이어짐)          ↔  저장 칸   — 시간·[추가·수정] 은 조작 패널 아래
   전엔 왼쪽이 내용 크기, 오른쪽이 나머지라 폭·아래 끝이 서로 달랐다. 사진은 fitStage 가 가운데 줄에 맞춘다 */
@media (min-width: 901px) {
  body.pb-v2 #article_motion { --m-top: 40px; --m-bot: 140px; flex-wrap: nowrap; align-items: stretch; margin-bottom: 0; }
  body.pb-v2 #article_motion > section {
    flex: 1 1 0 !important; width: auto !important; min-width: 0 !important;
    height: var(--motion-h, calc(100vh - 78px)); min-height: 560px; overflow: hidden;
    display: grid !important; grid-template-columns: minmax(0, 1fr); grid-template-rows: var(--m-top) minmax(0, 1fr) var(--m-bot); gap: 12px;
    padding: 14px 16px !important;
  }
  /* 왼쪽은 두 줄. 1300px 이상에선 폭을 오른쪽의 1.2배로 — 1:1 이면 사진(세로로 긴 그림)이 폭에 걸려 위아래가 빈다.
     위·아래 끝과 윗줄은 그대로 맞는다 */
  body.pb-v2 #article_motion > .pibo-section { grid-template-rows: var(--m-top) minmax(0, 1fr); }
  body.pb-v2 #article_motion .motion-record { display: contents; }
  body.pb-v2 #article_motion .motion-record > div:first-child { flex-wrap: nowrap; white-space: nowrap; min-width: 0; }
  body.pb-v2 #article_motion .motion-table-wrap { height: auto; min-height: 0; max-height: none; margin: 0; }
  body.pb-v2 .mside { align-self: center; }
  body.pb-v2 #article_motion .motion-name-wrap { flex-wrap: nowrap; }
}
@media (min-width: 1300px) { body.pb-v2 #article_motion > .pibo-section { flex-grow: 1.2 !important; } }
@media (max-width: 1023px) and (min-width: 901px) {   /* 아주 좁을 때: 저장 칸 이름 칸을 한 줄로 따로, 칸을 그만큼 키운다 */
  body.pb-v2 #article_motion { --m-bot: 178px; }
  body.pb-v2 #article_motion .motion-name-wrap { flex-wrap: wrap; }
  body.pb-v2 #article_motion .motion-name-wrap #motion_name_val { flex-basis: 100%; }
  body.pb-v2 .mlib__head > .motion-saved-title { display: none; }
}   /* 좁으면 표·저장 칸이 넘쳐 1:1 */
@media (max-width: 900px) { body.pb-v2 #article_motion > section:nth-child(2) { width: 100%; } }
body.pb-v2 .motion-table-wrap { overflow-x: auto; }

/* ── 도구 [카메라]·[음성] 정리 (260928) ────────────────────────────────────── */
/* 카메라 칸: 4:3 화면 칸. 그림이 오기 전에는 '기다리는 중' 안내(index.js 가 .v-stage[data-has] 를 단다) */
body.pb-v2 .v-head { display: flex; align-items: center; justify-content: space-between; }
body.pb-v2 .v-head > div { float: none !important; }
body.pb-v2 .v-stage {
  position: relative; margin: 10px 0 12px; aspect-ratio: 4 / 3; max-height: 60vh; border-radius: 12px; overflow: hidden;
  background: var(--v2-term);
}
body.pb-v2 .v-stage > #v_img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; object-fit: contain; border-radius: 0; background: transparent; }
/* ↑ max-*: tools/static/index.css 의 옛 규칙 #article_vision img { max-width: 560px; max-height: 420px } 를 푼다.
   안 풀면 칸이 560px 보다 넓을 때(큰 화면) 그림이 그 크기에서 멈추고 왼쪽 위에 붙는다(260929 기기에서 발견) */
body.pb-v2 .v-stage:not([data-has]) > #v_img { visibility: hidden; }
body.pb-v2 .v-stage:not([data-has]) > .v-stage__empty {
  display: flex !important; position: absolute; inset: 0; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  color: #8b94a1; font-size: 13px;
}
body.pb-v2 .v-stage__empty > i { font-size: 26px; opacity: .7; animation: pb-conn-blink 1.6s ease-in-out infinite; }
/* 카메라 방향: 제목 + 값 칩, 좌우·위아래 슬라이더. 아이콘은 가운데로 돌리는 버튼 */
body.pb-v2 .v-pos > header { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; color: var(--v2-ink); margin-bottom: 6px; }
body.pb-v2 .v-pos > header > i { font-size: 12px; font-weight: 600; color: var(--v2-ink-2); background: var(--v2-sunken); border-radius: 999px; padding: 3px 9px; }
body.pb-v2 .v-pos > header > i > span { font-family: var(--pb-font); font-variant-numeric: tabular-nums; }
body.pb-v2 .v-pos > div { gap: 8px; }
body.pb-v2 .v-pos a#v_pan_reset, body.pb-v2 .v-pos a#v_tilt_reset {
  display: inline-grid; place-items: center; width: 30px; height: 30px; flex: none; border-radius: 8px; cursor: pointer;
  color: var(--v2-ink-2); background: var(--v2-sunken); border: 1px solid var(--v2-line);
}
body.pb-v2 .v-pos a#v_pan_reset:hover, body.pb-v2 .v-pos a#v_tilt_reset:hover { background: var(--v2-tint, var(--v2-accent-t)); color: var(--v2-tint-ink, var(--v2-accent)); }
body.pb-v2 .v-pos input[type="range"] { flex: 1 1 0; width: auto !important; accent-color: var(--v2-accent); }

/* 비전 기능: select 대신 타일(누르면 select 값을 바꿔 원래 흐름 그대로 서버에 보낸다) */
body.pb-v2 .v-func { display: none; }
body.pb-v2 #v_tiles { display: grid !important; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; margin: 10px 0 12px; }
body.pb-v2 #v_tiles .v-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; min-height: 64px; padding: 8px 4px;
  border-radius: 10px; border: 1px solid var(--v2-line-2); background: var(--v2-raise) !important; color: var(--v2-ink-2);
  font-size: 12.5px !important; font-weight: 600; line-height: 1.2; text-align: center; box-shadow: var(--pb-shadow); cursor: pointer;
}
body.pb-v2 #v_tiles .v-tile > i { font-size: 17px; color: var(--v2-ink-3); }
/* 이름은 두 줄까지 접는다. 한글은 낱말 안에서 안 끊고(keep-all), 한 낱말이 칸보다 길면 그때만 끊는다.
   전엔 한 줄 고정이라 'Hand Gesture detection'·'얼굴분석(랜드마크)' 가 옆 칸으로 넘쳤다(260929) */
body.pb-v2 #v_tiles .v-tile { white-space: normal !important; }   /* 키트 버튼 규칙의 nowrap 을 푼다 */
body.pb-v2 #v_tiles .v-tile > span {
  max-width: 100%; white-space: normal; word-break: keep-all; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
/* 아직 모델이 안 올라온 기능(260929): 흐리게 + '준비 중' 꼬리표. 눌러 둘 수는 있다 */
body.pb-v2 #v_tiles .v-tile[data-wait] { position: relative; opacity: .6; }
body.pb-v2 #v_tiles .v-tile[data-wait]::after {
  content: attr(data-wait-label); position: absolute; top: 3px; right: 4px; padding: 0 5px; border-radius: 999px;
  font-size: 10px; font-weight: 700; line-height: 16px; background: var(--v2-sunken); color: var(--v2-ink-3);
}
/* 카메라 준비 단계 줄: 카메라 → 사물·손 인식 → 얼굴 인식 (n/3 · 경과 초) */
body.pb-v2 .v-boot { display: flex; flex-direction: column; gap: 7px; margin: 0 0 10px; padding: 10px 12px; border-radius: 10px;
  background: var(--v2-tint, var(--v2-accent-t)); color: var(--v2-ink); }
body.pb-v2 .v-boot[hidden] { display: none; }
body.pb-v2 .v-boot__row { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; }
body.pb-v2 .v-boot__row > i { color: var(--v2-accent); }
body.pb-v2 .v-boot__n { margin-left: auto; font-weight: 600; color: var(--v2-ink-3); font-variant-numeric: tabular-nums; white-space: nowrap; }
body.pb-v2 .v-boot__bar { height: 4px; border-radius: 999px; background: var(--v2-sunken); overflow: hidden; }
body.pb-v2 .v-boot__bar > i { display: block; height: 100%; background: var(--v2-accent); transition: width .4s; }
body.pb-v2 .v-boot[data-kind="err"] { background: var(--v2-red-t); color: var(--v2-red-ink); }
body.pb-v2 .v-boot[data-kind="err"] .v-boot__row > i, body.pb-v2 .v-boot[data-kind="err"] .v-boot__bar { display: none; }
body.pb-v2 #v_tiles .v-tile:hover { background: var(--v2-hover) !important; }
body.pb-v2 #v_tiles .v-tile[aria-pressed="true"] { background: var(--v2-tint) !important; border-color: rgba(37, 99, 235, .5); color: var(--v2-tint-ink); }
body.pb-v2 #v_tiles .v-tile[aria-pressed="true"] > i { color: var(--v2-tint-ink); }
html[data-theme="dark"] body.pb-v2 #v_tiles .v-tile[aria-pressed="true"] { border-color: var(--v2-accent); }
/* 마커 길이는 마커일 때만 */
body.pb-v2 #article_vision:not([data-func="marker"]) .v-marker { display: none; }
body.pb-v2 .v-marker { display: flex; align-items: center; gap: 8px; margin: -2px 0 12px; font-size: 13px; }
body.pb-v2 .v-marker .label { font-size: 13px !important; }
/* 결과: 읽기 전용 글자 칸 */
body.pb-v2 #v_result {
  background: var(--v2-sunken); color: var(--v2-ink); border: 1px solid var(--v2-line); border-radius: 8px;
  padding: 8px 10px; font-family: var(--pb-mono); font-size: 12.5px; line-height: 1.5; min-height: 96px; resize: vertical;
}

/* 음성: 이름표 폭을 맞춘 줄들. 주 동작은 칸마다 하나([실행하기] · [녹음하기]) */
body.pb-v2 .s-speak .speech-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
body.pb-v2 .s-speak .speech-input-group > .label { flex: none; width: 72px; font-size: 13px; font-weight: 600; color: var(--v2-ink-2); text-align: left; }
body.pb-v2 .s-speak #volume { width: 80px; text-align: right; }
body.pb-v2 .s-speak #volume + i { color: var(--v2-ink-3); font-size: 12px; }
body.pb-v2 .s-speak select[name="s_voice_type"] { width: 160px; }
body.pb-v2 .s-speak #s_tts_val { flex: 1 1 0; min-width: 0; }
/* 말할 글자: 여러 줄 칸을 전체 폭으로(260930). 전엔 한 줄 입력칸이 버튼 옆에 끼어 약 100px 이었다 */
body.pb-v2 .s-speak .s-text-row { align-items: flex-start; }
body.pb-v2 .s-speak .s-text-row > .label { padding-top: 9px; }
body.pb-v2 .s-speak textarea#s_tts_val {
  min-height: 76px; resize: vertical; padding: 8px 10px; border: 1px solid var(--v2-line-2); border-radius: 8px;
  background: var(--v2-raise); color: var(--v2-ink); font: 15px/1.5 var(--pb-font, inherit);
}
body.pb-v2 .s-speak textarea#s_tts_val:focus { outline: 2px solid var(--v2-accent); outline-offset: 1px; }
body.pb-v2 .s-speak .s-count { margin-left: auto; font-size: 12px; color: var(--v2-ink-3); font-variant-numeric: tabular-nums; }
body.pb-v2 .s-speak .s-count--full { color: var(--v2-red-ink, #b3261e); font-weight: 600; }
/* 목소리: PiBrain 도구처럼 타일(select 는 v2 에서 숨기고 값만 맞춘다) */
body.pb-v2 .s-speak .s-voice-row { align-items: flex-start; }
body.pb-v2 .s-speak .s-voice-row > .label { padding-top: 9px; }
body.pb-v2 .s-speak .s-voice-row > select, body.pb-v2 .s-speak .s-voice-row > .pb-sep { display: none; }
body.pb-v2 #s_voice_tiles { flex: 1 1 0; display: grid !important; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr)); gap: 6px; }
body.pb-v2 #s_voice_tiles .s-voice[data-v="espeak"] { grid-column: span 2; }
body.pb-v2 #s_voice_tiles .s-voice {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; padding: 0 8px;
  border-radius: 9px; border: 1px solid var(--v2-line-2); background: var(--v2-raise) !important; color: var(--v2-ink-2);
  font-size: 13px !important; font-weight: 600; box-shadow: var(--pb-shadow); cursor: pointer;
}
body.pb-v2 #s_voice_tiles .s-voice > i { color: var(--v2-ink-3); }
body.pb-v2 #s_voice_tiles .s-voice:hover { background: var(--v2-hover) !important; }
body.pb-v2 #s_voice_tiles .s-voice[aria-pressed="true"] { background: var(--v2-tint) !important; border-color: rgba(37, 99, 235, .5); color: var(--v2-tint-ink); }
body.pb-v2 #s_voice_tiles .s-voice[aria-pressed="true"] > i { color: var(--v2-tint-ink); }
html[data-theme="dark"] body.pb-v2 #s_voice_tiles .s-voice[aria-pressed="true"] { border-color: var(--v2-accent); }
body.pb-v2 #s_tts_stop_bt { min-width: 84px; }
body.pb-v2 .s-status { min-height: 20px; margin: -2px 0 0 82px; font-size: 13px; color: var(--v2-ink-3); }
body.pb-v2 .s-status[data-kind="ok"] { color: #1f9d62; }
body.pb-v2 .s-status[data-kind="err"] { color: var(--v2-red-ink); }
body.pb-v2 .s-status[data-kind="load"] { color: var(--v2-accent); font-weight: 600; }
body.pb-v2 #s_tts_bt { width: auto !important; min-width: 110px; background: var(--v2-dark) !important; color: var(--v2-on-dark); border-color: transparent; box-shadow: var(--v2-btn-sh); font-weight: 700; }
body.pb-v2 #s_tts_bt:hover { background: var(--v2-dark-hov) !important; }
body.pb-v2 #mic_bt { background: var(--v2-dark) !important; color: var(--v2-on-dark); border-color: transparent; box-shadow: var(--v2-btn-sh); font-weight: 700; }
body.pb-v2 #mic_bt:hover { background: var(--v2-dark-hov) !important; }
body.pb-v2 #mic_bt > i, body.pb-v2 #s_tts_bt > i, body.pb-v2 #add_motion_bt > i { color: inherit; }
body.pb-v2 .record-row { gap: 8px; align-items: center; }
body.pb-v2 .record-row #mic_time_val { width: 72px; text-align: right; }
body.pb-v2 .record-row .label { font-size: 13px; color: var(--v2-ink-3); }
body.pb-v2 .record-row button { min-width: 110px; height: 36px; padding: 0 14px; }
body.pb-v2 .record-row #mic_time_val { height: 36px; }
body.pb-v2 #mic_status { margin-left: 4px; color: var(--v2-ink-2) !important; font-weight: 600; }
body.pb-v2 .mic-wave span, body.pb-v2 .mic-progress > div { background: var(--v2-accent); }
body.pb-v2 .mic-progress { background: var(--v2-sunken); }
body.pb-v2 #motor_table tbody tr:hover td { background: var(--v2-tint, var(--v2-accent-t)) !important; }
body.pb-v2 .motion-table-wrap:has(tbody:empty) > .motion-empty {
  display: block !important; padding: 36px 20px; text-align: center; color: var(--v2-ink-3); font-size: 13px; line-height: 1.6;
}

/* 저장된 동작: 제목 + 파일 경로 캡션 + 읽기 전용 목록 */
body.pb-v2 .motion-path { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
body.pb-v2 .motion-saved-title { font-size: 13px; font-weight: 700; color: var(--v2-ink); }
body.pb-v2 .motion-path input#motion-path-disabled {
  border: 0 !important; background: transparent !important; padding: 0; height: auto;
  font-family: var(--pb-mono); font-size: 12px; color: var(--v2-ink-3);
}
body.pb-v2 .motion-path textarea {
  background: var(--v2-sunken); color: var(--v2-ink-2); border: 1px solid var(--v2-line); border-radius: 8px;
  padding: 8px 10px; font-size: 13px; line-height: 1.5; height: 64px; min-height: 64px;
}
/* 예제 동작: 주황 글씨를 쉼표로 늘어놓던 것 → 누르는 알약. 쉼표 글자는 font-size 0 으로 감춘다.
   평소엔 접어 둔다([예제 동작 29개 ▸]). 펼치면 표가 그만큼 줄어든다 */
body.pb-v2 .motion-samples { margin-top: 8px; }
body.pb-v2 .motion-samples > .motion-saved-title, body.pb-v2 .motion-samples > .pb-sep { display: none; }
body.pb-v2 #motion_samples_toggle {
  display: inline-flex !important; align-items: center; gap: 8px; height: 30px; padding: 0 10px 0 6px;
  background: transparent !important; border: 0; box-shadow: none; color: var(--v2-ink); font-size: 13px; font-weight: 700;
}
body.pb-v2 #motion_samples_toggle:hover { background: var(--v2-hover) !important; }
body.pb-v2 #motion_samples_toggle > i { font-size: 11px; color: var(--v2-ink-3); transition: transform .15s; }
body.pb-v2 .motion-samples[data-open] #motion_samples_toggle > i { transform: rotate(90deg); }
body.pb-v2 .motion-samples:not([data-open]) > b { display: none; }
body.pb-v2 .motion-samples > b { display: block; margin-top: 6px; font-weight: 600; }
body.pb-v2 #motion_samples { display: flex; flex-wrap: wrap; gap: 6px; font-size: 0; }
body.pb-v2 #motion_samples > a {
  font-size: 12.5px; color: var(--v2-ink-2) !important; background: var(--v2-sunken); border: 1px solid var(--v2-line);
  border-radius: 999px; padding: 3px 10px; font-family: var(--pb-mono); text-decoration: none;
}
body.pb-v2 #motion_samples > a:hover { background: var(--v2-tint, var(--v2-accent-t)); color: var(--v2-tint-ink, var(--v2-accent)) !important; border-color: transparent; }

/* ==========================================================================
   움직임 (260929) — CSS 와 짧은 JS 만. 그림 파일·라이브러리 없음, 브라우저를 타는 기능(View Transitions,
   @property, backdrop-filter 등)은 쓰지 않는다. 움직이는 건 transform · opacity · 작은 칸의 배경뿐.
   움직임 줄이기 설정(prefers-reduced-motion)이면 맨 아래에서 전부 멈춘다
   ========================================================================== */
@keyframes pb-breathe { 0% { box-shadow: 0 0 0 0 rgba(31, 157, 98, .5); } 70%, 100% { box-shadow: 0 0 0 7px rgba(31, 157, 98, 0); } }
@keyframes pb-scan    { from { background-position: 0 -48px; } to { background-position: 0 calc(100% + 48px); } }
@keyframes pb-tab-in  { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes pb-rise    { from { opacity: 0; transform: translateY(10px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes pb-shimmer { from { background-position: 150% 0; } to { background-position: -50% 0; } }
@keyframes pb-eq      { 0%, 100% { transform: scaleY(.3); } 50% { transform: scaleY(1); } }

/* 4) 로봇 연결됨: 초록 점이 숨 쉬듯 퍼진다 (도구 상단 칩) */
body.pb-v2 #onoff_val[data-state="on"]::before { animation: pb-breathe 2.4s ease-out infinite; }

/* 1) 결과 막대: 튕기듯 차오른다(살짝 넘쳤다 돌아옴). 숫자는 app.js 가 PiboUI.countTo 로 올린다 */
body.pb-v2 .bar-fill { transition: width .45s cubic-bezier(.34, 1.56, .64, 1), background-color .2s; }
body.pb-v2 .bar-val { font-variant-numeric: tabular-nums; }

/* 2) 카메라 칸 HUD: 네 모서리 꺾쇠. 그림이 오기 전에는 스캔선이 위아래로 지나간다 */
body.pb-v2 .v-stage::after, body.pb-v2 .stage::after {
  content: ""; position: absolute; inset: 10px; z-index: 3; pointer-events: none;
  --hc: rgba(255, 255, 255, .72); --hl: 18px; --hw: 2px;
  background:
    linear-gradient(var(--hc), var(--hc)) left top / var(--hl) var(--hw),
    linear-gradient(var(--hc), var(--hc)) left top / var(--hw) var(--hl),
    linear-gradient(var(--hc), var(--hc)) right top / var(--hl) var(--hw),
    linear-gradient(var(--hc), var(--hc)) right top / var(--hw) var(--hl),
    linear-gradient(var(--hc), var(--hc)) left bottom / var(--hl) var(--hw),
    linear-gradient(var(--hc), var(--hc)) left bottom / var(--hw) var(--hl),
    linear-gradient(var(--hc), var(--hc)) right bottom / var(--hl) var(--hw),
    linear-gradient(var(--hc), var(--hc)) right bottom / var(--hw) var(--hl);
  background-repeat: no-repeat;
}
body.pb-v2 .v-stage:not([data-has])::before, body.pb-v2 .stage:not(.has-frame)::before {
  background-image: linear-gradient(to bottom, rgba(120, 170, 255, 0), rgba(120, 170, 255, .14) 70%, rgba(160, 195, 255, .38));
  background-repeat: no-repeat; background-size: 100% 48px;
  animation: pb-scan 2.6s linear infinite;
}
body.pb-v2 .v-stage:not([data-has])::before { content: ""; position: absolute; inset: 0; pointer-events: none; }

/* 3) [동작] 모터 번호 점에 각도 링: 0° 는 빈 링, 한쪽 끝까지 가면 한 바퀴. + 는 시계 방향, − 는 반대.
      index.js 가 --g-a(호의 크기)·--g-s(시작 각)를 단다 */
body.pb-v2 #article_motion .mstage .mdot > b { position: relative; --g-t: rgba(255, 255, 255, .28); }
body.pb-v2 #article_motion .mstage .mdot[aria-pressed="true"] > b { --g-t: rgba(37, 99, 235, .16); }
body.pb-v2 #article_motion .mstage .mdot > b::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  background: conic-gradient(from var(--g-s, 0deg), currentColor 0deg var(--g-a, 0deg), var(--g-t) var(--g-a, 0deg) 360deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2.5px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 2.5px));
}

/* 6) 탭을 바꾸면 새 칸이 살짝 떠오르며 나타난다 (분류기 [학습|시험|보관함]). 도구 메뉴는 원래 슬라이드가 있다 */
body.pb-v2 .only-learn, body.pb-v2 .only-test, body.pb-v2 .only-store { animation: pb-tab-in .22s ease-out backwards; }

/* 7) 불러오는 동안 반짝이는 빈 카드 (분류기 보관함 첫 목록). 목록이 오면 app.js 가 통째로 바꾼다 */
.pb-skel {
  border-radius: 10px; min-height: 18px;
  background: linear-gradient(90deg, var(--v2-hover, #e8e8e8) 30%, var(--v2-press, #dadada) 50%, var(--v2-hover, #e8e8e8) 70%);
  background-size: 300% 100%; animation: pb-shimmer 1.4s ease-in-out infinite;
}
.pb-skel--card { height: 132px; margin-bottom: 12px; }

/* 8) 팝업·알림: 살짝 튀며 나타난다 */
body.pb-v2 .alert-popup-content, body.pb-v2 .prompt-popup-content, body.pb-v2 .confirm-popup-content,
body.pb-v2 .wifi-popup-content, body.pb-v2 .pb-modal__box { animation: pb-rise .26s cubic-bezier(.34, 1.4, .64, 1) backwards; }
body.pb-v2 .pb-toast { animation: pb-rise .26s cubic-bezier(.34, 1.4, .64, 1) backwards; }

/* 10) [음성] 말하는 동안 이퀄라이저 막대 (실제 소리 크기가 아니라 '말하는 중' 표시. 소리는 로봇에서 난다) */
.pb-eq { display: inline-flex; align-items: flex-end; gap: 2px; height: 14px; margin-right: 7px; vertical-align: -2px; }
.pb-eq > i { display: block; width: 3px; height: 100%; border-radius: 2px; background: currentColor; transform-origin: bottom;
             animation: pb-eq .9s ease-in-out infinite; }
.pb-eq > i:nth-child(2) { animation-delay: -.3s; }
.pb-eq > i:nth-child(3) { animation-delay: -.6s; }
.pb-eq > i:nth-child(4) { animation-delay: -.15s; }

@media (prefers-reduced-motion: reduce) {
  body.pb-v2 #onoff_val::before, body.pb-v2 .v-stage::before, body.pb-v2 .stage::before,
  body.pb-v2 .only-learn, body.pb-v2 .only-test, body.pb-v2 .only-store, .pb-skel, .pb-eq > i,
  body.pb-v2 .alert-popup-content, body.pb-v2 .prompt-popup-content, body.pb-v2 .confirm-popup-content,
  body.pb-v2 .wifi-popup-content, body.pb-v2 .pb-modal__box, body.pb-v2 .pb-toast { animation: none !important; }
  body.pb-v2 .bar-fill { transition: none; }
}
