:root{
  --bg:#12161d; --surface:#1a2029; --surface2:#222a35; --line:#2c3644;
  --ink:#e7ecf3; --ink2:#9aa7b8; --ink3:#6b7889;
  --accent:#4fb9ff; --ok:#3dc873; --warn:#ff9c3e; --err:#ff6e6e;
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Apple SD Gothic Neo","Malgun Gothic",sans-serif;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0}
body{background:var(--bg);color:var(--ink);font:13px/1.55 var(--sans);overflow:hidden}
.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}

#topbar{display:flex;align-items:center;gap:14px;height:44px;padding:0 14px;
  background:var(--surface);border-bottom:1px solid var(--line)}
#topbar strong{font-size:13px;letter-spacing:-.01em}
#tabs{display:flex;gap:2px}
#tabs button{background:none;border:1px solid transparent;color:var(--ink3);
  padding:5px 12px;border-radius:6px;cursor:pointer;font:inherit}
#tabs button:hover{color:var(--ink2);background:var(--surface2)}
#tabs button.on{color:var(--accent);background:var(--surface2);border-color:var(--line)}
#status{margin-left:auto;font-family:var(--mono);font-size:11px;letter-spacing:.06em;
  padding:3px 9px;border-radius:20px;border:1px solid var(--line);color:var(--ink3)}
#status.sim{color:var(--ok);border-color:var(--ok)}
#status.off{color:var(--err);border-color:var(--err)}
#scaleWarn{font-family:var(--mono);font-size:11px;color:var(--warn)}
/* 배치 전체 문제 — 축척 경고와 같은 자리·같은 모양 */
#layoutWarn{font-family:var(--mono);font-size:11px;color:var(--warn)}

main{display:flex;height:calc(100% - 44px);position:relative}
#panel{width:300px;flex:none;overflow-y:auto;background:var(--surface);
  border-right:1px solid var(--line);padding:4px 0 40px}
#panel section{padding:12px 14px;border-bottom:1px solid var(--line)}
#panel h2{margin:0 0 9px;font-family:var(--mono);font-size:10px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
.row{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-bottom:7px}
.row:last-child{margin-bottom:0}
label{display:flex;align-items:center;gap:5px;font-size:12px;color:var(--ink2)}
input[type=number],select{background:var(--surface2);color:var(--ink);
  border:1px solid var(--line);border-radius:5px;padding:4px 6px;font:inherit;width:66px}
select{width:auto}
input[type=range]{width:130px;accent-color:var(--accent)}
button{background:var(--surface2);color:var(--ink2);border:1px solid var(--line);
  border-radius:6px;padding:5px 10px;cursor:pointer;font:inherit}
button:hover{background:var(--line);color:var(--ink)}
button.primary{background:var(--accent);border-color:var(--accent);color:#07131d;font-weight:600}
button.on{color:var(--accent);border-color:var(--accent)}
:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

.jrow{display:grid;grid-template-columns:24px 1fr 58px;gap:7px;align-items:center;margin-bottom:5px}
.jrow b{font-family:var(--mono);font-size:11px;color:var(--ink3);font-weight:600}
.jrow span{font-family:var(--mono);font-size:11px;text-align:right;color:var(--ink)}
.jrow input[type=range]{width:100%}

#view{position:relative;flex:1;min-width:0}
#gl{display:block;width:100%;height:100%}
/* 지표 — 패널 맨 아래. 예전에는 3D 뷰 좌하단 오버레이라 메인 화면을 가렸다.
   한글은 모노 폰트에서도 폭이 두 배라 공백 정렬이 어긋나므로 grid 로 맞춘다. */
#hud{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;
  font-family:var(--mono);font-size:10.5px;line-height:1.5;
  font-variant-numeric:tabular-nums}
#hud i{color:var(--ink3);font-style:normal;white-space:nowrap}
#hud b{color:var(--ink2);font-weight:400;text-align:right;white-space:nowrap}
#toasts{position:absolute;right:12px;top:12px;display:flex;flex-direction:column;gap:6px;max-width:380px}
.toast{background:var(--surface);border:1px solid var(--err);border-left-width:3px;
  color:var(--ink);font-size:12px;padding:7px 10px;border-radius:6px}
#readout{margin:0;font-size:11px;color:var(--ink2);white-space:pre-wrap}
#ledPrev{image-rendering:pixelated;border:1px solid var(--line);border-radius:4px;background:#000}

#scLibInfo{margin:2px 0 0;font-size:10.5px;line-height:1.5;
  color:var(--ink3);white-space:pre-wrap}
#monOut{margin:0;font-size:10.5px;line-height:1.6;color:var(--ink2);white-space:pre-wrap;
  word-break:break-all;max-height:260px;overflow-y:auto;background:var(--bg);
  border:1px solid var(--line);border-radius:5px;padding:7px 8px}
#monOut b{color:var(--accent);font-weight:600}
#monOut i{color:var(--ink3);font-style:normal}
/* 파생 20B(STM32 가 만드는 라쿤 프레임)는 호스트 프레임과 구분되게 흐리게 */
#monOut u{color:var(--ink3);text-decoration:none;font-size:10px}
#panel section[hidden]{display:none}
input[type=checkbox]{accent-color:var(--accent)}

[hidden]{display:none!important}
input[type=color]{width:34px;height:22px;padding:0;border:1px solid var(--line);
  border-radius:4px;background:var(--surface2);cursor:pointer}
#ledRgbV{font-size:11px;color:var(--ink3)}

/* Phase 8 — 탭 UI */
#estop{background:var(--err);border-color:var(--err);color:#1a0b0b;font-weight:700;
  padding:5px 12px;letter-spacing:-.01em}
#estop:hover{background:#ff8a8a;color:#1a0b0b}
#status.on{color:var(--ok);border-color:var(--ok)}
.hint{font-size:10.5px;color:var(--ink3)}
#portList,#healthOut,#scMsg,#runMetrics{margin:0;font-size:10.5px;line-height:1.65;
  color:var(--ink2);white-space:pre-wrap;background:var(--bg);border:1px solid var(--line);
  border-radius:5px;padding:7px 8px;max-height:200px;overflow-y:auto}
#csvOut{width:100%;background:var(--bg);color:var(--ink2);border:1px solid var(--line);
  border-radius:5px;padding:6px 7px;font-family:var(--mono);font-size:10.5px;resize:vertical}
#seekBar{accent-color:var(--accent)}
#playPos{margin-left:auto;font-size:11px}

/* 시나리오 편집기 타임라인 — 3D 뷰 아래 오버레이.
   편집과 미리보기가 같은 화면이어야 한다(09 §4). */
/* 타임라인은 시나리오 탭 메인(#scen) 안에 산다 — 예전에는 3D 뷰 하단에
   절대배치된 얇은 띠였고, 그래서 편집기가 뷰의 부속처럼 보였다. */
#timeline{display:flex;flex-direction:column;gap:6px;min-height:0;flex:1}
/* 캔버스 높이는 트랙 수에 맞춰 JS 가 정한다 (drawTimeline) */
#tlCanvas{display:block;width:100%;cursor:crosshair}
#tlLegend{margin-top:5px;font-size:10.5px;color:var(--ink3)}
#tlLegend b{color:var(--ink2);font-weight:600}
#trList,#kfInfo,#scCheck{margin:0;font-size:10.5px;line-height:1.65;color:var(--ink2);
  white-space:pre-wrap;background:var(--bg);border:1px solid var(--line);
  border-radius:5px;padding:7px 8px;max-height:170px;overflow-y:auto}
#trList b{color:var(--accent);font-weight:600}
input[type=text]{background:var(--surface2);color:var(--ink);border:1px solid var(--line);
  border-radius:5px;padding:4px 6px;font:inherit}


/* ─────────────────────────────────────────────────────────────────────
   연결 탭 메인 화면 (#wire)

   #view 를 hidden 으로 접지 않고 **덮는다**:
     · 씬을 파괴하지 않아야 탭 왕복에 geometries 가 불변이다 (Phase 8 인수 조건)
     · 접으면 canvas clientWidth 가 0 이 되어 resize() 의 aspect 계산이 깨진다
   좌측 패널까지 덮는 전폭이라 #view 안이 아니라 main 직속이다.

   상태 색은 원본 gui/theme.py 규약을 그대로 쓴다 (docs/gui_connect/README.md §8).
   ★ --w-relax 의 붉은색은 반드시 유지한다 — 힘빠짐은 팔이 중력으로 내려앉은
     상태라 사람이 손을 대면 위험하다 (theme.py:90 이 주석으로 못박아 뒀다).
   ★ --w-unknown 을 --w-ok 로 합치지 않는다 — "아직 모른다" 와 "정상이다" 는
     다르다. 연결 직후 2초는 전부 unknown 이고, 초록으로 칠하면 공연 전 점검이
     거짓말을 한다.
   ───────────────────────────────────────────────────────────────────── */
:root{
  --w-ok:#78dc82; --w-warn:#ffbe6e; --w-crit:#ff6e6e;
  --w-dead:#462626; --w-nolink:#212329; --w-relax:#964646;
  --w-offline:#2a2c33; --w-unknown:#212329;
  --w-row-a:#26282e; --w-row-b:#212329; --w-sel:#3c4664; --w-busy:#324670;
}

#wire{position:absolute;inset:0;z-index:5;overflow-y:auto;
  background:var(--bg);padding:14px 16px 48px;display:flex;
  flex-direction:column;gap:12px}
#wire .wSec{background:var(--surface);border:1px solid var(--line);
  border-radius:8px;padding:12px 14px}
#wire h2{margin:0 0 10px;font-family:var(--mono);font-size:10px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
#wire .wDim{font-family:var(--sans);font-size:11px;letter-spacing:0;
  text-transform:none;color:var(--ink3);margin-left:8px}
#wire .wBar{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
#wire .wSpacer{flex:1}
#wire .wFig{font-family:var(--mono);font-size:12px;color:var(--ink)}
#wire .wNote{margin:8px 0 0;font-size:11px;color:var(--ink3);max-width:78ch}
#wire .wHint{margin:6px 0 0;font-family:var(--mono);font-size:11px;color:var(--accent)}
#wire .wProblems{margin:6px 0 0;font-family:var(--mono);font-size:11px;
  color:var(--err);white-space:pre-line}
#wire .wCols{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:12px;align-items:start}
#wire .wCols .wSec{margin:0}
#wire pre{margin:0;font-size:11px;line-height:1.5;color:var(--ink2);
  white-space:pre-wrap;word-break:break-all}

/* ② 번호 ↔ 위치 격자 — 넘치면 가로로 스크롤한다 (본문은 절대 옆으로 밀리지 않는다) */
#wire .wGridWrap,#wire .wTableWrap{overflow-x:auto;padding-bottom:2px}
#wire .wGrid{display:grid;gap:3px;width:max-content}
#wire .wCell{width:44px;height:34px;border-radius:4px;border:1px solid transparent;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  cursor:pointer;background:var(--w-row-a);color:var(--ink);
  font-size:12px;line-height:1.1;user-select:none}
#wire .wCell small{font-size:9px;color:var(--ink3)}
#wire .wCell:hover{border-color:var(--accent)}
#wire .wCell.picked{background:var(--w-sel);border-color:var(--accent);color:#fff}
#wire .wCell.picked small{color:#cfe0f5}
#wire .wRowLab,#wire .wColLab{display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-size:10px;color:var(--ink3)}

/* ③ 동글 표 — 열 규격을 grid 한 곳에 둬 헤더와 행이 어긋날 수 없게 한다
   (원본은 헤더를 공백 문자열로 맞춰 두 파일에서 같은 어긋남을 겪었다) */
#wire .wHubs{display:grid;
  grid-template-columns:auto auto auto auto 1fr auto;
  gap:0 14px;width:max-content;min-width:100%;font-size:12px}
#wire .wHubs .h{font-size:10px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink3);padding:0 0 6px}
#wire .wHubs .c{padding:5px 0;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:6px}
#wire .wHubs .c.num{justify-content:flex-end;font-variant-numeric:tabular-nums}
#wire .wHubs select{width:auto;padding:2px 4px;font-size:11px}
#wire .wLamp{width:12px;height:12px;border-radius:3px;flex:none;
  background:var(--w-offline);border:1px solid var(--line)}
#wire .wLamp.on{background:var(--w-ok);border-color:var(--w-ok)}
#wire .wOver{color:var(--warn)}

/* ④ 채널 상세 — 열 규격을 grid 한 곳에 둔다 (헤더와 행이 어긋날 수 없다).
   좌측 = 내려보낸 명령, 우측 = 올라온 센서리. 구분선이 그 경계를 보인다. */
#wire .wChans{display:grid;
  grid-template-columns:repeat(4,auto) 1px repeat(6,auto) 1px repeat(5,auto) auto;
  gap:0 10px;width:max-content;min-width:100%;font-size:11.5px}
#wire .wChans .h{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);padding:0 0 5px;text-align:right}
#wire .wChans .h.l{text-align:left}
#wire .wChans .d{padding:4px 0;border-top:1px solid var(--line);text-align:right;
  font-variant-numeric:tabular-nums;white-space:nowrap}
#wire .wChans .d.l{text-align:left}
#wire .wChans .sep{border-top:1px solid var(--line);background:var(--line);
  width:1px;margin:0 4px}
#wire .wChans .h.sep{background:none;border:0}
#wire .wChans .off{color:var(--ink3)}
#wire .wChans .rowDead{background:var(--w-dead)}
#wire .wChans .rowBusy{background:var(--w-busy)}
/* 힘빠진 채널은 붉게 — 각도 명령이 무시되는 상태임을 알려야 한다 */
#wire .wChans .relax{color:var(--w-crit)}

#wire .wDot{display:inline-block;width:8px;height:8px;border-radius:50%;
  margin-right:5px;vertical-align:middle}
#wire .st-ok{background:var(--w-ok)}
#wire .st-warn{background:var(--w-warn)}
#wire .st-crit{background:var(--w-crit)}
#wire .st-dead{background:var(--w-crit);opacity:.55}
#wire .st-nolink{background:var(--ink3)}
#wire .st-relax{background:var(--w-relax)}
#wire .st-offline,#wire .st-unknown{background:var(--ink3);opacity:.5}
#wire .wHubs .c.sel{background:var(--w-sel)}
#wire .wHubs .c{cursor:pointer}
#wire .wTally{font-size:11px;color:var(--ink3)}
#wire .wTally b{color:var(--ink2);font-weight:600}
#wire .wTally .bad{color:var(--w-crit)}
#wire .wTally .att{color:var(--w-warn)}

/* ⑤⑥ 보조 창 — 원본은 tkinter Toplevel 이었다. 웹에서는 덮개로 충분하고,
   "창이 내용보다 작게 열린다 / 좌측 아래에서 작게 뜬 뒤 커진다" 같은 지뢰가
   아예 없다 (docs/gui_connect/README.md §6 이 웹에서 사라진다고 지목한 것들). */
#wire .wModal{position:fixed;inset:0;z-index:20;background:rgba(8,10,14,.72);
  display:flex;align-items:flex-start;justify-content:center;padding:40px 16px;
  overflow-y:auto}
#wire .wModalBox{background:var(--surface);border:1px solid var(--line);
  border-radius:10px;padding:16px 18px;max-width:min(1100px,96vw);width:100%;
  box-shadow:0 18px 60px rgba(0,0,0,.5)}
#wire .wModalBox h3{margin:0 0 8px;font-size:14px}
#wire .wModalBox .wBar{margin:10px 0}

#wire .wPorts{display:grid;grid-template-columns:auto 1fr auto auto auto;
  gap:0 12px;width:max-content;min-width:100%;font-size:12px}
#wire .wPorts .h{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);padding:0 0 5px}
#wire .wPorts .c{padding:5px 0;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:6px;white-space:nowrap}
#wire .wPorts select{max-width:420px}
#wire .wPorts .warnDev{color:var(--warn)}
#wire .st-waiting{color:var(--w-warn)}
#wire .st-ready{color:var(--w-ok)}
#wire .st-error{color:var(--w-crit)}

#wire .wCheck{display:grid;grid-template-columns:repeat(6,auto) auto 1fr;
  gap:0 12px;width:max-content;min-width:100%;font-size:12px}
#wire .wCheck .h{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);padding:0 0 5px}
#wire .wCheck .c{padding:4px 0;border-top:1px solid var(--line);
  display:flex;align-items:center;gap:5px;white-space:nowrap}
#wire .wCheck .c.dead{background:var(--w-dead)}
#wire .wCheck .c.busy{background:var(--w-busy)}
#wire .wCheck button{padding:2px 8px;font-size:11px}
#wire .wCheck button:disabled{opacity:.4;cursor:not-allowed}
#wire .wCheck .pass{color:var(--w-ok)}
#wire .wCheck .fail{color:var(--w-crit)}
#wire .wCheck .none{color:var(--w-warn)}

/* ① 연결 툴바 — 원본 gui 의 최상단 툴바 자리. 한 줄로 압축해 결선 설계가
   화면의 주인으로 남게 한다. */
/* 툴바는 연결 탭과 시나리오 탭이 함께 쓴다 — 한쪽에만 매어 두면 다른 쪽에서
   flex 가 아예 걸리지 않아 항목이 세로로 쌓인다. */
.wTopbar{display:flex;flex-wrap:wrap;gap:10px;align-items:center;
  background:var(--surface2);border:1px solid var(--line);border-radius:8px;
  padding:9px 12px;position:sticky;top:0;z-index:6}
.wTopbar .wSep{width:1px;height:20px;background:var(--line);flex:none}
#wire .wTopbar #connMsg{font-size:11px;color:var(--ink3);text-align:right}
#wire .wChans .d{cursor:pointer}
#wire .wChans .d:hover{background:var(--surface2)}

/* 배율 컨트롤 — 우측 하단. 휠 확대를 껐으므로 이것이 유일한 배율 수단이다.
   범위와 현재 값이 화면에 늘 적혀 있어 "지금 얼마나 확대된 건지" 를 잃지 않는다. */
#zoomCtl{position:absolute;right:12px;bottom:10px;display:flex;align-items:center;
  gap:2px;background:rgba(18,22,29,.9);border:1px solid var(--line);
  border-radius:8px;padding:4px 5px;z-index:4}
#zoomCtl button{width:28px;height:26px;padding:0;font-size:15px;line-height:1;
  border-radius:5px;background:var(--surface2);color:var(--ink2);
  border:1px solid var(--line);cursor:pointer}
#zoomCtl button:hover:not(:disabled){color:var(--ink);background:var(--line)}
#zoomCtl button:disabled{opacity:.35;cursor:not-allowed}
/* 숫자는 눌러서 고칠 수 있다 — 평소에는 글자처럼 보이고 포커스가 가면 칸이 된다 */
#zoomCtl #zoomV{width:52px;text-align:center;font-size:11px;color:var(--ink2);
  font-family:var(--mono);font-variant-numeric:tabular-nums;
  background:none;border:1px solid transparent;border-radius:4px;padding:3px 0;
  cursor:text}
#zoomCtl #zoomV:hover{border-color:var(--line);color:var(--ink)}
#zoomCtl #zoomV:focus{outline:none;background:var(--surface2);color:var(--ink);
  border-color:var(--accent)}
/* 시나리오 탭에서는 타임라인이 하단을 덮으므로 위로 올린다 */
#view.withTimeline #zoomCtl{bottom:96px}


/* ─────────────────────────────────────────────────────────────────────
   시나리오 탭 (#scen)

   이 탭의 주인공은 **시간축 위의 연출**이다. 위에 그 시각의 벽(n×m), 아래에
   트랙×시간 타임라인, 3D 는 우상단 미리보기로 줄인다.
   #wire 와 달리 3D 를 완전히 덮지 않는다 — 팔이 실제로 그 자세를 잡을 수
   있는지는 3D 로만 보이므로 작게라도 살려 둔다.
   ───────────────────────────────────────────────────────────────────── */
/* 편집에 필요한 섹션은 **전부 인스펙터로 옮겨 온다**(placeInspector) —
   그래서 좌측 패널을 덮어도 잃는 것이 없고, 벽이 300px 을 더 쓴다.
   ★ 한때 inset:0 으로 두고 섹션은 패널에 남겨 뒀는데, 그때는 대상·각도·LED·
     트랙·키프레임·오프셋·사전검사가 화면 뒤에 깔려 **편집이 아예 불가능**했다.
     덮는 것 자체가 문제가 아니라, 덮으면서 옮기지 않은 것이 문제였다. */
#scen{position:absolute;inset:0;z-index:5;background:var(--bg);
  padding:12px 14px 16px;display:flex;flex-direction:column;gap:10px;overflow:hidden}
#scen .wSec{background:var(--surface);border:1px solid var(--line);
  border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;min-height:0}
#scen h2{margin:0 0 8px;font-family:var(--mono);font-size:10px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
#scen .wDim{font-family:var(--sans);font-size:11px;letter-spacing:0;
  text-transform:none;color:var(--ink3);margin-left:8px}
#scen .wGridWrap{overflow:auto;min-height:0;flex:1}
#scen .wSpacer{flex:1}
#scen .wSep{width:1px;height:20px;background:var(--line);flex:none}
#scen .wFig{font-family:var(--mono);font-size:12px;color:var(--ink)}

#scenBar{flex:none}
#scenSeekRow{flex:none;padding:0 2px}
#scenSeekRow input[type=range]{width:100%;accent-color:var(--accent)}
#sbCheck{font-size:11px;color:var(--ink3)}
#sbCheck.bad{color:var(--w-crit)}
#sbCheck.warn{color:var(--w-warn)}
#sbCheck.ok{color:var(--w-ok)}

/* ★ 타임라인은 **내용 높이**를 쓴다.
   비율(flex: 1 1 42%)로 나눠 줬더니 트랙이 2개뿐일 때도 남는 높이의 42% —
   실측 267px 를 차지하고 그 중 130px 가 빈 캔버스였다. 트랙 수는 시나리오마다
   다르므로 고정 비율은 언제나 틀린다.
   캔버스 높이는 drawTimeline 이 HEAD_H + 트랙수 × ROW_H 로 정하고, 트랙이
   많아지면 벽을 밀어내는 대신 이 안에서 스크롤한다. */
#scenWallSec{flex:1 1 auto;min-height:0}
#scenTlSec{flex:0 0 auto;max-height:55%;min-height:0}
/* ★ 캔버스는 flex 축소 대상이 아니다. 기본값(flex-shrink:1)으로 두면 트랙이
   많을 때 섹션의 max-height 에 눌려 **트랙이 잘린 채 스크롤도 안 됐다**.
   높이를 지킨 채 컨테이너가 스크롤해야 한다. */
#timeline{overflow-y:auto;min-height:0}
#scen #tlCanvas{flex:none}
#tlLegend{flex:none;font-size:10.5px;color:var(--ink3)}

/* 벽 칸 — 색(LED) + 번호 + 슬롯 약어 */
#scWall{display:grid;gap:3px;width:max-content}
#scWall .scCell{width:52px;height:40px;border-radius:4px;
  border:1px solid var(--line);display:flex;flex-direction:column;
  align-items:center;justify-content:center;cursor:pointer;user-select:none;
  font-size:11px;line-height:1.15;position:relative;overflow:hidden}
#scWall .scCell b{font-weight:600;font-size:11.5px}
#scWall .scCell small{font-size:9px;opacity:.85}
#scWall .scCell:hover{border-color:var(--accent)}
#scWall .scCell.on{outline:2px solid var(--accent);outline-offset:-2px}
#scWall .wRowLab,#scWall .wColLab{display:flex;align-items:center;
  justify-content:center;font-family:var(--mono);font-size:10px;color:var(--ink3)}

/* 시나리오 탭의 3D 는 **모달 미리보기**다.
   코너에 작게 띄웠더니 벽이 넓어지면(200대 = 20열) 칸 25개를 가렸다.
   이제 평소에는 상단바 버튼이고, 열면 화면의 80 %를 차지한다.

   ★ 닫혀 있어도 #view 를 hidden 으로 접지 않는다 — canvas clientWidth 가 0 이
     되면 resize() 의 aspect 계산이 깨진다(원근 전환 때 배운 것). 크기를 유지한
     채 visibility 로만 감춘다.
   ★ 동적 프레임도 멈추지 않는다 — 벽 격자가 그 프레임의 LED 슬롯으로 그려진다.
     연결 탭처럼 visible=false 를 보내면 벽이 멈춘다. */
main.scenTab #view{position:absolute;right:22px;top:150px;width:320px;height:240px;
  visibility:hidden;pointer-events:none;z-index:6}
main.scenTab.scenPreview #view{visibility:visible;pointer-events:auto;
  left:10%;top:11%;right:auto;width:80%;height:80%;z-index:21;
  border:1px solid var(--line);border-radius:10px;overflow:hidden;
  box-shadow:0 24px 70px rgba(0,0,0,.6)}
/* 배율은 100 % 고정 — 미리보기에서는 − / + 를 두지 않는다 */
main.scenTab #zoomCtl{display:none}

#prevShade{position:fixed;inset:0;background:rgba(8,10,14,.72);z-index:20}
#prevBar{position:absolute;left:10%;top:calc(11% - 34px);width:80%;z-index:22;
  display:flex;align-items:center;gap:10px}
#prevBar strong{font-size:13px}
#prevBar .wSpacer{flex:1}


/* 상단바는 **한 줄**이다 — 줄바꿈되면 시크바가 밀려 내려가 화면이 계단이 된다.
   이름이 길면 이름만 줄인다. */
#scenBar{flex-wrap:nowrap;gap:8px;overflow:hidden}
#scenBar #sbName{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  max-width:220px;flex:none}
#scenBar label{white-space:nowrap;flex:none}
#scenBar button{flex:none}
#scenBar #sbCheck{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  max-width:260px}
/* 미리보기가 모달이 되어 벽은 전폭을 쓴다 (200대에서 칸 25개가 가려졌던 것 해소) */


/* ── 시나리오 인스펙터 ────────────────────────────────────────────────
   "칸을 누른다 → 값을 만든다 → 그 시각에 박는다" 흐름이 한 화면에서 끝나야
   한다. 예전에는 그 흐름이 좌측 패널(값)과 가운데(시각)로 쪼개져 있었다.
   섹션은 **복제가 아니라 이동**이다 — id 가 하나뿐이라 핸들러가 한 벌로 남는다. */
/* 좌: 벽 + 타임라인 (세로) · 우: 인스펙터 (전체 높이).
   인스펙터를 벽과만 나란히 뒀을 때는 477px 뿐이라 트랙·키프레임이 스크롤
   아래로 밀렸다. 타임라인 옆까지 내려오면 757px 이라 흐름 전체가 한 화면이다. */
#scenBody{display:flex;gap:10px;flex:1 1 auto;min-height:0}
#scenLeft{display:flex;flex-direction:column;gap:10px;flex:1 1 auto;min-width:0;min-height:0}
#scenLeft #scenWallSec{flex:1 1 auto;min-width:0;min-height:0}
#scenInsp{flex:0 0 330px;overflow-y:auto;background:var(--surface);
  border:1px solid var(--line);border-radius:8px;padding:4px 0 20px}
#scenInsp section{padding:11px 13px;border-bottom:1px solid var(--line)}
#scenInsp section:last-child{border-bottom:0}
#scenInsp h2{margin:0 0 9px;font-family:var(--mono);font-size:10px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
#scenInsp pre{margin:0;font-size:11px;line-height:1.5;color:var(--ink2);
  white-space:pre-wrap;word-break:break-all}

/* 좁은 칸(330px)에서는 LED 색판을 줄여 키프레임 컨트롤이 접히지 않게 한다 */
#scenInsp #ledPrev{width:64px;height:64px}
#scenInsp .row{margin-bottom:6px}

/* 꺽인 배치의 면 경계 — 2D 미리보기가 세 벽면을 한 평면처럼 보이지 않게 */
.wColLab.wFaceEdge{border-left:2px solid var(--accent)}
.wColLab[data-face="left"],.wColLab[data-face="right"]{color:var(--accent)}
