/* UTWIN application styles. Keep component sections grouped by screen responsibility. */
@font-face{font-family:"UTWIN Pretendard";src:url("../assets/fonts/Pretendard-Regular.otf") format("opentype");font-style:normal;font-weight:400;font-display:swap}
@font-face{font-family:"UTWIN Pretendard";src:url("../assets/fonts/Pretendard-Medium.otf") format("opentype");font-style:normal;font-weight:500;font-display:swap}
@font-face{font-family:"UTWIN Pretendard";src:url("../assets/fonts/Pretendard-SemiBold.otf") format("opentype");font-style:normal;font-weight:600;font-display:swap}
@font-face{font-family:"UTWIN Pretendard";src:url("../assets/fonts/Pretendard-Bold.otf") format("opentype");font-style:normal;font-weight:700 900;font-display:swap}
#dbOverlay{--studio-series:#168578;--studio-compare:#8b96a5;--studio-fill:rgba(22,133,120,.20);--studio-fill0:rgba(22,133,120,.02);color:var(--ink);color-scheme:light}
body.utwin-dark #dbOverlay{--studio-series:#56d3c1;--studio-compare:#9aaabd;--studio-fill:rgba(86,211,193,.22);--studio-fill0:rgba(86,211,193,.02);color-scheme:dark}
/* ===== 테마 시스템 =====
   새 테마 추가 = 아래처럼 토큰 세트 한 블록만 추가하면 전체 UI에 일괄 적용된다.
   body.utwin-<name>{ 토큰 값 } + JS THEMES 배열에 이름만 넣으면 됨. */
body.utwin-dark{
  --bg:#0d0f0e;--ink:#eceeed;--muted:#a2a8a5;--line:#2a2f2d;
  --panel:#161918;--t-surface2:#1d2120;--t-input:#1d2120;
  --t-chip-bg:#282d2b;--t-chip-text:#eceeed;
  --t-glass:#161918;--t-glass2:#161918;--t-sec-bg:#1d2120;
  --t-envchip-bg:#1d2120;--t-envchip-ink:#eceeed;--t-envitem-ink:#eceeed;
  --t-scene:radial-gradient(70% 60% at 30% 10%,#1a1d1c,#0d0f0e 72%);
  --sky:radial-gradient(70% 60% at 30% 10%,#1a1d1c,#0d0f0e 72%);   /* 태양광 호의 하늘 */
  /* 라이트와 같은 역할의 토큰들 — 다크에서도 accent 를 덮어야 한다.
     전에는 --accent 를 덮지 않아 다크에서 초록(var(--accent))이 그대로 남았고,
     '도서관 템플릿' 부제 대비가 1.97:1 로 사실상 안 읽혔다. */
  --accent:#eceeed;--accent-soft:#282d2b;--accent-ink:#121514;
  --surface2:#1d2120;--surface3:#252927;
  --ink3:#8d938f;--line2:#212524;
  --hatch:rgba(236,238,237,.07);--grid:rgba(236,238,237,.045);--glow:rgba(236,238,237,.05);
  /* 차트는 다크에서 색을 뒤집는다 — 라이트용 검정 선을 그대로 쓰면 배경에 묻혀 보이지 않는다 */
  --chart-line:#eceeed;--chart-line2:#6d726f;--chart-bar:#c2c6c3;
  --chart-grid:rgba(236,238,237,.12);--chart-fill:rgba(236,238,237,.16);--chart-fill0:rgba(236,238,237,.02);
  --chart-soft:rgba(236,238,237,.70);
  --chart-s2:#c2c6c3;--chart-s3:#9aa09d;--chart-s4:#6d726f;--chart-s5:#4a4f4d;
  --shadow:0 1px 2px rgba(0,0,0,.5),0 10px 26px rgba(0,0,0,.4);
  --shadow-live:0 0 0 1px rgba(236,238,237,.32),0 0 46px rgba(236,238,237,.12),0 28px 64px rgba(0,0,0,.72);
  /* 유리창 닦는 빛 — 어두운 카드에서는 흰빛이 그대로 보이므로 약하게 둔다(강하면 번쩍인다) */
  --sheen-soft:rgba(255,255,255,.05);--sheen-core:rgba(255,255,255,.22);
}
/* ---- 테마 공통 오버라이드 (utwin-themed = 라이트가 아닌 모든 테마) ---- */
body.utwin-themed{color:var(--ink)}
/* 건물 템플릿 모달 — 카드 배경이 흰색으로 고정돼 있어 다크에서 글자가 거의 안 보였다 */
body.utwin-themed .modal{background:var(--panel)!important;color:var(--ink)!important;border:1px solid var(--line)}
body.utwin-themed .modal h2{color:var(--ink)!important}
body.utwin-themed .modal .msub,body.utwin-themed .close-x,body.utwin-themed .tpl .td{color:var(--muted)!important}
body.utwin-themed .tpl{background:var(--t-surface2)!important;border-color:var(--line)!important}
body.utwin-themed .tpl .tn{color:var(--ink)!important}
body.utwin-themed .tpl.sel{border-color:var(--accent)!important;box-shadow:0 0 0 2px rgba(236,238,237,.35)!important}
body.utwin-themed .tpl .chip{background:var(--t-chip-bg)!important;color:var(--t-chip-text)!important}
body.utwin-themed .modal .foot .btn:not(.primary){background:var(--t-input)!important;color:var(--ink)!important;border-color:var(--line)!important}
/* 자동제어 플로우 스튜디오 */
/* 배경'색'만 테마색으로 덮는다. background 단축 속성으로 덮으면 background-image 까지 none 이 되어
   AI 스튜디오의 무한 격자(#aiOverlay .db-viewport 가 그리는 점)가 다크에서 통째로 사라졌다. */
body.utwin-themed .db-shell,body.utwin-themed .db-node-stage,body.utwin-themed .db-viewport{background-color:var(--t-surface2)!important}
/* 점 패턴은 보드(transform 대상)에 있으므로 다크 테마도 보드에 적용한다 */
body.utwin-themed .db-board{background-image:radial-gradient(circle,rgba(150,170,190,.28) 1px,transparent 1.2px)!important}
/* AI 스튜디오는 격자를 **뷰포트**가 그린다(무한 격자). 위 줄의 !important 가
   '#aiOverlay .db-board{background-image:none}' 을 덮어서, 다크에서 판이 자기 격자를 다시 그렸다.
   그러면 1400x900 판의 끝이 사각형으로 드러나 격자가 화면과 어긋나 보인다. */
body.utwin-themed #aiOverlay .db-board{background-image:none!important}
body.utwin-themed .db-head,body.utwin-themed .db-col,body.utwin-themed .db-inspector,body.utwin-themed .db-add-popover,body.utwin-themed .db-node{background:var(--panel)!important;color:var(--ink);border-color:var(--line)!important}
body.utwin-themed .db-close,body.utwin-themed .db-status,body.utwin-themed .db-filter button,body.utwin-themed .db-chart-tools select,body.utwin-themed .db-node-toolbar select,body.utwin-themed .db-node-edit button,body.utwin-themed .db-device,body.utwin-themed .db-kpi,body.utwin-themed .db-point,body.utwin-themed .db-node-stat span,body.utwin-themed .db-flow-chip,body.utwin-themed .db-target,body.utwin-themed .db-slot,body.utwin-themed .db-chart-wrap{background:var(--t-surface2)!important;color:var(--ink)!important;border-color:var(--line)!important}
/* 노드 종류별 색 구분 유지 (다크 색조) */
body.utwin-themed .db-node-head{background:rgba(75,165,126,.24)!important;color:var(--ink)!important}
body.utwin-themed .db-node.source .db-node-head{background:rgba(224,128,66,.26)!important}
body.utwin-themed .db-node.chart .db-node-head{background:rgba(150,110,220,.28)!important}
/* 다크에서도 노드 종류가 색으로 구분돼야 한다 — 위의 기본 초록이 !important 로 모든 머리를
   덮어서, 집계 노드와 AI 스튜디오의 여덟 종류가 전부 같은 초록으로 보였다.
   라이트의 색조를 그대로 낮은 알파로 옮긴다(글자는 위 규칙의 --ink 를 그대로 쓴다). */
body.utwin-themed .db-node.aggregate .db-node-head{background:rgba(53,143,184,.26)!important}
body.utwin-themed .db-node.ai-feed .db-node-head{background:rgba(60,140,205,.26)!important}
body.utwin-themed .db-node.ai-group .db-node-head{background:rgba(120,145,160,.26)!important}
body.utwin-themed .db-node.ai-device .db-node-head{background:rgba(74,131,196,.26)!important}
body.utwin-themed .db-node.ai-trigger .db-node-head{background:rgba(75,165,126,.24)!important}
body.utwin-themed .db-node.ai-condition .db-node-head{background:rgba(53,143,184,.26)!important}
body.utwin-themed .db-node.ai-action .db-node-head{background:rgba(224,128,66,.26)!important}
body.utwin-themed .db-node.ai-wait .db-node-head{background:rgba(236,238,237,.14)!important}
body.utwin-themed .db-node.ai-check .db-node-head{background:rgba(150,110,220,.28)!important}
body.utwin-themed .db-node.ai-notify .db-node-head{background:rgba(220,90,80,.26)!important}
body.utwin-themed .db-node.ai-chart .db-node-head{background:rgba(130,110,230,.30)!important}
body.utwin-themed .db-shell b,body.utwin-themed .db-shell span,body.utwin-themed .db-shell small,body.utwin-themed .db-shell label,body.utwin-themed .db-shell code,body.utwin-themed .db-shell h2,body.utwin-themed .db-shell h3{color:var(--ink)!important}
body.utwin-themed .db-node-toolbar{background:var(--panel)!important;border-color:var(--line)!important}
body.utwin-themed .db-node-toolbar button,body.utwin-themed .db-add-popover button{background:var(--t-surface2)!important;color:var(--ink)!important;border-color:var(--line)!important}
/* 연결 데이터 차트 설정(빌더) 모달 */
body.utwin-themed .db-chart-builder-box{background:var(--panel)!important;color:var(--ink);border-color:var(--line)!important}
body.utwin-themed .db-chart-builder-box b,body.utwin-themed .db-chart-builder-box span,body.utwin-themed .db-chart-builder-box small,body.utwin-themed .db-chart-builder-box label,body.utwin-themed .db-chart-builder-box h2,body.utwin-themed .db-chart-builder-box h3,body.utwin-themed .db-chart-builder-box p{color:var(--ink)!important}
body.utwin-themed .db-builder-section,body.utwin-themed .db-template-section,body.utwin-themed .db-template-preview{background:var(--t-surface2)!important;border-color:var(--line)!important}
body.utwin-themed .db-chart-builder-box input,body.utwin-themed .db-chart-builder-box select,body.utwin-themed .db-builder-head button,body.utwin-themed .db-builder-device,body.utwin-themed .db-builder-actions button:not(.primary),body.utwin-themed .db-template-card{background:var(--t-input)!important;color:var(--ink)!important;border-color:var(--line)!important}
body.utwin-themed .db-template-card.selected{background:var(--t-chip-bg)!important;border-color:var(--accent)!important}
/* 시작 화면의 '판 안쪽' 은 어느 테마에서도 라이트로 고정 —
   --ink·--muted 만 고정했더니 다크에서 --accent 가 흰색(#eceeed)으로 남아
   'UTWIN' 제목이 흰 판에 묻히고, 기본 버튼(새로 시작하기)이 흰 배경 + 검은 글자로 뒤집혀
   보조 버튼(불러오기)보다 약해 보였다. 화면 안에서 쓰는 토큰을 전부 라이트 값으로 고정한다.
   ★ 배경(background)은 여기서 건드리지 않는다 — 아래 .loading 의 메쉬 그라데이션이
     테마와 무관하게 쓰인다. 여기에 background 를 !important 로 다시 적으면
     shorthand 라 background-image 까지 지워져 검은 판만 남는다. */
body.utwin-themed .loading{color:#161817;
  --ink:#161817;--muted:#5d6260;--ink3:#6d726f;
  --accent:#1b1e1d;--accent-soft:#eceeed;--accent-ink:#ffffff;
  --panel:#ffffff;--line:#dfe1df;--surface2:#f4f5f3}
body.utwin-themed .loading select,body.utwin-themed .loading select option,body.utwin-themed .loading input,body.utwin-themed .loading textarea{background:#fff!important;color:var(--ink)!important;border-color:var(--line)!important}
/* ===== 컬러 토큰 (라이트) =====
   리디자인 1단계: 초록 브랜드 → 무채색(차콜) + 상태 팝컬러 3색.
   변수 이름은 기존 것을 그대로 쓰고, 스펙에 있으나 없던 역할만 새로 추가했다.
   히트맵 팔레트는 사용자가 설정에서 바꾸는 값이므로 건드리지 않는다. */
:root{
  /* 설정 서랍의 폭 — 탭 줄(.control-menu)과 아래 패널(.cfg)이 **같은 값**을 써야 상자 선이 맞는다.
     탭을 다섯으로 늘릴 때 탭 줄만 460px 로 넓혀 아래 내용과 어긋났다(지적 받음). */
  --drawer-w:min(460px,96vw);
  --bg:#dcdfdb;--ink:#161817;--accent:#1b1e1d;--lime:#c8ff5b;--muted:#5d6260;--font-ui:"UTWIN Pretendard","Noto Sans KR","Malgun Gothic",sans-serif;
  --panel:#ffffff;--line:#dfe1df;--shadow:0 1px 2px rgba(16,20,18,.07),0 10px 26px rgba(16,20,18,.09);--shadow-live:0 2px 6px rgba(16,20,18,.10),0 30px 64px rgba(16,20,18,.22);
  /* 흰 카드 위에서는 흰 빛이 보이지 않는다. 옅은 그림자 띠(양옆) + 밝은 심(가운데) 으로
     '빛이 지나간다' 를 만든다 — 유리에 비친 반사가 스치는 모양이다. */
  --sheen-soft:rgba(16,20,18,.13);--sheen-core:rgba(255,255,255,1);
  /* 카드 내부 타일·버튼 면 / 한 단계 더 어두운 면(차트 fill) */
  --surface2:#f4f5f3;--surface3:#ebedea;
  /* 캡션·단위 텍스트 / 차트 그리드선 */
  --ink3:#686d6a;--line2:#ecedeb;   /* 캡션 — 흰 면에서 4.5:1 을 넘기려고 한 단계 진하게(4.48 → 4.7) */
  /* 강조 배경(아이콘 바탕) / 강조 위 텍스트 */
  --accent-soft:#eceeed;--accent-ink:#ffffff;
  --t-input:#ffffff;--t-surface2:#f4f5f3;--t-chip-bg:#eceeed;--t-chip-text:#1b1e1d;   /* 다크에만 있던 토큰의 라이트 값 — 컴포넌트가 한 이름만 쓰게 */
  /* 유리판(상단바·서랍·층 도구·범례) — 라이트는 반투명 흰색, 다크는 --panel.
     여기에 값을 두어야 컴포넌트가 흰색을 직접 쓰지 않고, 덧칠 규칙도 필요 없어진다. */
  --t-glass:rgba(255,255,255,.93);--t-glass2:rgba(255,255,255,.97);--t-sec-bg:rgba(255,255,255,.55);
  /* 상단바 환경 칩(미세먼지·습도 등) 고유색 */
  --t-envchip-bg:#edf3f0;--t-envchip-ink:#537067;--t-envitem-ink:#3f5047;
  /* 해치 패턴선 · 배경 그리드 · 배경 글로우 */
  --hatch:rgba(22,24,23,.09);--grid:rgba(27,30,29,.045);--glow:rgba(27,30,29,.07);
  /* 차트 색 — 캔버스로 그리므로 JS 가 이 값을 읽는다(core/dom.js chartColors) */
  --chart-line:#1b1e1d;--chart-line2:#9aa09d;--chart-bar:#1b1e1d;
  --chart-grid:rgba(22,24,23,.10);--chart-fill:rgba(27,30,29,.20);--chart-fill0:rgba(27,30,29,.02);
  --chart-soft:rgba(27,30,29,.70);
  --chart-s2:#4a4f4d;--chart-s3:#6d726f;--chart-s4:#9aa09d;--chart-s5:#c2c6c3}
/* ===== 상태 팝컬러 (양 테마 공통 · 변경 금지) =====
   면(배경)에 쓰고 그 위 텍스트는 #161817 을 쓴다. 텍스트 자체를 컬러로 칠하지 않는다
   (예외: POI 리더 라인·아이콘·상태 도트는 선/점이므로 컬러 사용). */
:root{--pop1:#f0442a;--pop2:#f5c518;--pop3:#25c26e}

/* ===== 상태색 (정상·주의·경고) =====
   여기까지가 '색을 정하는 단 한 곳' 이다. 컴포넌트는 아래 토큰만 쓴다 —
   지금까지는 컴포넌트가 #1d7a45 같은 값을 직접 칠하고, 다크에서는 body.utwin-themed 로 다시
   덧칠했다. 칠하는 곳이 두 군데라 한쪽만 고쳐지는 일이 반복됐다(다크에서 흰 알약에 흰 글자 등).

   쓰는 법
     --ok / --warn / --bad            선·점·강조 글자에 쓰는 진한 색
     --ok-bg / --warn-bg / --bad-bg   옅은 면(딱지·블록 배경)
     --ok-ink / --warn-ink / --bad-ink 그 면 위의 글자색
   테마를 추가할 때는 이 아홉 개만 새로 정하면 된다. */
:root{
  --ok:#1d7a45;--ok-bg:#e6f6ec;--ok-ink:#1d7a45;--ok-soft:#3f7a58;
  --warn:#b67820;--warn-bg:#fff6e2;--warn-ink:#8a5b0d;
  --bad:#c8382c;--bad-bg:#fff0ed;--bad-ink:#b94b3e;
  --sky:linear-gradient(180deg,#f5fbff,#e7f4fd);   /* 태양광 호의 하늘 */
}
/* 다크 — 옅은 파스텔 면은 어두운 판에서 눈이 부시므로 같은 색조의 낮은 알파로 두고,
   글자는 어두운 배경에서 읽히는 밝은 쪽으로 올린다. */
body.utwin-dark{
  --ok:#3fbd76;--ok-bg:rgba(63,189,118,.16);--ok-ink:#6fd79a;--ok-soft:#8fd8ae;
  --warn:#e0a63a;--warn-bg:rgba(224,166,58,.16);--warn-ink:#f0c368;
  --bad:#e0685c;--bad-bg:rgba(224,104,92,.16);--bad-ink:#f09a90;
}

/* ===== 스크롤바 =====
   기본 스크롤바는 위·아래 화살표와 두꺼운 회색 트랙이 붙어 있어 어두운 카드 위에서 크게 튄다.
   화살표를 없애고 얇은 막대만 남기며, 색은 그 요소의 글자색(--ink)에서 뽑아 테마를 따라간다.
   트랙은 투명 — 카드 배경 진하기를 낮췄을 때 회색 띠가 겹쳐 보이지 않게.

   표준 속성(scrollbar-width·scrollbar-color)과 웹킷 의사요소를 같이 쓰면 크로미움은
   표준 속성만 보고 의사요소를 무시한다(화살표가 그대로 남는다). 그래서 의사요소를 기본으로 두고,
   의사요소를 모르는 브라우저에서만 표준 속성을 쓴다. */
::-webkit-scrollbar{width:9px;height:9px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{border:2px solid transparent;border-radius:8px;background-clip:padding-box;
  background-color:color-mix(in srgb,var(--ink) 26%,transparent)}
::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb,var(--ink) 46%,transparent)}
::-webkit-scrollbar-thumb:active{background-color:color-mix(in srgb,var(--ink) 60%,transparent)}
::-webkit-scrollbar-corner{background:transparent}
::-webkit-scrollbar-button{display:none;width:0;height:0}
@supports not selector(::-webkit-scrollbar){
  *{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--ink) 30%,transparent) transparent}
}
*{box-sizing:border-box}
html,body{margin:0;width:100%;height:100%;overflow:hidden;background:var(--bg);color:var(--ink);
  font-family:var(--font-ui);font-variant-numeric:tabular-nums}
canvas{display:block}
/* ★ 버튼·입력칸은 브라우저 기본 글꼴(Arial 등)을 쓴다 — 물려받게 해야 화면 전체가 프리텐다드다
   (실측: 시뮬레이션 탭 버튼이 Arial 로 그려지고 있었다). 크기·굵기는 각 규칙이 정한다. */
button,input,select,textarea,optgroup{font-family:inherit}
#bg{position:fixed;inset:0;z-index:0;background:
  radial-gradient(60% 55% at 18% 12%,rgba(27,30,29,.10),transparent 60%),
  radial-gradient(55% 50% at 55% 20%,rgba(200,255,91,.14),transparent 60%),
  radial-gradient(70% 60% at 40% 90%,rgba(27,30,29,.08),transparent 62%),var(--bg);
  animation:drift 26s ease-in-out infinite alternate}
#bg.bg-solid{background:var(--bg);animation:none}
#bg.bg-line-waves{background:repeating-radial-gradient(ellipse 115% 72% at 50% 110%,transparent 0 23px,rgba(27,30,29,.16) 24px 26px,transparent 27px 52px),linear-gradient(135deg,#edf4ee,#dfe9e2);background-size:100% 100%,100% 100%;animation:waveShift 14s ease-in-out infinite alternate}
#bg.bg-soft-aurora{background:radial-gradient(ellipse at 16% 22%,rgba(66,211,159,.44),transparent 48%),radial-gradient(ellipse at 80% 15%,rgba(126,160,255,.36),transparent 46%),radial-gradient(ellipse at 51% 95%,rgba(238,194,255,.35),transparent 55%),#11201e;background-size:150% 150%;animation:auroraShift 16s ease-in-out infinite alternate}
#bg.bg-dot-field{background-color:var(--bg);background-image:radial-gradient(rgba(27,30,29,.32) 1.25px,transparent 1.25px),radial-gradient(rgba(27,30,29,.12) 1px,transparent 1px);background-position:0 0,11px 11px;background-size:22px 22px;animation:dotShift 12s linear infinite}
/* 하늘 — WebGL 을 못 얻는 PC 를 위한 CSS 대체본이다(셰이더 판은 ui/bg-fx.js 의 sky()).
   구름까지는 못 하지만 파란 하늘은 남는다 — 배경이 통째로 비는 일은 없어야 한다. */
#bg.bg-sky{background:linear-gradient(180deg,#2f7fd0 0%,#5ea3de 42%,#a7cdec 74%,#cfe6f7 100%);animation:none}
#bg.bg-image{background-position:center;background-size:cover;background-repeat:no-repeat;animation:none}
/* WebGL 을 못 얻는 PC 를 위한 대체 모양 — 셰이더 배경과 이름을 맞춰 둔다(ui/bg-fx.js).
   셰이더가 뜨면 이 판은 캔버스에 덮여 보이지 않는다. */
#bg.bg-line-flow{background:repeating-linear-gradient(8deg,rgba(126,224,255,0) 0 18px,rgba(126,224,255,.14) 19px 21px,rgba(126,224,255,0) 22px 44px),linear-gradient(180deg,#0b1220,#05070c);background-size:100% 100%,100% 100%;animation:waveShift 16s ease-in-out infinite alternate}
/* ===== 새 배경 네 가지 =====
   요청받은 참고(reactbits: Light Fall · Prism · Grainient · Galaxy)를 UTWIN 방식으로 다시 만든 것이다.
   ★ 규칙 두 가지
     · 그림 파일도, 바깥 라이브러리도 쓰지 않는다 — 오프라인 시연에서 배경이 비면 안 된다.
       입자는 인라인 SVG(feTurbulence), 나머지는 그라데이션만으로 만든다.
     · 움직임은 background-position·transform 만 쓴다. filter:blur 를 애니메이션에 물리면
       매 프레임 큰 화면을 다시 흐리게 그려 3D 프레임을 잡아먹는다(시작 화면에서 확인).
   #bg 는 3D 캔버스 뒤에 있는 판이라 여기 그린 것이 모델 뒤로 보인다. */

/* 빛내림 — 어두운 바탕에 빛줄기가 서 있고, 밝은 띠가 위에서 아래로 천천히 흐른다 */
#bg.bg-lightfall{background-color:#05070c;
  background-image:
    linear-gradient(180deg,rgba(150,200,255,0) 0%,rgba(150,200,255,.16) 42%,rgba(150,200,255,0) 78%),
    repeating-linear-gradient(94deg,rgba(190,220,255,0) 0 46px,rgba(190,220,255,.07) 50px 58px,rgba(190,220,255,0) 62px 128px),
    radial-gradient(72% 46% at 50% -8%,rgba(140,190,255,.30),transparent 72%),
    linear-gradient(180deg,#0b1424 0%,#05070c 82%);
  background-size:100% 62%,260px 100%,100% 100%,100% 100%;
  background-repeat:no-repeat,repeat,no-repeat,no-repeat;
  animation:lightfallDrop 11s linear infinite}
@keyframes lightfallDrop{
  0%{background-position:0 -62%,0 0,0 0,0 0}
  100%{background-position:0 162%,0 0,0 0,0 0}}

/* 프리즘 — 어두운 유리에 분광된 색 띠가 비스듬히 놓이고 아주 느리게 어긋난다 */
#bg.bg-prism{background-color:#070810;
  background-image:
    linear-gradient(118deg,rgba(255,90,120,0) 26%,rgba(255,90,120,.34) 37%,rgba(255,200,80,.30) 44%,rgba(120,255,190,.26) 51%,rgba(110,170,255,.34) 58%,rgba(190,120,255,.30) 66%,rgba(190,120,255,0) 78%),
    linear-gradient(118deg,rgba(255,255,255,0) 45%,rgba(255,255,255,.16) 50%,rgba(255,255,255,0) 55%),
    radial-gradient(60% 50% at 22% 18%,rgba(90,120,255,.24),transparent 70%),
    radial-gradient(66% 56% at 80% 84%,rgba(255,120,190,.20),transparent 72%),
    linear-gradient(160deg,#0a0c18,#05060c);
  /* 띠 판을 화면보다 조금만 크게 둔다 — 180% 로 두면 움직이는 동안 띠가 화면 밖으로 빠져
     한동안 검은 판만 보인다(실측: 왼쪽 위 사분면에 아무 것도 없었다). */
  background-size:120% 120%,120% 120%,100% 100%,100% 100%,100% 100%;
  animation:prismSlide 26s ease-in-out infinite alternate}
@keyframes prismSlide{
  0%{background-position:0% 100%,3% 96%,0 0,0 0,0 0}
  100%{background-position:100% 0%,97% 4%,0 0,0 0,0 0}}

/* 그레인 그라데이션 — 큰 색덩이가 번지고 그 위에 미세한 입자가 얹힌다(시작 화면과 같은 결) */
#bg.bg-grainient{background-color:#0a0f14;
  background-image:
    radial-gradient(58% 52% at 16% 14%,#3d4a54 0%,rgba(61,74,84,0) 60%),
    radial-gradient(52% 46% at 84% 10%,#243440 0%,rgba(36,52,64,0) 58%),
    radial-gradient(64% 58% at 78% 86%,#46525f 0%,rgba(70,82,95,0) 62%),
    radial-gradient(56% 62% at 8% 92%,#141a20 0%,rgba(20,26,32,0) 58%),
    radial-gradient(82% 82% at 46% 52%,#0c1116 0%,#070a0e 100%);
  background-size:150% 150%;
  animation:grainientDrift 30s ease-in-out infinite alternate}
#bg.bg-grainient::after{content:"";position:absolute;inset:0;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity:.17;mix-blend-mode:screen}
@keyframes grainientDrift{100%{background-position:12% 8%,-8% 10%,6% -8%,-6% -6%,0 0}}

/* 갤럭시 — 성운 두 덩이와 크기가 다른 별 세 겹. 별판을 아주 느리게 돌린다 */
#bg.bg-galaxy{background-color:#03040a;
  background-image:
    radial-gradient(48% 38% at 28% 32%,rgba(120,90,255,.30),transparent 70%),
    radial-gradient(54% 42% at 74% 66%,rgba(60,190,220,.22),transparent 72%),
    radial-gradient(90% 70% at 50% 50%,rgba(10,14,34,.85),#03040a 100%);
  background-size:130% 130%,130% 130%,100% 100%;
  animation:galaxyBreathe 34s ease-in-out infinite alternate}
#bg.bg-galaxy::after{content:"";position:absolute;inset:-30%;pointer-events:none;
  background-image:
    radial-gradient(#ffffff 1.1px,transparent 1.2px),
    radial-gradient(rgba(255,255,255,.75) .9px,transparent 1px),
    radial-gradient(rgba(190,215,255,.55) .7px,transparent .8px);
  background-size:190px 190px,117px 117px,73px 73px;
  background-position:0 0,37px 61px,19px 23px;
  animation:galaxySpin 240s linear infinite}
@keyframes galaxyBreathe{100%{background-position:14% 10%,-10% -8%,0 0}}
@keyframes galaxySpin{100%{transform:rotate(360deg)}}

@keyframes drift{100%{background-position:4% 3%,-3% 2%,2% -3%,0 0}}
@keyframes waveShift{100%{background-position:0 -34px,0 0}}@keyframes auroraShift{100%{background-position:20% 10%,-12% 18%,8% -12%,0 0}}@keyframes dotShift{100%{background-position:22px 22px,33px 33px}}
#grid{position:fixed;left:0;top:0;width:100vw;height:100vh;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,rgba(27,30,29,.035) 1px,transparent 1px),
             linear-gradient(rgba(27,30,29,.035) 1px,transparent 1px);background-size:42px 42px}
#three{position:fixed;left:0;top:0;z-index:2}

/* ★ 상단 바만 겨냥한다(body 의 직속 header). 태그 선택자로 두면 다른 화면의 <header> 까지
   물들어 사고가 난다 — 도움말 상자가 화면 맨 위로 튀어나갔고(그때는 div 로 우회했다),
   위젯 설정 창의 머리줄은 pointer-events:none 을 물려받아 ?·× 가 눌리지 않았다(지적 받음). */
body>header{position:fixed;z-index:6;left:0;top:0;padding:20px 26px;pointer-events:none}
/* 브랜드는 버튼이다 — 누르면 처음 화면(시작 화면)으로 간다(요청).
   button 이라 기본 테두리·배경·글자 정렬을 지워야 예전 모양이 그대로 유지된다.
   h1 을 span 으로 바꾼 이유: 버튼 안에 제목 태그를 넣으면 읽기 순서가 어그러진다. */
.brand{display:flex;align-items:center;gap:12px;pointer-events:auto;
  appearance:none;border:0;background:none;padding:0;margin:0;text-align:left;cursor:pointer;font:inherit;color:inherit}
.brand>span{display:block}
.brand-name{display:block;font-size:22px;font-weight:700;letter-spacing:-.04em;color:var(--ink)}
.brand-name b{color:var(--accent)}
.brand:hover .brand-name,.brand:focus-visible .brand-name{color:var(--accent)}
.brand:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
/* 유트윈 실제 로고의 큐브 마크. 헤더는 3D 씬 위에 떠 있어 배경색이 정해져 있지 않으므로
   로고 원본과 같은 흰 카드 위에 올려 어떤 장면에서도 읽히게 한다. */
.logo{width:34px;height:34px;border-radius:5px;background:#fff url(../../img/utwin-mark.png) center/auto 23px no-repeat;border:1px solid rgba(22,24,23,.09);box-shadow:0 8px 20px rgba(16,20,18,.16)}

.brand .sub{display:block;font-size:10px;color:var(--muted);letter-spacing:.28em;margin-top:2px}
/* 상단바는 세 묶음이다(요청) — ① 스튜디오·시나리오 ② 환경 정보 ③ 화면 도구.
   한 알약에 열넷을 이어 붙여 두니 어디까지가 한 갈래인지 읽히지 않았다. 묶음마다 알약을 따로 두고
   사이를 띄운다 — 구분선을 그리지 않아도 갈래가 보인다. */
.top-environment{position:fixed;z-index:18;top:12px;right:16px;display:flex;align-items:center;gap:9px;white-space:nowrap}
.top-group{display:flex;min-height:38px;align-items:center;gap:5px;padding:5px 7px;border:1px solid var(--line);
  border-radius:999px;background:var(--t-glass);box-shadow:0 8px 24px rgba(16,20,18,.11);backdrop-filter:blur(16px)}
.top-building{display:flex;height:30px;align-items:center;gap:7px;padding:0 8px;border:0;border-radius:999px;background:transparent;color:var(--ink);cursor:pointer}.top-building[hidden]{display:none}.top-building:hover{background:var(--accent-soft)}.top-building>span:nth-child(2){display:flex;flex-direction:column;align-items:flex-start}.top-building b{font-size:10px}.top-building small{color:var(--accent);font-size:7px;font-weight:700}.top-env-icon{display:grid;width:22px;height:22px;place-items:center;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:12px;font-style:normal;font-weight:900}
.top-env-divider{width:1px;height:21px;background:var(--line)}.top-env-item{display:flex;height:28px;align-items:center;gap:4px;padding:0 7px;border-radius:999px;color:var(--t-envitem-ink);font-size:9px}.top-env-item i{display:grid;min-width:22px;height:20px;place-items:center;border-radius:999px;background:var(--t-envchip-bg);color:var(--t-envchip-ink);font:800 6px/1 var(--font-ui);letter-spacing:.03em}.top-env-item i.weather{color:var(--ink3);font-size:12px}.top-env-item b{font-size:9px;font-variant-numeric:tabular-nums}.top-env-item small{color:var(--muted);font-size:6px}.top-env-item.dust b{color:var(--accent)}#clockT{display:inline-block;width:51px;font-family:var(--font-ui);text-align:center;font-variant-numeric:tabular-nums}#clockD{display:inline-block;width:91px;text-align:center;font-variant-numeric:tabular-nums}
.top-command{display:flex;height:30px;align-items:center;gap:6px;padding:0 9px;border:0;border-radius:999px;background:transparent;color:var(--muted);font-family:var(--font-ui);cursor:pointer;white-space:nowrap;transition:background .18s,color .18s}.top-command span{display:grid;width:20px;height:20px;place-items:center;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:900}.top-command b{font-size:9px}.top-command:hover,.top-command[aria-expanded="true"]{background:var(--accent-soft);color:var(--accent)}.top-command[aria-expanded="true"] span{background:var(--accent);color:var(--accent-ink)}.top-command-divider{margin-left:1px}
/* ===== 전체화면 = 시연 상태 =====
   전체화면으로 들어가면 상단바에서 **만들고 고치는 길**을 감춘다 — 스튜디오·시나리오 묶음과
   대시보드 편집·설정·테마다. 남는 것은 전체화면(나가기)과 자동 순회 둘이고, 환경 정보(시각·날씨)는
   시연에서 읽는 값이라 그대로 둔다(요청). 표시는 ui/fullscreen.js 가 몸에 붙인다.
   ★ 여기서 :fullscreen 대신 몸의 표시를 쓰는 이유 — F11 로 들어간 브라우저 전체화면은
     :fullscreen 이 걸리지 않는다. 몸의 표시는 fullscreenchange 를 되받아 맞추므로 한 길이다. */
body.utwin-fullscreen .top-group-studio{display:none}
body.utwin-fullscreen .top-group-tools>.top-command:not(.fullscreen-toggle):not(.tour-toggle){display:none}
.ut-dashboard-cancel[hidden]{display:none}.ut-dashboard-cancel span{background:#f6dede;color:#b4432f}.ut-dashboard-cancel:hover{background:#fbeceb;color:#b4432f}

/* 층 메뉴는 마우스로 끌어 미세 위치를 맞출 수 있다(dashboard.js bindFloorDockDrag).
   끌고 있는 동안에는 transition 을 끈다 — 켜 두면 커서를 따라오지 않고 뒤에서 미끄러진다.
   커서는 배경에서만 grab 이고 버튼 위에서는 평범한 pointer 다(클릭을 막지 않는다). */
.left-controls{position:fixed;z-index:9;left:26px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;align-items:center;gap:7px;cursor:grab;transition:left .28s,right .28s,top .28s,bottom .28s,transform .28s}
.left-controls.dragging{cursor:grabbing;transition:none;user-select:none}
.left-controls button{cursor:pointer}
.floor-dock-tools{display:none;padding:6px;border:1px solid var(--line);border-radius:5px;background:var(--t-glass2);box-shadow:0 8px 22px rgba(16,20,18,.12);backdrop-filter:blur(14px)}body.dashboard-editing .floor-dock-tools{display:block}.floor-dock-tools span{display:block;margin-bottom:4px;color:var(--muted);font-size:7px;font-weight:900;text-align:center}.floor-dock-tools div{display:flex;gap:3px}.floor-dock-tools button{width:23px;height:23px;padding:0;border:1px solid var(--line);border-radius:5px;background:var(--t-surface2);color:var(--muted);font-size:11px;font-weight:900;cursor:pointer}.floor-dock-tools button:hover,.floor-dock-tools button.on{border-color:var(--accent);background:var(--accent);color:var(--accent-ink)}
.nav{max-height:calc(100vh - 48px);overflow-y:auto;padding:12px;background:var(--panel);border:1px solid var(--line);border-radius:5px;box-shadow:var(--shadow);backdrop-filter:blur(18px);scrollbar-width:thin}
.nav .cap{color:var(--muted);font:800 9px/1 var(--font-ui);letter-spacing:2px;text-align:center;padding:2px 0 12px}
.nav button{display:block;width:84px;height:var(--floor-btn-h,44px);border:0;border-top:1px solid var(--line);background:transparent;
  color:var(--muted);font-size:var(--floor-btn-fs,14px);font-weight:700;cursor:pointer;transition:.22s;border-radius:5px}

/* ===== 층이 많은 건물 =====
   세로 배치는 1920×1080 에서 버튼 44px × 21개가 한계였다(실측) — 전체·히트맵·청정도를 빼면
   약 18층이라 17층 건물부터 스크롤바가 생겼다. 층 수로 문턱을 박지 않고 floors.js 가
   '실제로 넘치는지' 재서 2열로 바꾼다(화면 높이가 달라도 맞는다).
   폭을 줄이는 것은 답이 아니다 — 넘치는 방향은 세로다.
   버튼 크기는 줄이지 않는다: 2열만으로 실측 33개(약 30층)까지 스크롤 없이 들어가므로
   글자를 작게 만들 이유가 없었다. 40층대는 여전히 넘치며, 그때는 상·하 가로 배치가 37개를 담는다.
   2열 — 캡션과 히트맵·청정도 버튼은 두 열을 걸친다 */
/* 장면 줄 — 층 메뉴의 **하위 메뉴**처럼 보이게 한다(요청).
   같은 폭·같은 상자에 위쪽 모서리를 맞물리고(위 모서리 각지게 · 위 테두리 없음) 사이 간격을 없앤다.
   버튼은 층 버튼과 같은 폭(84px)이고 높이만 낮다. */
/* ★ hidden 을 CSS display 가 덮지 않게 — 층이 하나일 때 '전체' 를 감추는데, .nav button 규칙이
   display 를 주면 [hidden] 이 무력해진다(실측: 감췄는데 화면에 그대로 보였다). */
.nav button[hidden],.nav [hidden]{display:none!important}
/* ★ 장면 상자는 층 상자와 **떼어** 둔다(요청) — 붙여 놓으니 칸 크기가 어색했다.
   자기 테두리·자기 폭을 갖고, 층 메뉴와 같은 부모(.left-controls) 안에 있어 옮기면 함께 따라간다. */
/* 바탕은 '약간 밝은 검정'(요청) — 층 상자와 붙어 있어도 하위 메뉴임이 한눈에 보인다.
   라이트·다크 모두 이 색을 쓴다(일부러 어둡게 두는 자리라 테마를 따르지 않는다). */
/* 자동 순회 버튼 — 도는 중에는 상단바에서 켜져 보인다 */
.top-command.tour-toggle.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.top-command.tour-toggle.on b,.top-command.tour-toggle.on span{color:var(--accent-ink)}
.nav.scene{--scene-bg:#242827;--scene-ink:#f2f4f3;--scene-line:#3a3f3e;--scene-w:104px;
  padding:10px 12px 12px;background:var(--scene-bg);border-color:var(--scene-line);
  display:flex;flex-direction:column;gap:5px;max-height:46vh}
/* 장면 목록과 히트맵 묶음 — 세로로 쌓는다(장면 상자 안) */
.nav.scene>#sceneList{display:flex;flex-direction:column;gap:4px}
/* ★ 히트맵·청정도는 이제 **장면의 항목**이다(요청) — 별도 버튼은 감춘다.
   DOM 에서 없애지는 않는다: heatmap.js 가 그 버튼으로 상태를 표시하고 대상 층·범례를 맞추므로,
   장면 쪽에서 그 버튼을 눌러 재사용한다(같은 코드를 두 벌 만들지 않는다). */
.nav.scene .scene-heat{display:none}
/* 히트맵 버튼도 장면 상자의 어두운 바탕에 맞춘다(밖에서 쓰던 밝은 색을 덮어쓴다) */
.nav.scene .heat-btn{width:var(--scene-w);height:30px;margin:0;padding:0 6px;font-size:11px;font-weight:800;
  border:1px solid var(--scene-line);border-radius:5px;background:#2c3130;color:var(--scene-ink)}
.nav.scene .heat-btn:hover:not(:disabled){border-color:#8d9997;color:#fff}
.nav.scene .heat-btn.on{background:var(--scene-ink);border-color:var(--scene-ink);color:#1b1e1d}
.nav.scene .heat-btn:disabled{opacity:.4;cursor:default}
.nav.scene .cap-row{display:flex;align-items:center;justify-content:space-between;width:var(--scene-w);margin:0 0 5px}
.nav.scene .cap{font-size:10px}
.nav.scene .cap{margin:0;padding-left:2px;color:#9aa3a1}
.nav.scene .scene-help,.nav.scene .scene-pencil{width:22px;height:22px;flex:0 0 20px;display:flex;align-items:center;justify-content:center;
  padding:0;border:1px solid transparent;border-radius:5px;background:transparent;color:#9aa3a1;cursor:pointer}
.nav.scene .scene-pencil>svg{width:13px;height:13px}
.nav.scene .scene-help{font-size:11px;font-weight:900}
.nav.scene .scene-help:hover,.nav.scene .scene-pencil:hover{color:#fff;border-color:var(--scene-line)}
.nav.scene .scene-pencil.on{background:var(--scene-ink);border-color:var(--scene-ink);color:#1b1e1d}
.nav.scene button{display:block;width:var(--scene-w);height:30px;padding:0 8px;font-size:12px;font-weight:800;
  border:1px solid var(--scene-line);border-radius:5px;background:#2c3130;color:var(--scene-ink);
  cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-align:center}
.nav.scene button:hover{border-color:#8d9997;color:#fff}
.nav.scene button.on{background:var(--scene-ink);border-color:var(--scene-ink);color:#1b1e1d}
.nav.scene button.scene-add{border-style:dashed;background:transparent;color:#b9c2c0}
/* 편집 줄 — 아이콘 넷을 84px 안에 넣는다 */
/* 편집 줄 — 아이콘 여섯(📌 표출 ↑ ↓ ✎ ×)을 **3개씩 두 줄**로 넣는다.
   한 줄에 다 넣었더니 한 칸이 16px 밖에 안 돼 무엇인지 읽을 수 없었다(지적 받음). */
/* 편집 중에는 상자를 넓힌다 — 84px 에 아이콘 다섯을 넣으면 무엇인지 안 보인다(지적 받음).
   넓히는 것은 편집 중일 때뿐이라 평소 화면은 그대로 좁다. */
.nav.scene.editing{--scene-w:152px}
/* ★ 선택자를 .nav.scene 아래로 붙여야 한다 — `.nav.scene button{width:84px}` 이 더 세서
   그냥 .scene-tools button 으로는 폭이 안 먹고 한 칸이 84px 이 됐다(실측: 84×26 여섯 줄). */
.nav.scene .scene-tools{display:grid;grid-template-columns:repeat(5,1fr);gap:3px;width:var(--scene-w);margin:0 0 3px}
.nav.scene .scene-tools button{width:auto;min-width:0;height:28px;padding:0;display:flex;
  align-items:center;justify-content:center;line-height:1;
  background:#343937;color:#e6ebe9;border-color:#4d5452}
.nav.scene .scene-tools button>svg{width:14px;height:14px}
.nav.scene .scene-tools button:hover:not(:disabled){background:#4a514f;border-color:#9aa5a3;color:#fff}
.nav.scene .scene-tools button:disabled{opacity:.28;cursor:default}
/* 표출은 제 줄에 글자로 — 무엇을 표출하는지가 값이라 그림으로는 못 읽는다 */
.nav.scene .scene-show-row{width:var(--scene-w);margin:0 0 5px}
.nav.scene .scene-show-row button.scene-show{width:var(--scene-w);height:26px;font-size:10px;font-weight:800;
  background:#343937;color:#e6ebe9;border-color:#4d5452}
.nav.scene .scene-show-row button.scene-show:hover{background:#4a514f;border-color:#9aa5a3;color:#fff}
/* 표출을 가진 장면은 칩 오른쪽에 작은 점 — 낱말을 붙이지 않는다 */
.nav.scene button.show-temperature::after,.nav.scene button.show-air::after{
  content:"";position:absolute;right:6px;top:50%;width:5px;height:5px;margin-top:-2.5px;border-radius:50%}
.nav.scene button.show-temperature::after{background:#f0a63c}
.nav.scene button.show-air::after{background:#48c0a3}
.nav.scene button.has-view{position:relative}
.nav.scene button.show-temperature,.nav.scene button.show-air{position:relative}
.left-controls.floor-dock-top .nav.scene,.left-controls.floor-dock-bottom .nav.scene{flex-direction:row;align-items:center;margin:0;border-top:1px solid var(--line);border-radius:5px}
.left-controls.floor-dock-top .nav.scene>#sceneList,.left-controls.floor-dock-bottom .nav.scene>#sceneList{flex-direction:row;align-items:center}

.left-controls.floor-dock-top .nav.scene button,.left-controls.floor-dock-bottom .nav.scene button{width:76px;flex:0 0 76px;height:30px}
.left-controls.floor-dock-top .nav.scene .scene-tools,.left-controls.floor-dock-bottom .nav.scene .scene-tools{width:auto;margin:0}
.left-controls.floors-2col .nav{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:4px;align-content:start}
.left-controls.floors-2col .nav .cap,
.left-controls.floors-2col .nav .heat-btn{grid-column:1 / -1}
.left-controls.floors-2col .nav button{width:auto;border-top:0}
.nav button:first-of-type{border-top:0}
.nav button:hover{color:var(--accent)}
.nav button.on{color:var(--accent-ink);background:var(--accent);box-shadow:0 8px 18px rgba(27,30,29,.25)}
.heat-btn{margin-top:10px;width:84px;padding:10px 5px;border:1px solid var(--accent);border-radius:5px;
  background:var(--t-surface2);color:var(--accent);font-size:11px;font-weight:800;cursor:pointer;transition:.2s;white-space:nowrap}
.heat-btn.on{background:var(--accent);color:var(--accent-ink)}
.heat-btn:disabled{border-color:var(--line);color:var(--ink3);background:var(--t-sec-bg);cursor:default}
/* 청정도(종합 공기질) 히트맵 — 온도 히트맵과 구분되는 색으로 둔다 */
.heat-btn.air-btn{margin-top:6px;border-color:#2f8fbe;color:#2f8fbe}
.heat-btn.air-btn.on{background:#2f8fbe;color:#fff}
.heat-btn.air-btn:disabled{border-color:var(--line);color:var(--ink3)}
.heat-btn[hidden]{display:none}
.left-controls.floor-dock-top .nav,.left-controls.floor-dock-bottom .nav{display:flex;max-width:calc(100vw - 48px);max-height:none;overflow-x:auto;overflow-y:hidden;align-items:center;padding:8px 10px}.left-controls.floor-dock-top .nav .cap,.left-controls.floor-dock-bottom .nav .cap{padding:0 10px 0 2px;writing-mode:vertical-rl}.left-controls.floor-dock-top .nav button,.left-controls.floor-dock-bottom .nav button{flex:0 0 66px;width:66px;height:38px;border-top:0;border-left:1px solid var(--line);font-size:12px}.left-controls.floor-dock-top .nav button:first-of-type,.left-controls.floor-dock-bottom .nav button:first-of-type{border-left:0}.left-controls.floor-dock-top .heat-btn,.left-controls.floor-dock-bottom .heat-btn{width:70px;margin:0 0 0 7px;padding:0}.left-controls.floor-dock-top .floor-dock-tools,.left-controls.floor-dock-bottom .floor-dock-tools{order:2}
/* 설정 센터·서랍은 상단 환경 바 **아래**에서 시작한다.
   전에는 top:0 에서 108px 높이여서 상단 바(건물명·시계·날씨, top:12 높이 38)를 덮었다.
   --top-bar-h 하나만 바꾸면 세 곳(메뉴·서랍·대시보드 작업영역)이 함께 따라온다. */
:root{--top-bar-h:62px;--menu-h:108px}
.control-menu{position:fixed;z-index:20;right:0;top:var(--top-bar-h);transform:translateX(100%);width:var(--drawer-w);height:var(--menu-h);padding:13px 14px 11px;background:var(--t-glass2);border-left:1px solid var(--line);border-bottom:1px solid var(--line);box-shadow:-16px 10px 38px rgba(16,20,18,.13);backdrop-filter:blur(18px);opacity:0;visibility:hidden;pointer-events:none;transition:transform .32s cubic-bezier(.22,1,.36,1),opacity .2s ease,visibility 0s linear .32s;display:grid;grid-template-columns:repeat(5,1fr);grid-template-rows:25px 52px;gap:6px}.control-menu.show{transform:translateX(0);opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s}.control-menu .cap{grid-column:1/-1;color:var(--accent);font:900 12px/25px var(--font-ui);letter-spacing:1.2px;padding-left:2px}.control-menu .close-menu{position:absolute;right:13px;top:11px;width:30px;height:30px;border:1px solid var(--line);border-radius:5px;background:var(--t-surface2);color:var(--muted);font-size:17px;cursor:pointer}.control-menu .close-menu:hover{border-color:var(--accent);color:var(--accent)}
.tool-btn{width:88px;height:40px;border:1px solid transparent;border-radius:5px;background:transparent;color:var(--muted);font-size:11px;font-weight:800;cursor:pointer;transition:.2s}
.control-menu .tool-btn{width:100%;min-width:0;height:52px;padding:4px;font-size:10px}
.tool-btn:hover{border-color:var(--line);background:var(--t-surface2);color:var(--accent)}
.tool-btn.on{border-color:var(--accent);background:var(--accent);color:var(--accent-ink);box-shadow:0 7px 16px rgba(27,30,29,.22)}

.cfg{position:fixed;z-index:20;right:0;top:calc(var(--top-bar-h) + var(--menu-h));transform:translateX(100%);width:var(--drawer-w);height:calc(100vh - var(--top-bar-h) - var(--menu-h));max-height:none;overflow:auto;
  background:var(--t-glass2);border:0;border-left:1px solid var(--line);border-radius:5px;padding:14px 18px 22px;box-shadow:-16px 18px 38px rgba(16,20,18,.13);backdrop-filter:blur(18px);
  opacity:0;visibility:hidden;pointer-events:none;transition:transform .32s cubic-bezier(.22,1,.36,1),opacity .2s ease,visibility 0s linear .32s}
.cfg.show{transform:translateX(0);opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s}.cfg.free-pos.show{transform:none}
.cfg.view-drawer,.cfg.eq-drawer{width:var(--drawer-w)}   /* 예전에 패널마다 폭이 달랐다 — 이제 한 값이다 */
.cfg-h{position:sticky;top:-14px;z-index:3;margin:-14px -18px 12px;padding:14px 18px 11px;font-size:13px;font-weight:800;color:var(--accent);display:flex;justify-content:space-between;align-items:center;cursor:default;user-select:none;background:var(--t-glass2);border-bottom:1px solid var(--line);backdrop-filter:blur(18px)}
.cfg-h .drawer-close{display:none}
.drawer-close{border:1px solid var(--line);border-radius:5px;background:var(--t-surface2);color:var(--muted);font-size:10px;font-weight:800;padding:5px 8px;cursor:pointer;transition:.18s}
.drawer-close:hover{border-color:var(--accent);color:var(--accent)}
.cfg-sec{padding:9px 10px;margin-top:8px;border:1px solid var(--line);border-radius:5px;background:var(--t-sec-bg)}
.cfg-sec:first-of-type{margin-top:0}.cfg-title{font-size:11px;font-weight:900;color:var(--accent);letter-spacing:.05em;margin-bottom:3px}
.cfg-row{display:flex;align-items:center;justify-content:space-between;font-size:12px;padding:7px 0;cursor:pointer}
.cfg-row.col{flex-direction:column;align-items:stretch;gap:6px;cursor:default}
.cfg-row.col span{display:flex;justify-content:space-between;color:var(--muted)}
.cfg-row.col b{color:var(--accent)}
.cfg-row input[type=checkbox]{width:16px;height:16px;accent-color:var(--accent);cursor:pointer}
.cfg-row input[type=range]{width:100%;accent-color:var(--accent)}
.cfg-row input[type=number]{width:100%;height:27px;padding:0 7px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--ink);font-size:11px}
.cfg-row select,.cfg-row input[type=color]{border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--ink);height:26px}
.cfg-row select{max-width:128px;padding:0 5px;font-size:11px}.cfg-row input[type=color]{width:42px;padding:2px;cursor:pointer}
.cfg-note{font-size:10px;line-height:1.45;color:var(--muted);padding:5px 0 1px}
/* 서랍 안 입력칸 — 낱개로 칠하지 않고 여기 한 곳에서 정한다.
   select option 은 다크에서 브라우저 기본 흰 바탕이 남아 흰 글자가 안 보였다. */
.cfg input:not([type=range]):not([type=checkbox]),.cfg select,.cfg textarea,
.control-menu input:not([type=range]):not([type=checkbox]),select option{
  background:var(--t-input);color:var(--ink);border-color:var(--line)}
/* 저장파일 목록의 모델 배지 — 이 저장이 모델까지 담고 있는지 열어 보지 않아도 알 수 있게 */
#snapshotFolderList .tool-btn{height:auto;padding:7px 9px;display:flex;flex-direction:column;align-items:flex-start;gap:2px;line-height:1.35}
.snap-models{font-size:9px;font-weight:700;letter-spacing:.1px}
.snap-models[data-state=ok]{color:var(--accent)}
.snap-models[data-state=none]{color:var(--muted)}
.snap-models[data-state=missing]{color:#c0392b}
/* 서버가 옛 코드로 떠 있을 때의 경고 띠 — 재시작하지 않으면 저장·복원이 조용히 실패한다 */
#staleServer{position:fixed;left:0;right:0;top:0;z-index:9999;display:none;gap:10px;align-items:center;justify-content:center;
  padding:9px 14px;background:#c0392b;color:#fff;font-size:12px;font-weight:800;box-shadow:0 4px 14px rgba(0,0,0,.28)}
#staleServer.on{display:flex}
#staleServer small{font-weight:600;opacity:.9}
#staleServer button{border:1px solid rgba(255,255,255,.55);border-radius:5px;background:transparent;color:#fff;font-size:11px;font-weight:800;padding:3px 9px;cursor:pointer}
.node-pos{display:grid;grid-template-columns:repeat(3,1fr);gap:5px;margin-top:6px}
.node-pos label{font-size:9px;color:var(--muted)}.node-pos input{width:100%;padding:5px;border:1px solid var(--line);border-radius:5px;font-size:10px;background:var(--t-input);color:var(--ink)}
.vgrid{display:grid;grid-template-columns:1fr 1fr;gap:7px}
.vgrid label{font-size:9px;color:var(--muted)}
.vgrid input,.vgrid select{width:100%;margin-top:3px;padding:6px;border:1px solid var(--line);border-radius:5px;font-size:11px;background:var(--t-input);color:var(--ink)}
.vbtns{display:flex;gap:6px;margin-top:10px}
.vbtns button{flex:1;padding:7px 6px;border:1px solid var(--accent);border-radius:5px;background:var(--t-surface2);color:var(--accent);font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap}
.vbtns button.p{background:var(--accent);color:var(--accent-ink)}
.mat-filter-list{max-height:176px;overflow:auto;margin-top:7px;border-top:1px solid var(--line)}
.mat-filter-row{display:flex;align-items:center;gap:7px;padding:6px 1px;border-bottom:1px solid var(--line2);font-size:10px;cursor:pointer}
.mat-filter-row input{width:15px;height:15px;accent-color:var(--accent);flex:0 0 auto}.mat-filter-row span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* 관제점 목록 — 장비 타입 프로파일의 points 를 동적으로 렌더한다 */
.eq-point-list{margin-top:7px;border-top:1px solid var(--line)}
.eq-point{display:flex;align-items:center;gap:8px;padding:7px 1px;border-bottom:1px solid rgba(22,24,23,.07);font-size:10px}
.eq-point>.pt-label{flex:1 1 auto;min-width:0;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.eq-point>.pt-value{flex:0 0 auto;font-size:11px;font-weight:700;color:var(--ink);font-variant-numeric:tabular-nums}
.eq-point.warn>.pt-value{color:#c07a12}
.eq-point.alarm>.pt-value{color:#c9403a}
.eq-point .pt-tag{flex:0 0 auto;font-size:8px;font-weight:700;letter-spacing:.04em;padding:2px 5px;border-radius:5px;background:rgba(22,24,23,.07);color:var(--muted)}
.eq-point.writable .pt-tag{background:color-mix(in srgb,var(--accent) 16%,transparent);color:var(--accent)}
.eq-point input[type=number],.eq-point select{flex:0 0 92px;height:26px;padding:0 6px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--ink);font-size:11px}
.eq-point input[type=checkbox]{width:16px;height:16px;accent-color:var(--accent);flex:0 0 auto;cursor:pointer}
.eq-point-empty{font-size:10px;color:var(--muted);padding:8px 1px}

/* 좌하단 층 정보 — 히트맵 범례를 품는다. 세로로 쌓고 블록째 끌어 옮긴다.
   위치는 왼쪽 아래 기준으로 잡고, 끌면 인라인 left/top 이 붙는다(ui/stage.js). */
.stage{position:fixed;z-index:5;left:32px;bottom:30px;display:flex;flex-direction:column;align-items:flex-start;gap:8px;cursor:move;user-select:none;touch-action:none}
.stage-main{display:flex;align-items:flex-end;gap:14px}
.stage.dragging{opacity:.9}
/* 큰 층 글씨는 층 표시에만 — .stage b 로 두면 안에 들어온 범례의 값까지 60px 이 된다 */
.stage-main>b{color:var(--accent);font-size:60px;line-height:.8;letter-spacing:-.04em}
.stage-main .l{font-size:13px;color:var(--ink);font-weight:700}
.stage-main .s{font-size:11px;color:var(--muted)}
/* white-space 는 pre 여야 한다 — pre-line 은 줄바꿈만 살리고 연속 공백을 접어서
   라벨 자리맞춤(padEnd)이 무너진다. 값이 라벨 길이에 따라 들쭉날쭉했다.
   z-index 는 #dashboardRoot(7) 보다 위여야 한다 — 5 로 두면 좌측 위젯과 하단 차트 뒤에 깔려
   켜도 거의 보이지 않았다(줄이 늘면서 드러났다). 사용자가 직접 켜는 디버그 표시이고
   pointer-events:none 이라 위젯 조작을 막지 않으므로 위에 그린다. */
/* ===== 화재 시뮬레이션 설정 팝업 =====
   서랍이 아니라 떠 있는 창이다 — 화점을 놓는 동안 3D 를 보면서 값을 만져야 한다.
   색은 전부 토큰이라 테마를 따라간다(덧칠 규칙 없이 라이트·다크가 함께 맞는다). */
/* 시뮬레이션 창은 좌측, 층 메뉴는 우측 고정이다. 층 전환으로 위치를 바꾸지 않는다. */
/* 창 자체는 스크롤하지 않는다 — 제목·탭은 붙어 있고 내용(.sim-scroll)만 흐른다(요청).
   flex 칼럼으로 두고 내용에 min-height:0 을 줘야 스크롤이 그 안에서 생긴다. */
.sim-panel{position:fixed;z-index:19;left:20px;top:96px;width:272px;max-height:calc(100vh - 200px);
  display:flex;flex-direction:column;overflow:hidden;
  padding:12px 14px 14px;border:1px solid var(--line);border-radius:5px;background:var(--t-glass2);
  color:var(--ink);box-shadow:var(--shadow);backdrop-filter:blur(14px)}
/* 스크롤바가 글자에 붙지 않게 오른쪽만 창 여백을 조금 빌린다 */
.sim-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;margin-right:-8px;padding-right:8px}
/* 늘 보이는 바(FDS 재생) — 스크롤 밖이라 내용을 내려도 자리를 지킨다.
   창 안쪽 여백을 빌려 창 폭을 꽉 채우고, 위에 실선을 그어 스크롤 끝을 알린다. */
.sim-foot{flex:0 0 auto;margin:8px -14px -14px;padding:8px 14px 10px;
  border-top:1px solid var(--line);background:var(--t-glass2)}
.sim-foot[hidden]{display:none}
.sim-foot .cfg-row.col{padding:3px 0}
/* 아이콘만 있는 버튼 줄(재생 조작) — 글자가 없으니 높이를 직접 준다 */
.vbtns.icons button{display:flex;align-items:center;justify-content:center;height:30px;padding:0}
.vbtns.icons button>svg{width:15px;height:15px}
/* 설명 접기 — 소제목 오른쪽의 ⓘ. 설명은 기본으로 접혀 있다(실측: FDS 탭에서 설명이 733px 였다). */
.sim-body .cfg-title{display:flex;align-items:center;justify-content:space-between;gap:6px}
/* 카메라 경로점 목록 — 한 줄에 번호·자리·손질 버튼 넷.
   장면 도구(.scene-tools)와 같은 결로 두어 아이콘 크기·간격이 화면에서 겉돌지 않게 한다. */
/* 잠긴 줄 — '경로 사용' 이 꺼져 있으면 아래 항목을 흐리게 해서 못 만지는 것이 보이게 한다 */
#camMoveSec .cfg-row.locked{opacity:.42}
#camMoveSec .cfg-row.locked *{cursor:default}
/* 히트맵 적용 재질 목록의 색 조각 — 이름 없는 재질(SBMX)을 눈으로 가려내는 자리다 */
.mat-filter-row .mat-chip{width:18px;height:18px;flex:0 0 auto;border:1px solid var(--line);border-radius:4px;
  background-repeat:no-repeat;background-position:center;image-rendering:auto}
.cam-path-list{display:flex;flex-direction:column;gap:4px;margin:6px 0 2px}
.cam-path-row{display:grid;grid-template-columns:22px 1fr repeat(4,26px);gap:4px;align-items:center;
  padding:3px 5px;border:1px solid var(--line);border-radius:5px;background:var(--t-surface2)}
.cam-path-row>b{color:var(--accent);font-size:10px;text-align:center}
.cam-path-row>span{color:var(--muted);font-size:9px;font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cam-path-row button{width:26px;height:22px;padding:0;border:1px solid var(--line);border-radius:4px;
  background:var(--t-input);color:var(--ink);font-size:9px;line-height:1;cursor:pointer}
.cam-path-row button:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}
.cam-path-row button:disabled{opacity:.35;cursor:default}
.cam-path-empty{color:var(--muted);font-size:9px;padding:2px 4px}
/* ===== 설정 섹션 제목행 =====
   기능이 늘어 나열식으로 읽힌다는 지적에 탭을 성격별로 갈랐고(카메라·화면·히트맵·장비·저장),
   섹션은 제목행에 옅은 회색 바탕을 깔아 덩어리 경계만 표시한다.
   ★ 갈래마다 다른 색과 왼쪽 세로줄을 넣었더니 과했다("그냥 배경색만 회색으로, 세로줄은 삭제").
     색은 하나(회색)로 두고 세로줄은 없앤다 — 다크에서 검정에 묻히지 않을 만큼만 밝힌다. */
.cfg-sec[data-kind]{padding:11px 12px 12px;border:1px solid var(--line);border-radius:7px;background:var(--panel)}
.cfg-sec[data-kind]>.cfg-title{margin:-11px -12px 9px;padding:8px 12px;border-radius:6px 6px 0 0;
  border-bottom:1px solid var(--line);display:flex;align-items:center;gap:6px;background:var(--t-surface2)}
body.utwin-dark .cfg-sec[data-kind]>.cfg-title{background:#2b3130}   /* 검정이 아니라 회색 */
.cfg-help{width:18px;height:18px;flex:0 0 auto;border:1px solid color-mix(in srgb,var(--ink) 30%,transparent);border-radius:50%;
  background:transparent;color:var(--muted);font:800 10px/1 var(--font-ui);cursor:pointer;display:grid;place-items:center}
/* ★ 테두리를 --line 으로 두면 다크에서 사라진다 — 섹션 제목행 회색(#2b3130)과 --line(#2a2f2d)이
   거의 같은 색이다(지적 받음). 글자색을 섞어 어느 테마에서든 원이 보이게 한다. */
body.utwin-dark .cfg-help{border-color:color-mix(in srgb,var(--ink) 45%,transparent);color:var(--ink)}
.cfg-help:hover{border-color:var(--accent);color:var(--accent)}
.cfg-help[aria-expanded="true"]{border-color:var(--accent);color:var(--accent);background:var(--t-surface2)}
.sim-panel[hidden]{display:none}
/* 시뮬레이션에서는 위젯만 접고 층 메뉴의 표시와 클릭은 유지한다. */
.left-controls{transition:left .28s,right .28s,top .28s,bottom .28s,transform .28s,opacity .9s ease}
body.sim-tucked .left-controls{z-index:19;transition:none;cursor:default;max-height:calc(100vh - 116px)}
body.sim-tucked .left-controls .floor-dock-tools{display:none}

.sim-head{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;margin-bottom:6px}
.sim-head b{display:flex;align-items:center;gap:6px;font-size:12px;font-weight:900;color:var(--accent)}
.sim-head b span[data-icon]>svg{width:14px;height:14px}
.sim-head-tools{display:flex;align-items:center;gap:5px}
/* 패널 도움말 물음표 — 닫기 버튼과 같은 크기·같은 색이라 줄이 흔들리지 않는다 */
.panel-help{width:24px;height:24px;border:1px solid var(--line);border-radius:5px;
  background:var(--t-surface2);color:var(--muted);font-size:12px;font-weight:900;line-height:1;cursor:pointer}
.panel-help:hover{border-color:var(--accent);color:var(--accent)}
.sim-head button{width:24px;height:24px;border:1px solid var(--line);border-radius:5px;
  background:var(--t-surface2);color:var(--muted);font-size:14px;line-height:1;cursor:pointer}
.sim-head button:hover{border-color:var(--accent);color:var(--accent)}
.sim-panel .cfg-row.col{padding:5px 0}
.sim-panel .cfg-title{margin-top:10px}
/* 탭 — 화재 / 유동. 같은 창에 엔진이 둘이라 어느 쪽을 보고 있는지가 늘 보여야 한다. */
.sim-tabs{flex:0 0 auto;display:flex;gap:0;margin:2px 0 8px;border-bottom:1px solid var(--line)}
.sim-tab{flex:1;padding:6px 4px;border:0;border-bottom:2px solid transparent;background:none;
  color:var(--muted);font-size:11px;font-weight:800;letter-spacing:.02em;cursor:pointer}
.sim-tab:hover{color:var(--ink)}
.sim-tab.on{color:var(--accent);border-bottom-color:var(--accent)}
.sim-body[hidden]{display:none}
/* 계산 설정은 접어 둔다 — 시연에서 매번 건드리는 값이 아니다 */
.sim-adv{margin:6px 0 2px;border:1px solid var(--line2);border-radius:5px;padding:0 8px}
.sim-adv>summary{padding:6px 0;font-size:10px;font-weight:800;color:var(--muted);cursor:pointer}
.sim-adv[open]>summary{color:var(--ink);border-bottom:1px solid var(--line2);margin-bottom:4px}
.sim-panel button[disabled]{opacity:.45;cursor:default}
/* 하위 항목 줄 — 체크 목록을 한 칸 들여 쓴다 */
.sim-panel .cfg-row.sub{padding-left:10px}
.sim-panel .cfg-row.sub>span{color:var(--muted)}
/* FDS 단면 범례 — 색이 어떤 값인지 알려 준다(Smokeview 는 이걸 늘 띄워 둔다) */
.fds-legend{margin:6px 0 2px}
.fds-legend>i{display:block;height:9px;border:1px solid var(--line);border-radius:3px}
.fds-legend>span{display:flex;justify-content:space-between;margin-top:3px;
  color:var(--muted);font-size:9px;font-weight:800}
/* 숫자 입력 줄 — 이름 왼쪽, 칸 오른쪽으로 한 줄에 앉힌다(빨간 별표는 그대로 필수 표시).
   전에는 이름·칸·조건을 세 줄로 쌓아 한 줄이 70px 였다 — 유동 탭에 12개가 있어 840px 였고,
   그래서 '영역 설정' 과 '유동량 구성' 이 730px 떨어져 늘 내려가야 했다.
   조건 글씨(i)는 ⓘ 로 접고, 접힌 동안에는 입력칸의 도움말(title)로 남는다. */
.sim-panel .cfg-row.num{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:4px 0}
.sim-panel .cfg-row.num>span{flex:1 1 auto;min-width:0;font-size:10px;font-weight:800;color:var(--ink)}
.sim-panel .cfg-row.num>span::before{content:"* ";color:var(--bad)}
.sim-panel .cfg-row.num>input{width:74px;flex:0 0 auto;box-sizing:border-box;padding:4px 6px;
  border:1px solid var(--line);text-align:right;
  border-radius:5px;background:var(--t-surface2);color:var(--ink);font:800 11px/1.2 var(--font-ui)}
.sim-panel .cfg-row.num>input:focus{outline:0;border-color:var(--accent)}
.sim-panel .cfg-row.num>input:invalid{border-color:var(--bad)}
/* 단위 — 조건 글씨를 접어도 무엇으로 적는 값인지는 보여야 한다(m · 도 · 명 · m/s …) */
.sim-panel .cfg-row.num>em{flex:0 0 26px;color:var(--muted);font-size:9px;font-weight:800;font-style:normal}
.sim-panel .cfg-row.num>i{flex:1 0 100%;margin-top:1px;color:var(--muted);font-size:9px;font-style:normal;line-height:1.35}
.fire-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:5px 7px;
  border-bottom:1px solid var(--line2);font-size:10px}
.fire-row.on{background:var(--t-chip-bg);border-radius:5px}
.fire-row button{padding:3px 7px;border:1px solid var(--line);border-radius:5px;
  background:var(--t-surface2);color:var(--muted);font-size:9px;font-weight:800;cursor:pointer}
.fire-row button:hover{border-color:var(--accent);color:var(--accent)}
/* 배치 중인 동안 상단바 버튼이 눌린 상태로 보이게 */
.top-building.on{background:var(--accent);color:var(--accent-ink)}
.top-building.on .top-env-icon{background:var(--accent-ink);color:var(--accent)}
.top-building.on small{color:var(--accent-ink)}
@media(max-width:900px){.sim-panel{left:10px;top:84px;width:min(272px,84vw)}}
.perf-stats{position:fixed;z-index:8;left:144px;bottom:114px;display:none;min-width:212px;padding:8px 9px;background:rgba(12,18,20,.90);border:1px solid rgba(255,255,255,.16);border-radius:5px;color:#f4f7f6;font:12px/1.5 var(--font-ui);box-shadow:0 8px 20px rgba(0,0,0,.18);pointer-events:none;white-space:pre}.perf-stats.show{display:block}
.heat-legend{position:static;width:186px;padding:12px 14px 11px;border:1px solid var(--line);border-radius:5px;background:var(--t-glass);box-shadow:0 10px 30px rgba(16,20,18,.12);backdrop-filter:blur(12px);opacity:0;transform:translateY(8px);pointer-events:none;transition:.3s ease}
.heat-legend{display:none}
.heat-legend.show{display:block;opacity:1;transform:translateY(0)}
.heat-legend .ht{display:flex;align-items:center;justify-content:space-between;gap:8px;white-space:nowrap;font-size:11px;font-weight:800;color:var(--ink);margin-bottom:8px}.heat-legend .ht b{color:var(--accent);font-size:11px;line-height:1.2;letter-spacing:0}
.heat-legend .scale{height:9px;border-radius:5px;background:linear-gradient(90deg,#0878ff,#16bdf2,#38d982,#e6df36,#ff8a24,#f13b2f);box-shadow:0 0 10px rgba(57,190,255,.18)}
.heat-legend .ticks{display:flex;justify-content:space-between;margin-top:5px;font-size:9px;color:var(--muted)}
/* 그릴 것이 없을 때의 안내 — 눈금 아래에 한 줄. 경고가 아니라 다음에 할 일을 알려 주는 자리다 */
.heat-legend .ht-none{margin-top:9px;padding-top:8px;border-top:1px solid var(--line);
  font-size:10px;line-height:1.5;color:var(--muted);white-space:normal}
.heat-legend .ht-none b{color:var(--ink);font-weight:800}

/* 우측 1/3 패널 */
.panel{position:fixed;right:0;top:0;bottom:0;width:33.3vw;min-width:400px;z-index:6;
  background:linear-gradient(180deg,var(--surface2),var(--surface2));border-left:1px solid var(--line);
  box-shadow:-14px 0 46px rgba(16,20,18,.10);display:flex;flex-direction:column;transition:transform .36s cubic-bezier(.22,1,.36,1)}
.panel.closed{transform:translateX(100%)}
.panel-toggle{position:absolute;left:-44px;top:50%;transform:translateY(-50%);width:44px;height:92px;border:1px solid var(--line);border-right:0;border-radius:5px 0 0 5px;background:rgba(255,255,255,.96);box-shadow:-8px 8px 22px rgba(16,20,18,.12);color:var(--accent);font-size:11px;font-weight:900;letter-spacing:.04em;line-height:1.35;cursor:pointer;writing-mode:vertical-rl;display:flex;align-items:center;justify-content:center;gap:5px;transition:.2s}
.panel-toggle:hover{background:#fff;color:#0f533f}
.panel-top{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 18px;border-bottom:1px solid var(--line);flex-shrink:0}
.bldg-btn{display:flex;align-items:center;gap:10px;cursor:pointer;padding:8px 13px;border:1px solid var(--line);border-radius:5px;background:#fff;transition:.2s}
.bldg-btn:hover{border-color:var(--accent)}
.bldg-btn .ico{width:24px;height:24px;display:grid;place-items:center;border-radius:5px;background:rgba(27,30,29,.12);font-size:14px}
.bldg-btn .name{font-weight:800;font-size:14px}.bldg-btn .type{font-size:11px;color:var(--accent);font-weight:600}
.clock{text-align:right}.clock .t{font-size:16px;font-weight:800;color:var(--accent)}.clock .d{font-size:10px;color:var(--muted)}
.panel-body{flex:1;overflow:auto;padding:14px 16px;display:flex;flex-direction:column;gap:14px}
/* 칩이 둘로 늘어 상단 바가 약 100px 넓어졌다 — 1280·1440 폭에서 브랜드 제목과 겹쳤다(실측).
   축약을 시작하는 폭을 1200 → 1500 으로 올린다. 더 좁아지면 브랜드 부제를 한 번 더 줄인다
   (1100 폭에서는 330px 로 잘라도 여전히 겹쳤다). */
@media(max-width:1500px){.top-env-date,.top-building small{display:none}.top-environment{gap:6px}.top-group{gap:2px}.top-env-item{padding:0 4px}.brand .sub{max-width:330px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
/* 1500 블록의 .brand .sub{max-width:330px} 를 덮어야 하므로 뒤에 온다(같은 우선순위라 순서가 이긴다) */
@media(max-width:1200px){.brand .sub{max-width:170px}}
@media(max-width:900px){:root{--drawer-w:min(420px,96vw)}.top-building,.top-env-item:nth-of-type(5){display:none}.top-environment{right:10px;gap:6px}.top-group:empty{display:none}.top-env-item{padding:0 3px}.top-command{padding:0 7px}.top-command b{display:none}}
.wcap{font-size:11px;font-weight:800;letter-spacing:.12em;color:var(--muted);text-transform:uppercase;
  display:flex;align-items:center;gap:8px;margin:2px 2px 0}
.wcap::before{content:"";width:3px;height:12px;border-radius:5px;background:var(--accent)}
.wcap .exp{margin-left:auto;font-size:10px;color:var(--accent);cursor:pointer;letter-spacing:0}

/* 날씨 위젯 (예시 마크업 스케일) */
.wx-embed{position:relative;width:100%;overflow:hidden;border-radius:5px}
.wx-screen{width:1120px;transform-origin:top left;background:#fff;border-radius:5px;
  padding:26px 34px 30px;box-shadow:inset 0 0 0 1px rgba(0,0,0,.05);
  font-family:var(--font-ui)}

/* 생키 위젯 (예시 다크 패널) */
.sk{background:linear-gradient(180deg,#0f172a,#0c1322);border:1px solid rgba(236,238,237,.14);
  border-radius:5px;padding:18px 20px;color:#e2e8f0;font-family:var(--font-ui)}
.sk-tag{font-size:10px;font-weight:700;letter-spacing:.16em;color:#facc15;text-transform:uppercase;margin-bottom:5px}
.sk-title{font-size:15px;font-weight:800;color:#f1f5f9}
.sk-sub{font-size:11px;color:#94a3b8;margin-top:3px}
.sk-svg{width:100%;height:auto;display:block;margin-top:6px}
.sk-foot{font-size:10px;color:#64748b;margin-top:10px;padding-top:10px;border-top:1px solid rgba(236,238,237,.10);display:flex;justify-content:space-between}

/* loading / upload */
/* 시작 화면 배경 — 메쉬 그라데이션 + 노이즈(검정 계통)
   회사 유지보수 포털 로그인 화면과 같은 결이다: 큰 색덩이 몇 개가 부드럽게 번지고
   그 경계에 미세한 입자가 얹힌다. 색만 청록·파랑 대신 검정 계통으로 바꿨다.
   ★ 그림 파일을 쓰지 않는다 — 노이즈는 인라인 SVG(feTurbulence)다.
     밖에서 받아오는 자원이 있으면 오프라인 시연에서 배경이 비어 버린다.
   ★ 번짐은 filter:blur 가 아니라 radial-gradient 의 무른 끝으로 만든다.
     blur 를 쓰면 시작 화면이 뜰 때마다 큰 화면을 다시 흐리게 그려 첫 그림이 늦다. */
.loading{position:fixed;inset:0;z-index:20;display:grid;place-items:center;
  background-color:#05070a;
  background-image:
    /* 사선 광택 — 원본 화면의 오른쪽 아래로 흐르는 빛줄기 자리다 */
    linear-gradient(118deg, rgba(255,255,255,0) 34%, rgba(214,230,238,.075) 54%, rgba(255,255,255,0) 72%),
    radial-gradient(58% 52% at 14% 10%, #55646c 0%, rgba(85,100,108,0) 58%),
    radial-gradient(50% 44% at 88% 6%,  #26333c 0%, rgba(38,51,60,0) 56%),
    radial-gradient(62% 56% at 82% 88%, #46525a 0%, rgba(70,82,90,0) 60%),
    radial-gradient(46% 42% at 62% 30%, #1b2429 0%, rgba(27,36,41,0) 62%),
    radial-gradient(56% 62% at 6% 94%,  #101417 0%, rgba(16,20,23,0) 58%),
    radial-gradient(82% 82% at 46% 52%, #0b0f12 0%, #040608 100%)}
/* 입자 — 밝게 얹어야(screen) 검은 판에서 보인다. 곱하기·오버레이는 검정에 묻힌다. */
.loading::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:0;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity:.19;mix-blend-mode:screen}
.loading.done{display:none}
.startup-layout{position:relative;z-index:1;display:grid;grid-template-columns:minmax(250px,.74fr) minmax(420px,1fr);gap:18px;align-items:stretch;width:min(850px,92vw)}
.login-panel{display:flex;flex-direction:column;padding:28px 26px;border:1px solid rgba(255,255,255,.22);border-radius:12px;background:linear-gradient(145deg,rgba(25,52,68,.96),rgba(10,24,34,.98));box-shadow:0 30px 90px rgba(0,0,0,.38);color:#f4fbff}
.login-brand{display:flex;align-items:center;gap:8px;font-size:15px;font-weight:900;letter-spacing:.08em}.login-mark{display:grid;width:25px;height:25px;place-items:center;border:1px solid rgba(183,229,247,.7);border-radius:7px;background:rgba(159,218,239,.14);font-size:13px}
.login-kicker{margin-top:22px;color:#a9cad9;font-size:9px;font-weight:800;letter-spacing:.14em}.login-panel h1{margin:8px 0 0;font-size:25px;line-height:1.18;letter-spacing:-.05em}.login-copy{margin:10px 0 20px;color:#c2d4dc;font-size:11px;line-height:1.55}
.login-form{display:grid;gap:10px}.login-form[hidden],.login-divider[hidden],.login-demo[hidden]{display:none}.login-form label{display:grid;gap:5px;color:#d9e8ee;font-size:10px;font-weight:800}.login-form input{width:100%;box-sizing:border-box;height:38px;padding:0 10px;border:1px solid rgba(188,224,237,.25);border-radius:6px;background:rgba(4,15,23,.42);color:#f7fcff;font:12px var(--font-ui);outline:none}.login-form input::placeholder{color:#8ca8b4}.login-form input:focus{border-color:#9bd7ed;box-shadow:0 0 0 3px rgba(132,208,236,.16)}
.login-status{min-height:16px;margin:0;color:#b5ced9;font-size:9px;line-height:1.35}.login-status.error{color:#ffb9a9}.login-submit,.login-demo{height:39px;border-radius:6px;font:800 11px var(--font-ui);cursor:pointer}.login-submit{display:flex;justify-content:space-between;align-items:center;padding:0 13px;border:1px solid #d7f2fb;background:#e3f7fc;color:#112936}.login-submit:hover{background:#fff}.login-submit span{font-size:16px}.login-divider{display:flex;align-items:center;gap:8px;margin:17px 0 10px;color:#91adba;font-size:9px}.login-divider::before,.login-divider::after{content:"";height:1px;flex:1;background:rgba(185,221,234,.18)}.login-demo{width:100%;border:1px solid rgba(185,221,234,.34);background:transparent;color:#e9f7fb}.login-demo:hover{border-color:#dff7ff;background:rgba(199,238,250,.10)}.login-note{margin:12px 0 0;color:#87a4b0;font-size:8px;line-height:1.45}
.login-signed{align-items:center;gap:9px;margin-top:auto;padding:12px 0 2px;border-top:1px solid rgba(189,223,235,.18)}.login-signed[hidden]{display:none}.login-signed-dot{width:8px;height:8px;border-radius:50%;background:#73e0a5;box-shadow:0 0 0 4px rgba(115,224,165,.12)}.login-signed div{display:grid;gap:2px;flex:1;min-width:0}.login-signed b{font-size:11px}.login-signed small{overflow:hidden;color:#a9c4cf;font-size:9px;text-overflow:ellipsis;white-space:nowrap}.login-signed button{border:0;background:transparent;color:#c7e2eb;font:700 9px var(--font-ui);cursor:pointer}.login-signed button:hover{color:#fff}
.box{position:relative;z-index:1;min-width:0;padding:30px 32px;border:1px solid rgba(255,255,255,.7);border-radius:12px;background:rgba(255,255,255,.97);box-shadow:0 30px 90px rgba(0,0,0,.55);width:auto}
.box:before{content:"UTWIN";display:block;color:var(--accent);font-size:22px;font-weight:900;margin-bottom:6px;text-align:center}
.pk-sub{font-size:13px;color:var(--muted);margin-bottom:14px;text-align:center}
.auth-locked{opacity:.42;filter:saturate(.2)}
.pk-btns{display:flex;gap:10px}
.pk-btn{flex:1;padding:11px;border-radius:5px;font-size:13px;font-weight:700;cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--ink)}
.pk-btn:hover{border-color:var(--accent)}.pk-btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.pk-btn:disabled{opacity:.45;cursor:default}
.pk-btn.position-start{border-color:var(--accent);box-shadow:inset 0 0 0 1px var(--accent-soft)}
.pk-list{margin-top:14px;max-height:230px;overflow:auto}
.pk-row{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--line);border-radius:5px;margin-bottom:6px;font-size:12px}
.pk-row input{width:15px;height:15px;accent-color:var(--accent)}
.pk-row .badge{min-width:34px;text-align:center;padding:3px 6px;border-radius:999px;background:rgba(27,30,29,.12);color:var(--accent);font-weight:800;font-size:11px}
.pk-row .fn{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pk-row .sz{color:var(--muted)}
.pk-row.skip{background:rgba(245,197,24,.10);border-color:rgba(245,197,24,.38)}
.pk-floor-select{width:82px;min-width:82px;padding:5px 7px;border:1px solid var(--line);border-radius:5px;background:#fff;color:var(--ink);font:inherit;font-weight:700}
/* 테마별 데모 목록 — 클릭 한 번에 전체 복원 */
.pk-demos{margin-top:14px;padding-top:12px;border-top:1px dashed var(--line)}
.pk-demos[hidden]{display:none}
.pk-demos-cap{margin-bottom:8px;color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.1em}
.pk-demo-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(132px,1fr));gap:8px}
.pk-demo-card{padding:9px 10px;border:1px solid var(--line);border-radius:5px;background:#fff;color:var(--ink);text-align:left;cursor:pointer;transition:border-color .15s,background .15s}
.pk-demo-card:hover{border-color:var(--accent);background:#f2f8f5}
.pk-demo-card b{display:block;font-size:12px}
.pk-demo-card small{display:block;margin-top:3px;color:var(--muted);font-size:9px;font-weight:500}
/* 시작 화면 상자는 테마와 무관하게 항상 밝다. 카드에 테마 색(어두운 surface)을 물리면
   밝은 상자 위에 검은 카드가 얹히고 부제 글자(회색)가 거의 안 보인다. 그래서 카드만 밝게 고정한다. */
body.utwin-themed .pk-demo-card{background:#fff!important;border-color:#d9e3dd!important;color:#12241d!important}
body.utwin-themed .pk-demo-card:hover{border-color:var(--accent)!important;background:#f2f8f5!important}
body.utwin-themed .pk-demo-card small{color:#5d6f66!important}
body.utwin-themed .pk-demos-cap{color:#5d6f66!important}
/* '+경량화' 옵션 — 기본 경량화는 항상 적용되고, 이것은 폴리곤 감축을 더한다.
   시작 화면 상자는 테마와 무관하게 항상 밝으므로(위 pk-demo-card 주석 참고) 여기도 색을 고정한다. */
.pk-opt{display:flex;align-items:flex-start;gap:9px;margin-top:14px;padding:10px 11px;
  border:1px solid var(--line);border-radius:5px;background:#fbfcfb;cursor:pointer}
.pk-opt:hover{border-color:var(--accent)}
.pk-opt input{width:15px;height:15px;margin-top:1px;flex:none;accent-color:var(--accent)}
.pk-opt-text{display:block;font-size:12px;line-height:1.45}
.pk-opt-text b{font-weight:800}
.pk-opt-text em{display:block;margin-top:2px;font-style:normal;font-size:11px;color:var(--muted)}
body.utwin-themed .pk-opt{background:#fbfcfb!important;border-color:#d9e3dd!important;color:#12241d!important}
body.utwin-themed .pk-opt-text em{color:#5d6f66!important}
.pk-hint{font-size:11px;color:var(--muted);margin-top:12px;line-height:1.5;text-align:center}
.spin{width:34px;height:34px;margin:14px auto 0;border:3px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.bar{height:3px;margin-top:16px;background:var(--line);border-radius:5px;overflow:hidden}.bar i{display:block;height:100%;width:0;background:var(--accent);transition:.3s}
@media(max-width:760px){.loading{overflow:auto;padding:20px;box-sizing:border-box}.startup-layout{grid-template-columns:1fr;width:min(460px,100%)}.login-panel{padding:22px}.login-kicker{margin-top:14px}.login-panel h1{font-size:22px}.login-copy{margin-bottom:14px}.login-signed{margin-top:14px}.box{padding:25px 23px}.pk-demo-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

.overlay{position:fixed;inset:0;z-index:30;background:rgba(22,24,23,.42);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center}
.overlay.show{display:flex}
.modal{width:min(860px,92vw);max-height:88vh;overflow:auto;background:#fff;border-radius:5px;padding:26px 28px;box-shadow:0 40px 90px rgba(16,20,18,.35)}
.modal h2{margin:0 0 4px;font-size:21px}.modal .msub{color:var(--muted);font-size:13px;margin-bottom:20px}
.tpl-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.tpl{border:1px solid var(--line);border-radius:5px;padding:16px;cursor:pointer;background:#fff;transition:.2s;position:relative}
.tpl:hover{transform:translateY(-3px);box-shadow:var(--shadow)}
.tpl.sel{border-color:var(--accent);box-shadow:0 0 0 2px rgba(27,30,29,.25)}
.tpl .em{font-size:28px}.tpl .tn{font-weight:800;font-size:15px;margin-top:8px}
.tpl .td{font-size:11px;color:var(--muted);margin-top:4px;line-height:1.5}
.tpl .chip{display:inline-block;margin-top:10px;font-size:10px;padding:3px 9px;border-radius:999px;background:rgba(27,30,29,.1);color:var(--accent);font-weight:700}
.tpl /* 템플릿 스와치 — 건물별 강조색이 없어졌으므로(templates.js 참고) 테마 강조색을 보여준다.
   인라인 background 가 비어 있을 때 이 값이 쓰인다. */
.swatch{position:absolute;top:14px;right:14px;width:16px;height:16px;border-radius:5px;background:var(--accent)}
.modal .foot{display:flex;justify-content:flex-end;gap:10px;margin-top:22px}
.btn{padding:10px 20px;border-radius:5px;font-size:14px;font-weight:700;cursor:pointer;border:1px solid var(--line);background:var(--t-input);color:var(--ink)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.close-x{float:right;cursor:pointer;color:var(--muted);font-size:24px}
.wov{position:fixed;inset:0;z-index:40;background:rgba(22,24,23,.55);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;padding:40px}
.wov.show{display:flex}
.wov-box{position:relative;width:min(1240px,94vw);max-height:92vh;overflow:auto;background:#fff;border-radius:5px;padding:24px;box-shadow:0 40px 90px rgba(16,20,18,.4)}
.wov-box.dark{background:#0c1322}
.wov-x{position:absolute;top:12px;right:16px;z-index:2;cursor:pointer;font-size:26px;color:#94a3b8}
/* ===== 자동제어 분석 플로우 스튜디오 =====
   여기 색은 스튜디오 것이다. 대시보드 무채색 팔레트를 여기까지 끌고 오지 않는다 —
   한 번 그렇게 했다가 노드 머리(장비 초록·집계 파랑·차트 보라)와 간선·판 점이 모두
   회색이 되어 흐름이 구분되지 않았다(2026-08-06 되돌림).
   글자색만 테마 토큰(--ink·--muted)을 쓴다. 그러지 않으면 다크에서 글자가 안 보인다. */
.db-overlay{position:fixed;inset:0;z-index:70;display:none;padding:24px;background:rgba(11,20,16,.62);backdrop-filter:blur(7px)}.db-overlay.show{display:grid;place-items:center}
.db-shell{width:min(1500px,97vw);height:min(900px,94vh);display:grid;grid-template-rows:auto 1fr;background:var(--t-surface2);border:1px solid rgba(255,255,255,.35);border-radius:5px;overflow:hidden;box-shadow:0 40px 110px rgba(0,0,0,.38)}
.db-head{position:relative;left:auto;top:auto;z-index:8;pointer-events:auto;display:flex;align-items:center;gap:15px;padding:15px 19px;background:var(--panel);border-bottom:1px solid var(--line)}.db-head h2{margin:0;font-size:18px}.db-head p{margin:3px 0 0;color:var(--muted);font-size:11px}.db-head .db-actions{margin-left:auto;display:flex;align-items:center;gap:8px}.db-status{padding:6px 9px;border-radius:5px;background:#e8f2ed;color:var(--accent);font-size:10px;font-weight:800}.db-close{width:34px;height:34px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--muted);font-size:20px;cursor:pointer}
.db-body{min-height:0;display:grid;grid-template-columns:250px minmax(390px,1fr) minmax(360px,430px);gap:10px;padding:10px}.db-col{min-height:0;overflow:auto;background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:13px}.db-col h3{margin:0 0 5px;font-size:12px;color:var(--accent)}.db-help{font-size:10px;line-height:1.45;color:var(--muted);margin-bottom:10px}
.db-filter{display:flex;gap:5px;margin-bottom:9px}.db-filter button{flex:1;padding:6px 3px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--muted);font-size:9px;font-weight:800;cursor:pointer}.db-filter button.on{border-color:var(--accent);background:var(--accent);color:#fff}
.db-device{width:100%;display:grid;grid-template-columns:34px 1fr auto;align-items:center;gap:7px;margin-bottom:5px;padding:8px;border:1px solid transparent;border-radius:5px;background:#f5f7f5;text-align:left;cursor:pointer}.db-device:hover,.db-device.on{border-color:#83ad9e;background:#edf5f1}.db-device .di{width:30px;height:30px;display:grid;place-items:center;border-radius:5px;background:#315f50;color:#fff;font-size:9px;font-weight:900}.db-device b{display:block;font-size:11px}.db-device small{display:block;margin-top:2px;color:var(--muted);font-size:9px}.db-device em{font-style:normal;font-size:10px;font-weight:800;color:var(--accent)}
.db-map-summary{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;margin-bottom:12px}.db-kpi{padding:9px;border-radius:5px;background:#f2f6f3;border:1px solid var(--line)}.db-kpi b{display:block;color:var(--accent);font-size:17px}.db-kpi span{font-size:9px;color:var(--muted)}
.db-flow{display:grid;grid-template-columns:1fr 38px 1.25fr;gap:8px;align-items:start}.db-source,.db-target{min-height:260px;padding:9px;border:1px solid var(--line);border-radius:5px;background:#fafbfa}.db-flow-arrow{display:grid;place-items:center;height:260px;color:#73a08f;font-size:22px}.db-section-label{margin-bottom:8px;color:var(--muted);font-size:9px;font-weight:900;letter-spacing:.1em}
.db-point{margin-bottom:6px;padding:8px;border:1px solid #cbd8d1;border-radius:5px;background:var(--panel);cursor:grab}.db-point:active{cursor:grabbing}.db-point b{display:block;font-size:10px}.db-point span{display:block;margin-top:2px;color:var(--muted);font:9px var(--font-ui);font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.db-slot{position:relative;margin-bottom:7px;padding:8px 8px 8px 12px;border:1px dashed #9bb8ac;border-radius:5px;background:#f4f9f6;transition:.15s}.db-slot::before{content:"";position:absolute;left:-5px;top:50%;width:8px;height:8px;margin-top:-4px;border-radius:50%;background:#55a181}.db-slot.drag{border-color:var(--accent);background:#e5f3ec}.db-slot b{display:block;font-size:10px}.db-slot span{display:block;margin-top:3px;color:var(--accent);font:9px var(--font-ui);font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.db-chart-tools{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:9px}.db-chart-tools select{width:100%;height:30px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px}.db-chart-wrap{position:relative;height:260px;border:1px solid var(--line);border-radius:5px;background:linear-gradient(#fbfdfb,#f5f8f5);overflow:hidden}.db-chart-wrap canvas{width:100%;height:100%}.db-latest{display:grid;grid-template-columns:repeat(4,1fr);gap:5px;margin-top:9px}.db-live{padding:8px 5px;border:1px solid var(--line);border-radius:5px;text-align:center}.db-live b{display:block;font-size:13px;color:var(--accent)}.db-live span{display:block;margin-top:2px;color:var(--muted);font-size:8px}
@media(max-width:1050px){.db-body{grid-template-columns:210px 1fr}.db-col:last-child{grid-column:1/-1}.db-shell{overflow:auto}.db-body{overflow:visible}.db-col{overflow:visible}}
.db-shell.node-mode{width:min(1680px,98vw);height:min(960px,96vh);grid-template-columns:minmax(0,1fr)}.db-shell.node-mode>.db-head,.db-shell.node-mode>.db-body{width:100%;min-width:0}.db-shell.node-mode .db-head{padding:12px 17px}.db-shell.node-mode .db-body{display:block;width:100%;height:100%;min-height:0;padding:0;position:relative;overflow:hidden}
.db-node-stage{position:relative;width:100%;height:100%;min-height:500px;background:var(--t-surface2)}.db-node-toolbar{height:48px;display:flex;align-items:center;gap:7px;padding:7px 11px;background:var(--t-glass2);border-bottom:1px solid var(--line);position:relative;z-index:6}.db-node-toolbar button,.db-node-toolbar select{height:32px;padding:0 10px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px;font-weight:800;cursor:pointer}.db-node-toolbar button:hover{border-color:var(--accent);color:var(--accent)}.db-node-toolbar .spacer{flex:1}.db-node-kpi{font-size:9px;color:var(--muted);white-space:nowrap}.db-node-kpi b{color:var(--accent);font-size:11px}
/* 툴바 구분선·메뉴 소제목·빈 캔버스 안내 — 처음 여는 사용자가 순서를 알 수 있게 */
.db-tool-sep{width:1px;height:20px;margin:0 3px;background:#d3dcd7}
.db-node-toolbar button:disabled{opacity:.42;cursor:default}
.db-add-group{display:block;margin:6px 8px 3px;color:#8a9a91;font-size:8px;font-weight:800;letter-spacing:.04em}
.db-empty-hint{position:absolute;z-index:3;left:50%;top:38%;width:min(420px,72%);margin:0;padding:14px 16px;transform:translateX(-50%);border:1px dashed #bcc8c2;border-radius:5px;background:rgba(255,255,255,.92);color:#5c6b62;font-size:11px;line-height:1.7;text-align:center;pointer-events:none}
/* 방금 만든 노드 — 어디 생겼는지 바로 보이게 1.4초 반짝인다 */
@keyframes dbJustAdded{
  0%{box-shadow:0 0 0 0 rgba(245,197,24,.95),0 8px 22px rgba(16,20,18,.18);transform:scale(1)}
  18%{transform:scale(1.035)}
  60%{box-shadow:0 0 0 14px rgba(245,197,24,0),0 8px 22px rgba(16,20,18,.18);transform:scale(1)}
  100%{box-shadow:0 0 0 0 rgba(245,197,24,0),0 8px 22px rgba(16,20,18,.18)}
}
.db-node.just-added{animation:dbJustAdded 700ms ease-out 2;border-color:#d2a33d;z-index:4}
@media (prefers-reduced-motion:reduce){.db-node.just-added{animation:none;border-color:#d2a33d;box-shadow:0 0 0 3px rgba(210,163,61,.5)}}
/* 복수 선택 — 빈 곳을 끌면 나오는 사각형과, 선택된 장비 노드 표시 */
.db-marquee{position:absolute;z-index:5;border:1px solid var(--accent);border-radius:5px;background:rgba(23,107,84,.10);pointer-events:none}
.db-node.multi{border-color:#d2a33d;box-shadow:0 0 0 2px rgba(210,163,61,.28),0 8px 22px rgba(30,48,39,.16)}
.db-node.multi::after{content:"✓";position:absolute;right:-7px;top:-7px;width:16px;height:16px;border-radius:50%;background:#d2a33d;color:#fff;font-size:9px;font-weight:800;line-height:16px;text-align:center}
/* 선택 도구 바 — 캔버스 **좌측 상단**. 하단 중앙에 있을 때는 눈에 안 들어와 놓치기 쉬웠다.
   강조 테두리와 등장 애니메이션으로 "지금 선택됨" 을 확실히 알린다. */
/* top 은 상위 요소(.db-node-stage) 기준이라 툴바 높이(48px)를 더해야 캔버스 안쪽에 놓인다 */
.db-selection-bar{position:absolute;z-index:9;left:12px;top:60px;display:flex;align-items:center;gap:7px;padding:9px 12px;border:2px solid var(--accent);border-radius:5px;background:rgba(255,255,255,.99);box-shadow:0 14px 36px rgba(23,107,84,.28);white-space:nowrap;animation:dbSelectionIn 220ms ease-out}
@keyframes dbSelectionIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}
.db-selection-bar[hidden]{display:none}
.db-selection-bar b{padding:2px 8px;border-radius:5px;background:var(--accent);color:var(--accent-ink);font-size:11px}
.db-selection-bar label{color:var(--muted);font-size:9px}
.db-selection-bar select{height:26px;padding:0 6px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px}
.db-selection-bar button{height:26px;padding:0 10px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--muted);font-size:10px;font-weight:700;cursor:pointer}
.db-selection-bar button:hover{background:#edf5f1;color:var(--accent)}
.db-selection-bar button.p{border-color:var(--accent);background:var(--accent);color:var(--accent-ink)}
.db-selection-bar button.p:hover{background:#125c47}
.db-selection-bar button.db-sel-clear{border-style:dashed;color:var(--muted)}
.db-sel-sep{width:1px;height:18px;background:var(--line)}
.db-sel-hint{color:var(--muted);font-size:9px;max-width:230px;overflow:hidden;text-overflow:ellipsis}
body.utwin-themed .db-marquee{background:rgba(120,200,240,.14)}
body.utwin-themed .db-selection-bar{background:var(--panel)!important;border-color:var(--line)!important;color:var(--ink)}
body.utwin-themed .db-selection-bar select,body.utwin-themed .db-selection-bar button{background:var(--t-input)!important;color:var(--ink)!important;border-color:var(--line)!important}
body.utwin-themed .db-selection-bar button.p{background:var(--accent)!important;color:var(--accent-ink)!important}
/* ＋ 컴포넌트 팝오버 — 관제점·집계를 고르는 줄이 들어가 조금 넓다 */
.db-add-popover.wide{width:268px}
.db-add-row{display:flex;align-items:center;gap:7px;padding:5px 8px}
.db-add-row span{min-width:44px;color:var(--muted);font-size:9px;font-weight:700}
.db-add-row select{flex:1;height:26px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px}
body.utwin-themed .db-add-row select{background:var(--t-input)!important;color:var(--ink)!important;border-color:var(--line)!important}
.db-add-menu{position:relative}.db-add-popover{display:none;position:absolute;left:0;top:38px;width:230px;padding:6px;border:1px solid var(--line);border-radius:5px;background:var(--panel);box-shadow:0 18px 42px rgba(24,40,32,.2)}.db-add-popover.show{display:block}.db-node-toolbar .db-add-popover button{display:block;width:100%;height:auto;padding:9px 10px;border:0;text-align:left}.db-add-popover button:hover{background:#edf5f1}.db-add-popover b{display:block;font-size:10px}.db-add-popover small{display:block;margin-top:3px;color:var(--muted);font-size:8px;font-weight:500;line-height:1.45}
/* 점 패턴을 보드에 얹어 transform 이 같이 움직이게 한다 — 휠·팬 때 배경 스타일을 다시 쓰지 않는다 */
.db-viewport{position:absolute;left:0;right:0;top:48px;bottom:0;overflow:hidden;cursor:crosshair;background-color:var(--t-surface2)}.db-viewport.panning{cursor:grabbing}
.db-board{position:absolute;left:0;top:0;width:1400px;height:900px;transform-origin:0 0;background-image:radial-gradient(circle,#bdc8c1 1px,transparent 1.2px);background-size:20px 20px;will-change:transform}.db-edges{position:absolute;left:0;top:0;width:1400px;height:900px;overflow:visible;pointer-events:auto}.db-edge{fill:none;stroke:#86a69a;stroke-width:2;pointer-events:stroke;cursor:context-menu}.db-edge:hover{stroke-width:5;filter:drop-shadow(0 0 3px rgba(27,30,29,.4))}.db-edge.temperature{stroke:#e08042}.db-edge.humidity{stroke:#358fb8}.db-edge.status{stroke:#4ba57e}.db-edge.power{stroke:#b58b35}.db-edge.preview{stroke:#176b54;stroke-dasharray:6 5;pointer-events:none}.db-nodes{position:absolute;inset:0;pointer-events:none}.db-node{pointer-events:auto}
.db-node{position:absolute;width:220px;border:1px solid #cbd4cf;border-radius:5px;background:var(--panel);box-shadow:0 5px 16px rgba(30,48,39,.10);overflow:visible;user-select:none}.db-node.selected{border-color:var(--accent);box-shadow:0 0 0 2px rgba(23,107,84,.16),0 8px 22px rgba(30,48,39,.16)}.db-node-head{position:relative;height:42px;padding:8px 39px 8px 11px;border-radius:5px 5px 0 0;background:#e8f2ed;cursor:default}.db-node.source .db-node-head{background:#f6e7de;color:#9b4e2f}.db-node.equipment .db-node-head{background:#dfeee8;color:#176b54}.db-node-head b{display:block;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.db-node-head small{display:block;margin-top:2px;font-size:8px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.db-drag-handle{position:absolute;right:7px;top:7px;width:27px;height:27px;padding:0;border:1px solid rgba(50,80,66,.18);border-radius:5px;background:var(--t-input);color:var(--muted);font-size:14px;line-height:25px;text-align:center;cursor:grab}.db-drag-handle:hover{border-color:var(--accent);background:var(--panel);color:var(--accent)}.db-drag-handle:active{cursor:grabbing}.db-node-body{padding:7px 0}.db-port-row{position:relative;min-height:28px;display:flex;align-items:center;justify-content:space-between;padding:4px 12px;font-size:9px}.db-port-row span{color:var(--muted)}.db-port-row code{max-width:120px;color:#87948d;font:8px var(--font-ui);font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.db-port{position:absolute;top:50%;width:12px;height:12px;margin-top:-6px;border:2px solid #fff;border-radius:50%;box-shadow:0 0 0 1px #78958a;background:#55a181;cursor:crosshair;z-index:2}.db-port.out{right:-7px}.db-port.in{left:-7px}.db-port.temperature{background:#e08042}.db-port.humidity{background:#358fb8}.db-port.status{background:#4ba57e}.db-port.power{background:#b58b35}.db-floor-tag{position:absolute;left:-9px;top:-9px;padding:3px 6px;border-radius:5px;background:#203c32;color:#fff;font-size:8px;font-weight:900}
.db-node.aggregate{width:240px;border-color:#9ab8c5}.db-node.aggregate .db-node-head{background:#dfedf3;color:#245f78}.db-node.chart{width:250px;border-color:#b5a4c8}.db-node.chart .db-node-head{background:#eee7f5;color:#674c82}
/* ===== AI 플로우 스튜디오 =====
   캔버스·노드·간선은 자동제어 스튜디오(.db-*)를 그대로 재사용한다. 여기서는 그 스튜디오에
   없는 것(우측 속성 패널·팔레트 캡션·노드 종류 색)만 더한다. */
.ai-side{flex:0 0 260px;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--panel);min-width:0}
.ai-side-cap{padding:11px 13px;border-bottom:1px solid var(--line);color:var(--muted);font:800 10px/1 var(--font-ui);letter-spacing:.12em}
.ai-side-body{flex:1;padding:12px 13px;overflow:auto;font-size:12px;color:var(--ink);line-height:1.6}
/* ===== 좌측 AI 감시 시나리오 패널 =====
   시나리오를 팝업 select 로만 고르던 것을 늘 보이는 목록으로 옮겼다. 각 줄이 그 시나리오의
   감시 조건과 지금 상태(대기·감시 중·조건 충족·발동)를 함께 말한다. */
.ai-list{flex:none;width:250px;min-width:0;display:flex;flex-direction:column;border-right:1px solid var(--line);background:#fff}
.ai-list .ai-side-cap{display:flex;align-items:center;justify-content:space-between;gap:6px}
.ai-watch-btn{height:22px;padding:0 8px;border:1px solid var(--line);border-radius:5px;background:#fff;color:var(--muted);font-size:9px;font-weight:900;cursor:pointer}
.ai-watch-btn:hover{border-color:#176b54;color:#176b54}
.ai-watch-btn.on{border-color:#176b54;background:#176b54;color:#fff}
.ai-list-body{flex:1;min-height:0;overflow:auto;padding:6px}
.ai-sc-item{display:block;width:100%;margin-bottom:5px;padding:7px 8px;border:1px solid var(--line);border-radius:5px;background:#fff;text-align:left;cursor:pointer}
.ai-sc-item:hover{border-color:#176b54}
.ai-sc-item.on{border-color:#176b54;background:#f1f8f5}
.ai-sc-item b{display:block;font-size:11px;font-weight:900;line-height:1.4}
.ai-sc-item-type{display:block;margin-top:1px;color:#718078;font-size:8.5px;font-weight:800}
.ai-sc-item-watch{display:block;margin-top:3px;color:#526159;font-size:9px;line-height:1.45}
.ai-sc-badge{display:inline-block;margin-top:5px;padding:1px 6px;border-radius:999px;font-style:normal;font-size:8.5px;font-weight:900}
.ai-sc-badge.is-off{background:#eef1ee;color:#718078}
.ai-sc-badge.is-watch{background:#e8f2ed;color:#176b54}
.ai-sc-badge.is-fire{background:#fdecea;color:#b03a2c}
body.utwin-themed .ai-list{background:var(--panel)!important;border-right-color:var(--line)!important}
body.utwin-themed .ai-sc-item{background:var(--t-surface2)!important;color:var(--ink)!important;border-color:var(--line)!important}
body.utwin-themed .ai-sc-item.on{background:var(--t-chip-bg)!important}
body.utwin-themed .ai-sc-item-type,body.utwin-themed .ai-sc-item-watch{color:var(--muted)!important}
body.utwin-themed .ai-watch-btn{background:var(--t-surface2)!important;color:var(--ink)!important}
body.utwin-themed .ai-watch-btn.on{background:#176b54!important;color:#fff!important;border-color:#176b54!important}
/* 실행 기록 — 실행이 무엇을 했는지 최근 줄만. 속성 패널 아래에 붙는다. */
.ai-run-log{flex:none;max-height:220px;overflow:auto;margin:0;padding:6px 13px 10px;list-style:none;font-size:10.5px;line-height:1.7;color:var(--ink)}
/* 기록 줄의 머리글자(✔ ✖ · ■)에 색을 준다 — 무엇이 실행되고 무엇이 건너뛰어졌는지 눈으로 갈린다 */
.ai-run-log li{position:relative;padding-left:2px}
.ai-run-log li.ok{color:var(--ink)}
.ai-run-log li.ok::first-letter{color:#2f7d5b;font-weight:900}
.ai-run-log li.bad::first-letter{color:#d0342c;font-weight:900}
.ai-run-log li.step{color:var(--muted)}
.ai-run-log li.end{margin-top:3px;padding-top:4px;border-top:1px solid var(--line);color:var(--ink);font-weight:800}
.ai-run-log li{padding:1px 0;white-space:normal}
.ai-run-log:empty::before{content:"실행을 누르면 여기에 남습니다";color:var(--ink3);font-size:10px}
/* 실행 중인 노드 · 지나간 노드 · 확인 실패 노드 */
.db-node.ai-running{outline:2px solid var(--pop2,#f5c518);outline-offset:1px}
.db-node.ai-ran{opacity:.82}
.db-node.ai-failed{outline:2px solid var(--pop1,#f0442a);outline-offset:1px}
/* 실행하지 않고 넘어간 노드(명령이 없거나 장비로 보낼 수 없는 명령) — 실패와 구분한다 */
.db-node.ai-skipped{outline:2px dashed var(--muted);outline-offset:1px;opacity:.85}
.ai-side-body .ai-empty{color:var(--muted)}
.ai-side-body label{display:block;margin:0 0 9px}
.ai-side-body label span{display:block;margin-bottom:4px;color:var(--muted);font-size:10px;font-weight:800}
.ai-side-body input,.ai-side-body select,.ai-side-body textarea{width:100%;padding:6px 8px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font:inherit;font-size:12px}
.ai-side-body textarea{min-height:74px;resize:vertical}
.ai-palette-cap{flex:none;white-space:nowrap;color:var(--muted);font:800 10px/1 var(--font-ui);letter-spacing:.1em;margin-right:4px}
/* 노드 종류가 늘고 왼쪽에 시나리오 패널이 생기면서 버튼 글자가 '+ 트리 거' 처럼 쪼개졌다.
   버튼은 줄이지 않고, 자리가 모자라면 팔레트를 가로로 밀어 보게 한다. */
#aiPalette{flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden}
#aiPalette button,#aiPalette select{flex:none;white-space:nowrap}
/* 노드 종류별 색 — 트리거·조건·동작·대기·확인·알림·SOP. db-node 기본 스타일 위에 머리색만 얹는다.
   색은 무채색 팔레트와 충돌하지 않게 낮은 채도로 둔다. */
/* 장비 노드는 3D 의 실제 장비를 가리키므로 다른 색으로 구분한다 */
.db-node.ai-feed      .db-node-head{background:#e2eef9;color:#2a5b86}
.db-node.ai-group     .db-node-head{background:#e9eef1;color:#3f5a68}
.db-node.ai-device    .db-node-head{background:#dde8f5;color:#2b4f78}
.db-node.ai-trigger   .db-node-head{background:#e3f1e9;color:#2f6b4c}
.db-node.ai-condition .db-node-head{background:#dfedf3;color:#245f78}
.db-node.ai-action    .db-node-head{background:#fbeadb;color:#8a5b2f}
.db-node.ai-wait      .db-node-head{background:#ecefed;color:#5d6560}
.db-node.ai-check     .db-node-head{background:#eee7f5;color:#674c82}
.db-node.ai-notify    .db-node-head{background:#fbe1de;color:#8c3f36}
.db-node.ai-chart     .db-node-head{background:#e7e2f7;color:#4b3f86}
.db-node.ai-sop       .db-node-head{background:#f6efd8;color:#7a6320}

/* ===== 모눈(격자) — 오른쪽·위·아래로 끝이 없다 =====
   자동제어 스튜디오는 격자를 .db-board(1400×900 고정)에 그려서 판의 끝이 눈에 보인다.
   AI 스튜디오는 격자를 뷰포트가 그리고 배경 위치를 팬 값만큼 밀어 무한 격자로 만든다 —
   판을 아무리 밀어도 격자가 끊기지 않는다. 확대·축소하면 격자 간격도 같은 배율로 따라간다.
   왼쪽만 경계를 둔다: 보드 x<0 영역(= 팬 값만큼의 폭)을 격자 없이 덮고 그 오른쪽에 선을 그린다.
   시작점이 어디인지 보이게 하려는 것이고, 나머지 세 방향은 열어 둔다. */
#aiOverlay .db-viewport{
  background-color:#f7f8f7;
  background-image:radial-gradient(circle,#bdc8c1 1px,transparent 1.2px);
  background-size:calc(20px * var(--ai-zoom,1)) calc(20px * var(--ai-zoom,1));
  background-position:var(--ai-pan-x,0px) var(--ai-pan-y,0px);
}
#aiOverlay .db-board{background-image:none}          /* 격자는 뷰포트가 그린다 */
#aiOverlay .db-viewport::before{
  content:"";position:absolute;left:0;top:0;bottom:0;z-index:0;pointer-events:none;
  width:max(0px,var(--ai-pan-x,0px));
  background:#eef1ee;border-right:1px solid #c3ccc7;
}
body.utwin-themed #aiOverlay .db-viewport{background-color:var(--bg);background-image:radial-gradient(circle,rgba(236,238,237,.26) 1px,transparent 1.2px)}
body.utwin-themed #aiOverlay .db-viewport::before{background:rgba(236,238,237,.05);border-right-color:var(--line)}

/* AI 스튜디오는 캔버스 + 우측 패널 두 칸이다.
   자동제어 스튜디오는 node-mode 에서 .db-body 를 display:block(캔버스 한 칸)으로 두므로,
   그 규칙을 건드리지 않고 #aiOverlay 안에서만 가로 배치로 덮는다. */
#aiOverlay .db-shell.node-mode .db-body{display:flex;flex-direction:row;gap:0}
#aiOverlay .db-node-stage{flex:1 1 auto;min-width:0}
#aiOverlay .db-viewport{top:48px}

/* 노드 이동 손잡이 — 머리 오른쪽. 평소엔 흐리고 노드에 올리면 진해진다.
   몸통을 끌어도 옮겨지지만, 옮길 수 있다는 사실이 보이지 않는다는 지적을 받아 표시를 붙였다. */
#aiOverlay .db-node-head{position:relative;padding-right:26px}
#aiOverlay .ai-grab{position:absolute;right:6px;top:50%;transform:translateY(-50%);
  display:inline-flex;width:14px;height:14px;opacity:.35;cursor:move}
#aiOverlay .db-node:hover .ai-grab{opacity:.75}
#aiOverlay .db-node.selected .ai-grab{opacity:.9}
#aiOverlay .ai-grab svg{width:14px;height:14px}

/* 시나리오 정보 패널 */
.ai-sc-type{display:inline-block;padding:3px 8px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:10px;font-weight:800}
.ai-sc h3{margin:8px 0 6px;font-size:14px}
.ai-sc-comment{margin:0 0 10px;padding:9px 10px;border-radius:6px;background:var(--t-surface2);font-size:12px;line-height:1.55}
.ai-sc dl{margin:0 0 10px}
.ai-sc dt{margin-top:9px;color:var(--muted);font-size:10px;font-weight:800}
.ai-sc dd{margin:2px 0 0;font-size:12px;line-height:1.55}
.ai-sc dd ul,.ai-sc-check{margin:2px 0 0;padding-left:16px}
.ai-sc-caution{color:#a2542c}
.ai-sc-hint{color:var(--muted)}
.ai-sc-cap{margin:12px 0 5px;color:var(--muted);font-size:10px;font-weight:800}
.ai-sc-cap em{font-style:normal;font-weight:600}
/* 시나리오를 차트로 만들어 메인에 올리는 버튼 — 패널 맨 아래에서 폭을 꽉 채운다 */
.ai-sc-publish{width:100%;margin-top:7px}
.ai-sc p.ai-sc-hint{margin:0 0 2px;line-height:1.5}
.ai-sc-table{width:100%;border-collapse:collapse;font-size:11px}
.ai-sc-table th{width:46%;padding:5px 6px;border-bottom:1px solid var(--line);color:var(--muted);font-weight:700;text-align:left;vertical-align:top}
.ai-sc-table td{padding:5px 6px;border-bottom:1px solid var(--line);font-weight:800}
.ai-sc-conf{margin-top:11px;font-size:11px}
.ai-sc-bar{display:block;height:6px;margin-top:4px;border-radius:99px;background:var(--surface2);overflow:hidden}
.ai-sc-bar i{display:block;height:100%;background:var(--accent)}
body.utwin-themed .ai-side{background:var(--panel)!important;border-color:var(--line)!important}
/* 자동 생성 폴더 — 접으면 목록 요약만 보이고 펼치면 내용이 캔버스에 배치된다 */
.db-node.folder{width:270px;border-style:dashed;border-color:#b6a06a}
.db-node.folder .db-node-head{background:#fbf3dd;color:#7a5a12}
.db-node.folder.collapsed{box-shadow:0 5px 16px rgba(30,48,39,.10),6px 6px 0 -2px rgba(182,160,106,.35)}
.db-folder-list{display:grid;gap:3px;padding:8px 10px 2px}
.db-folder-list>div{display:flex;align-items:center;gap:6px;color:#5c6b62;font-size:9px;white-space:nowrap;overflow:hidden}
.db-folder-list>div b{overflow:hidden;color:#33413a;font-weight:700;text-overflow:ellipsis}
.db-folder-list .more{color:#8a9a91;font-size:8px}
.db-folder-toggle{margin:6px 10px 10px;padding:5px 8px;border:1px solid #d9cba5;border-radius:5px;background:var(--t-surface2);color:#7a5a12;font-size:9px;font-weight:800;cursor:pointer}
.db-folder-toggle:hover{background:#fdf6e4}.db-node-stat{display:grid;grid-template-columns:1fr 1fr;gap:5px;padding:8px 10px}.db-node-stat span{padding:7px;border-radius:5px;background:#f5f7f6;color:var(--muted);font-size:8px}.db-node-stat b{display:block;margin-top:2px;color:#176b54;font-size:12px}.db-flow-chip{display:inline-flex;align-items:center;gap:4px;margin:6px 10px 9px;padding:4px 7px;border-radius:5px;background:#f1f4f2;color:var(--muted);font-size:8px;font-weight:800}
.db-inspector{position:absolute;z-index:7;right:12px;top:60px;width:330px;max-height:calc(100% - 72px);overflow:auto;padding:13px;border:1px solid var(--line);border-radius:5px;background:var(--t-glass2);box-shadow:0 16px 36px rgba(24,40,32,.16);backdrop-filter:blur(12px)}.db-inspector h3{margin:0 0 3px;color:var(--accent);font-size:12px}.db-inspector .db-help{margin-bottom:8px}.db-node-edit{display:flex;gap:5px;margin:8px 0}.db-node-edit input{min-width:0;flex:1;height:31px;padding:0 8px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--ink);font-size:10px}.db-node-edit button{height:31px;padding:0 9px;border:1px solid var(--accent);border-radius:5px;background:var(--panel);color:var(--accent);font-size:9px;font-weight:800;cursor:pointer}.db-inspector .db-chart-wrap{height:190px}.db-inspector .db-latest{grid-template-columns:repeat(2,1fr)}.db-inspector-close{float:right;border:0;background:transparent;color:var(--muted);cursor:pointer}.db-node-hint{position:absolute;z-index:5;left:14px;bottom:12px;padding:7px 10px;border-radius:5px;background:rgba(31,49,40,.88);color:#f4f7f5;font-size:9px;pointer-events:none}
.db-chart-edit{width:100%;height:31px;margin:0 0 8px;border:1px solid #b5a4c8;border-radius:5px;background:var(--t-surface2);color:var(--ink);font-size:9px;font-weight:900;cursor:pointer}.db-chart-edit:hover{background:var(--t-chip-bg)}.db-chart-legend{display:flex;align-items:center;gap:12px;min-height:18px;color:var(--ink3);font-size:8px}.db-chart-legend i{display:inline-block;width:16px;height:3px;margin-right:4px;border-radius:5px;vertical-align:middle}.db-chart-legend .current{background:var(--studio-series)}.db-chart-legend .previous{background:var(--studio-compare)}
.db-chart-builder{display:none;position:absolute;z-index:20;inset:0;align-items:center;justify-content:center;padding:24px;background:rgba(18,30,24,.38);backdrop-filter:blur(3px)}.db-chart-builder.show{display:flex}.db-chart-builder-box{width:min(760px,92vw);max-height:88vh;overflow:auto;padding:19px;border:1px solid #d2dbd6;border-radius:5px;background:var(--panel);box-shadow:0 28px 70px rgba(18,30,24,.3)}.db-builder-head{display:flex;align-items:flex-start;gap:12px}.db-builder-head>div{flex:1}.db-builder-head h3{margin:0;color:var(--ink);font-size:16px}.db-builder-head p{margin:5px 0 0;color:var(--ink3);font-size:10px}.db-builder-head button{width:31px;height:31px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink3);font-size:18px;cursor:pointer}.db-builder-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:17px}.db-builder-grid label{display:block}.db-builder-grid label.wide{grid-column:1/-1}.db-builder-grid span{display:block;margin-bottom:5px;color:var(--muted);font-size:9px;font-weight:900}.db-builder-grid input,.db-builder-grid select{width:100%;height:35px;padding:0 9px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--ink);font-size:10px}.db-builder-section{margin-top:15px;padding:12px;border:1px solid var(--line);border-radius:5px;background:var(--t-surface2)}.db-builder-section-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:9px;color:var(--muted);font-size:10px}.db-builder-section-head span{color:var(--accent);font-weight:900}.db-builder-devices{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;max-height:230px;overflow:auto}.db-builder-device{display:flex;align-items:center;gap:7px;padding:8px;border:1px solid #d6ded9;border-radius:5px;background:var(--panel);cursor:pointer}.db-builder-device:hover{border-color:#77a896}.db-builder-device input{width:14px;height:14px;accent-color:var(--accent)}.db-builder-device b{display:block;font-size:9px}.db-builder-device small{display:block;margin-top:2px;color:var(--muted);font-size:8px}.db-builder-device.disabled{opacity:.42;pointer-events:none}.db-builder-actions{display:flex;justify-content:flex-end;gap:7px;margin-top:15px}.db-builder-actions button{height:35px;padding:0 15px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px;font-weight:900;cursor:pointer}.db-builder-actions button.primary{border-color:var(--accent);background:var(--accent);color:var(--accent-ink)}@media(max-width:800px){.db-builder-grid,.db-builder-devices{grid-template-columns:1fr 1fr}}
/* 판 색은 테마를 따른다 — 흰색으로 박아 두면 다크에서 글자(--ink)가 묻힌다.
   항목 색(초록 hover·붉은 삭제)은 스튜디오 색이라 그대로 둔다. */
.db-context{display:none;position:absolute;z-index:12;width:132px;padding:5px;border:1px solid var(--line);border-radius:5px;background:var(--panel);box-shadow:0 14px 32px rgba(24,40,32,.22)}.db-context.show{display:block}.db-context button{display:block;width:100%;padding:8px 10px;border:0;border-radius:5px;background:transparent;color:var(--ink);text-align:left;font-size:10px;font-weight:800;cursor:pointer}.db-context button:hover{background:#e8f2ed;color:#176b54}.db-context button.danger{color:var(--bad-ink)}.db-context button.danger:hover{background:var(--bad-bg)}.db-context button:disabled{color:var(--ink3);cursor:not-allowed;background:transparent}
.db-confirm{display:none;position:absolute;z-index:15;inset:0;align-items:center;justify-content:center;background:rgba(18,30,24,.28);backdrop-filter:blur(2px)}.db-confirm.show{display:flex}.db-confirm-box{width:340px;padding:18px;border:1px solid #d7ddd9;border-radius:5px;background:var(--panel);box-shadow:0 22px 50px rgba(18,30,24,.25)}.db-confirm-box h4{margin:0 0 7px;color:#253a31;font-size:14px}.db-confirm-box p{margin:0;color:#64736b;font-size:10px;line-height:1.6}.db-confirm-link{margin:11px 0;padding:10px;border-radius:5px;background:#f3f6f4;color:var(--ink);font-size:10px;font-weight:800;word-break:keep-all}.db-confirm-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:14px}.db-confirm-actions button{height:33px;padding:0 13px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);font-size:10px;font-weight:800;cursor:pointer}.db-confirm-actions .danger{border-color:#ba5146;background:#ba5146;color:#fff}.db-confirm-actions .danger:hover{background:#9f3e35}
@keyframes dtFloat{50%{transform:translateY(-6px)}}
@keyframes dtGaugePulse{0%,100%{filter:drop-shadow(0 0 5px rgba(40,180,170,.35))}50%{filter:drop-shadow(0 0 12px rgba(40,180,170,.7))}}
.db-chart-builder-box{width:min(900px,94vw);max-height:90vh}
.db-template-section{margin-top:16px;padding:12px;border:1px solid var(--line);border-radius:5px;background:var(--t-surface2)}
.db-template-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px}
.db-template-card{min-width:0;padding:9px 8px 8px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);text-align:left;cursor:pointer;transition:border-color .16s,box-shadow .16s,transform .16s}
.db-template-card:hover{transform:translateY(-1px);border-color:#77a896;box-shadow:0 5px 14px rgba(23,107,84,.10)}
.db-template-card.selected{border-color:var(--accent);box-shadow:0 0 0 2px rgba(23,107,84,.13);background:var(--t-chip-bg)}
.db-template-preview{display:block;width:100%;height:60px;margin-bottom:7px;border-radius:5px;background:var(--t-surface2)}
.db-template-card b,.db-template-card small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.db-template-card b{font-size:11px}.db-template-card small{margin-top:2px;color:var(--muted);font-size:9px}
@media(max-width:900px){.db-template-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}
.db-aggregate-edit{display:grid;grid-template-columns:1fr auto;gap:6px;margin:8px 0}
.db-aggregate-edit select,.db-aggregate-edit button{height:32px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);color:var(--muted);font-size:9px;font-weight:800}
.db-aggregate-edit button{padding:0 10px;border-color:var(--accent);color:var(--accent);cursor:pointer}
.db-chart-builder.chart-only .db-builder-target,.db-chart-builder.chart-only .db-builder-devices-section{display:none}
.db-chart-builder [hidden],.db-aggregate-edit[hidden]{display:none!important}

/* ===== SVG 아이콘 (이모지 대체) =====
   아이콘은 ui/icons.js 가 [data-icon] 자리에 채운다. 색은 currentColor 를 따라오므로
   테마가 바뀌면 함께 바뀐다. 여기서는 자리·크기만 잡는다. */
[data-icon]>svg{display:block}
.top-command span[data-icon]>svg,.top-env-icon[data-icon]>svg{width:13px;height:13px}
.top-env-item i[data-icon]{min-width:16px}
.top-env-item i[data-icon]>svg{width:12px;height:12px}
/* 설정 센터 탭 — 아이콘 위, 라벨 아래 */
.control-menu .tool-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px}
.control-menu .tool-btn span[data-icon]>svg{width:15px;height:15px}
/* 아이콘 + 글자가 함께 있는 일반 버튼 */
.vbtns button span[data-icon],.pk-btn span[data-icon],.btn span[data-icon]{display:inline-flex;vertical-align:-2px;margin-right:5px}
.vbtns button:has(span[data-icon]){display:inline-flex;align-items:center;justify-content:center;gap:2px}

/* ===== 도움말 (ui/help.js) =====
   전체를 덮는 문서 페이지다. 스튜디오 오버레이(.db-overlay)와 같은 방식·같은 토큰을 쓴다 —
   테마를 따라가고, 덧칠 규칙을 만들지 않는다. */
/* ★ 색을 새로 정하지 않고 옆 버튼(.pk-btn)의 색을 그대로 쓴다.
   시작 화면(.loading)은 그 안에서 토큰을 다시 정의하는 자리다(--ink·--panel·--line …).
   처음에 var(--t-surface2)·var(--ink2) 를 썼다가 **글자가 안 보였다** — --ink2 는 아예 없는
   토큰이라 색이 무효가 되고, 물려받은 어두운 글자색이 어두운 바탕에 얹혔다(지적 받음). */
.pk-help{flex:0 0 auto;align-self:center;margin-top:10px;padding:7px 16px;font-size:11px;font-weight:800}
/* ===== 안내 투어 =====
   비추는 방법 — 대상 자리에 투명한 상자(.tour-spot)를 놓고 아주 큰 box-shadow 로 나머지를 덮는다.
   구멍 뚫린 막(clip-path)보다 흔들림이 적고, 상자만 옮기면 되니 계산이 한 곳이다.
   z-index 는 시작 화면(20)보다 위, 도움말 페이지(80)보다 아래에 둔다 — 투어에서 '자세히' 를
   누르면 도움말이 그 위에 떠야 한다. */
.tour-overlay{position:fixed;inset:0;z-index:75;display:none}
.tour-overlay.show{display:block}
.tour-spot{position:absolute;border-radius:8px;border:2px solid var(--accent);
  box-shadow:0 0 0 9999px rgba(9,28,38,.60);transition:left .18s,top .18s,width .18s,height .18s;pointer-events:none}
/* ★ 클릭 걸음 — 어둠막이 클릭을 **통과시킨다**(사용자가 그 버튼을 실제로 눌러야 한다).
   카드만 클릭을 받고, 비추는 테두리는 맥동해 '여기를 누르라' 는 뜻을 준다. */
.tour-overlay.pass{pointer-events:none}
.tour-overlay.pass .tour-card{pointer-events:auto}
.tour-overlay.pass .tour-spot{animation:tourPulse 1.5s ease-in-out infinite;
  box-shadow:0 0 0 9999px rgba(9,28,38,.44),0 0 0 2px var(--accent)}
@keyframes tourPulse{0%,100%{border-color:var(--accent)}50%{border-color:rgba(255,255,255,.85)}}
.tour-card.wait .tour-text::after{content:" ← 이 버튼을 눌러 주세요";color:var(--accent);font-weight:800}
.tour-card{position:absolute;width:min(340px,86vw);padding:13px 14px;border-radius:8px;
  background:var(--panel,#fff);color:var(--ink,#1b1e1d);border:1px solid var(--accent);
  box-shadow:0 18px 44px rgba(6,20,28,.38),0 0 0 4px rgba(9,28,38,.18);font-family:var(--font-ui)}
.tour-card-h{display:flex;align-items:center;gap:8px;margin-bottom:6px}
.tour-card-h b{font-size:13px}
.tour-step{margin-left:auto;color:var(--muted);font-size:10px;font-variant-numeric:tabular-nums}
.tour-text{margin:0 0 11px;color:var(--ink2,var(--ink));font-size:11.5px;line-height:1.62}
.tour-btns{display:flex;align-items:center;gap:6px}
.tour-btns .tour-gap{flex:1}
.tour-btns button{padding:6px 10px;border:1px solid var(--line);border-radius:5px;background:var(--t-input);
  color:var(--ink);font-family:var(--font-ui);font-size:11px;font-weight:700;cursor:pointer}
.tour-btns button:hover{border-color:var(--accent)}
.tour-btns button:disabled{opacity:.4;cursor:default}
.tour-btns button.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}
.tour-btns .tour-skip{color:var(--muted);border-color:transparent;font-weight:600}
body.utwin-dark .tour-card{background:#141a18;color:#e8efe9;border-color:rgba(255,255,255,.14)}
body.utwin-dark .tour-btns button{background:var(--t-input);color:#e8efe9;border-color:rgba(255,255,255,.16)}
body.utwin-dark .tour-btns button.primary{background:var(--accent);color:var(--accent-ink)}
/* 시작 화면의 도움말·안내 투어 버튼 두 개를 한 줄에 */
.pk-help-row{display:flex;gap:8px;margin-top:10px}
.pk-help-row .pk-help{margin-top:0}

.help-overlay{position:fixed;inset:0;z-index:80;display:none;padding:24px;
  background:rgba(11,20,16,.62);backdrop-filter:blur(7px)}
.help-overlay.show{display:grid;place-items:center}
.help-shell{display:flex;flex-direction:column;width:min(980px,96vw);height:min(76vh,760px);
  border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);
  box-shadow:var(--shadow);overflow:hidden}
.help-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;
  padding:14px 16px;border-bottom:1px solid var(--line)}
.help-head h2{margin:0;font-size:15px;font-weight:900;letter-spacing:.02em}
.help-head p{margin:3px 0 0;color:var(--muted);font-size:11px;line-height:1.5;max-width:56ch}
.help-head-tools{display:flex;align-items:center;gap:8px;flex:0 0 auto}
.help-head-tools input{width:210px;padding:6px 9px;border:1px solid var(--line);border-radius:5px;
  background:var(--t-input);color:var(--ink);font:800 11px/1.2 var(--font-ui)}
.help-head-tools input:focus{outline:0;border-color:var(--accent)}
.help-head-tools button{width:28px;height:28px;border:1px solid var(--line);border-radius:5px;
  background:var(--t-surface2);color:var(--muted);font-size:16px;line-height:1;cursor:pointer}
.help-head-tools button:hover{border-color:var(--accent);color:var(--accent)}
.help-body{display:grid;grid-template-columns:232px minmax(0,1fr);flex:1 1 auto;min-height:0}
.help-list{display:flex;flex-direction:column;gap:4px;padding:12px;overflow-y:auto;
  border-right:1px solid var(--line);background:var(--t-sec-bg)}
.help-list button{display:block;width:100%;padding:8px 10px;border:1px solid transparent;border-radius:5px;
  background:none;color:var(--ink);text-align:left;cursor:pointer}
.help-list button b{display:block;font-size:12px;font-weight:800}
.help-list button small{display:block;margin-top:2px;color:var(--muted);font-size:10px;line-height:1.4}
.help-list button:hover{border-color:var(--line)}
.help-list button.on{background:var(--t-surface2);border-color:var(--accent)}
.help-empty{margin:6px 4px;color:var(--muted);font-size:11px}
.help-doc{padding:16px 18px;overflow-y:auto}
.help-doc h3{margin:0 0 10px;font-size:14px;font-weight:900;color:var(--accent)}
.help-doc p{margin:0 0 10px;font-size:12px;line-height:1.7;color:var(--ink)}
.help-table{width:100%;margin:0 0 12px;border-collapse:collapse;font-size:12px}
.help-table th{width:170px;padding:7px 10px 7px 0;border-top:1px solid var(--line);
  color:var(--ink);font-weight:800;text-align:left;vertical-align:top}
.help-table td{padding:7px 0;border-top:1px solid var(--line);color:var(--ink);line-height:1.6}
/* 값·파일 이름·열쇠는 글자와 구분한다 — 도움말이 기술 문서라 이 구분이 읽기를 좌우한다 */
.help-doc code,.help-table code{padding:0 4px;border:1px solid var(--line);border-radius:3px;
  background:var(--t-surface2);font-family:var(--font-ui);font-variant-numeric:tabular-nums;font-size:11px;
  white-space:nowrap}
.help-doc b,.help-table b{font-weight:800}
@media(max-width:760px){.help-body{grid-template-columns:1fr}.help-list{border-right:0;border-bottom:1px solid var(--line);max-height:32vh}}

/* ===== AI 스튜디오 — 카드·간선·목록·문서 (디자인 개선 요청) =====
   고친 까닭: 노드가 '제목 + 검은 상자' 뿐이어서 종류·순서·상태가 읽히지 않았고, 시나리오 목록은
   아홉 개가 한 줄로 늘어서 있었고, 속성 문서는 모두 같은 크기 글씨라 위계가 없었다. */
.ai-card{min-width:186px;max-width:250px;border-radius:7px;
  box-shadow:0 6px 18px rgba(20,30,26,.10);transition:box-shadow .16s,transform .16s}
.ai-card:hover{box-shadow:0 12px 26px rgba(20,30,26,.18);transform:translateY(-1px)}
.ai-card .db-node-head{display:flex;align-items:center;gap:6px;padding:6px 8px;font-size:9.5px;
  font-weight:900;letter-spacing:.04em;border-radius:6px 6px 0 0}
.ai-card .ai-kind-icon{display:grid;place-items:center;width:15px;height:15px;opacity:.9}
.ai-card .ai-kind-icon svg{width:12px;height:12px}
.ai-card .ai-kind-name{flex:1}
.ai-card .db-node-body{padding:8px 9px;font-size:11px;line-height:1.5}
/* 종류는 머리 배경색으로만 보인다 — 상자 앞 세로선은 쓰지 않는다(지적) */
.ai-card .ai-kind-icon{color:inherit}
/* 실행 상태 — 머리 오른쪽에 점 하나 */
.ai-card.ai-running .db-node-head::after,.ai-card.ai-ran .db-node-head::after,
.ai-card.ai-failed .db-node-head::after,.ai-card.ai-skipped .db-node-head::after{
  content:"";width:7px;height:7px;border-radius:50%;flex:0 0 auto}
.ai-card.ai-running .db-node-head::after{background:#d2a33d;animation:aiPulse 900ms infinite}
.ai-card.ai-ran .db-node-head::after{background:#2f7d5b}
.ai-card.ai-failed .db-node-head::after{background:#d0342c}
.ai-card.ai-skipped .db-node-head::after{background:#9aa8a1}
@keyframes aiPulse{0%,100%{opacity:1}50%{opacity:.25}}
/* 간선 — 화살표와 흐르는 점선 */
.ai-edge{stroke:#9aa8a1;stroke-width:2;color:#9aa8a1}
.ai-edge:hover{stroke:#176b54;color:#176b54;stroke-width:3}
.ai-edges-run .ai-edge{stroke:#176b54;color:#176b54;stroke-dasharray:7 6;animation:aiFlow 900ms linear infinite}
@keyframes aiFlow{to{stroke-dashoffset:-13}}
/* 시나리오 목록 — 성격별 묶음 */
.ai-sc-group{margin-bottom:11px}
.ai-sc-group-cap{display:flex;align-items:center;gap:5px;margin:0 2px 5px;color:var(--muted);
  font-size:8.5px;font-weight:900;letter-spacing:.1em}
.ai-sc-group-cap::after{content:"";flex:1;height:1px;background:var(--line)}
.ai-sc-group-cap em{font-style:normal;padding:0 5px;border-radius:999px;background:var(--t-surface2);
  color:var(--muted);font-size:8px}
/* 선택은 배경색으로만 나타낸다(지적) */
.ai-sc-item.on{background:color-mix(in srgb,#176b54 14%,transparent);border-color:#176b54}
.ai-sc-group[data-group="재난 대응"] .ai-sc-item.on{background:color-mix(in srgb,#b03a2c 15%,transparent);border-color:#b03a2c}
body.utwin-dark .ai-sc-item.on{background:color-mix(in srgb,#4ba57e 20%,transparent)}
body.utwin-dark .ai-sc-group[data-group="재난 대응"] .ai-sc-item.on{background:color-mix(in srgb,#d0564a 22%,transparent)}
/* 속성 문서 — 위계 */
/* 성격은 종류 딱지 배경색으로 보인다 — 문서 옆 세로선은 쓰지 않는다(지적) */
.ai-sc-doc[data-tone="fire"] .ai-sc-type{background:color-mix(in srgb,#b03a2c 16%,transparent);color:#b03a2c}
.ai-sc-doc[data-tone="energy"] .ai-sc-type{background:color-mix(in srgb,#c9a227 20%,transparent);color:#8a6d10}
.ai-sc-doc[data-tone="comfort"] .ai-sc-type{background:color-mix(in srgb,#358fb8 16%,transparent);color:#245f78}
.ai-sc-situation{margin:4px 0 10px;font-size:13px;font-weight:700;line-height:1.55;color:var(--ink)}
.ai-sc-label{margin:10px 0 4px;color:var(--muted);font-size:8.5px;font-weight:900;letter-spacing:.1em}
.ai-sc-evidence{margin:0 0 10px;padding-left:15px;font-size:11px;line-height:1.6;color:var(--ink)}
.ai-sc-recommend{margin:0 0 10px;padding:10px 11px;border:0;border-radius:6px;
  background:color-mix(in srgb,#176b54 12%,transparent)}
.ai-sc-doc[data-tone="fire"] .ai-sc-recommend{background:color-mix(in srgb,#b03a2c 12%,transparent)}
.ai-sc-doc[data-tone="energy"] .ai-sc-recommend{background:color-mix(in srgb,#c9a227 16%,transparent)}
.ai-sc-doc[data-tone="comfort"] .ai-sc-recommend{background:color-mix(in srgb,#358fb8 12%,transparent)}
body.utwin-dark .ai-sc-recommend{background:color-mix(in srgb,#4ba57e 16%,transparent)}
body.utwin-dark .ai-sc-doc[data-tone="fire"] .ai-sc-recommend{background:color-mix(in srgb,#d0564a 18%,transparent)}
.ai-sc-recommend span{display:block;margin-bottom:3px;color:var(--muted);font-size:8.5px;font-weight:900;letter-spacing:.1em}
.ai-sc-recommend b{display:block;font-size:12px;line-height:1.55}
.ai-sc-recommend em{display:block;margin-top:5px;color:var(--muted);font-style:normal;font-size:10px;font-weight:700}
.ai-sc-caution{position:relative;margin:0 0 10px;padding:8px 10px 8px 26px;border-radius:5px;
  background:#fdecea;color:#8f2c20;font-size:10.5px;line-height:1.6}
.ai-sc-caution::before{content:"!";position:absolute;left:9px;top:8px;width:12px;height:12px;border-radius:50%;
  background:#b03a2c;color:#fff;font-size:9px;font-weight:900;line-height:12px;text-align:center}
body.utwin-dark .ai-sc-caution{background:#3a2320;color:#f0b7ae}
/* ===== 분석 플로우 — 줌이 낮을 때 단순화 ===== */
.db-board.far .db-node .db-node-body,.db-board.far .db-node .db-node-stat,.db-board.far .db-node .db-port{display:none}
.db-board.far .db-node{min-height:0}
.db-board.far .db-node .db-node-head{font-size:calc(10px / var(--db-zoom,1));line-height:1.25;
  padding:calc(4px / var(--db-zoom,1)) calc(6px / var(--db-zoom,1));white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.db-board.far .db-edge{stroke-width:3}
.ai-side-cap .ai-cap-note{font-style:normal;font-weight:600;letter-spacing:0;color:var(--ink3);font-size:8.5px}

/* ===== 온톨로지 모드 (AI 스튜디오 안) =====
   중국지사 uStudio 의 '온톨로지 관리' 를 우리 개념으로 옮긴 목업이다. 스튜디오 껍데기를 그대로
   쓰고 본문만 갈아 끼운다 — 흐름 모드의 세 칸(목록·캔버스·속성)을 감추고 이 세 칸을 편다. */
/* ===== 모드 탭 — 머리 아래 한 줄(지적: 크고 잘 보이게) =====
   작은 세그먼트 버튼으로 머리 오른쪽에 끼워 두었더니 눈에 띄지 않았다. 시뮬레이션 창의 탭과
   같은 방식으로 왼쪽 위에 크게 둔다 — 지금 어느 모드인지 먼저 읽힌다. */
.ai-tabs{display:flex;align-items:stretch;gap:2px;padding:0 14px;background:var(--panel);
  border-bottom:1px solid var(--line)}
.ai-tabs button{position:relative;height:44px;padding:0 20px;border:0;background:transparent;
  color:var(--muted);font-size:14px;font-weight:800;cursor:pointer}
.ai-tabs button:hover{color:var(--ink)}
.ai-tabs button.on{color:var(--accent)}
.ai-tabs button.on::after{content:"";position:absolute;left:12px;right:12px;bottom:0;height:3px;
  border-radius:3px 3px 0 0;background:var(--accent)}
.ai-tabs .ai-tabs-note{margin-left:auto;align-self:center;color:var(--muted);font-size:10px}
.onto-wrap{display:none;min-height:0;grid-column:1 / -1;grid-template-columns:190px minmax(0,1fr) minmax(300px,360px);
  height:100%}
.db-shell.onto-mode .ai-list,.db-shell.onto-mode .db-node-stage,.db-shell.onto-mode .ai-side{display:none}
.db-shell.onto-mode .onto-wrap{display:grid}
.db-shell.onto-mode .db-body{display:grid;grid-template-columns:minmax(0,1fr)}
.onto-rail{display:flex;flex-direction:column;gap:2px;padding-bottom:10px;border-right:1px solid var(--line);
  background:var(--panel);overflow:auto}
.onto-rail-item{margin:0 8px;padding:8px 10px;border:1px solid transparent;border-radius:5px;background:transparent;
  color:var(--ink);font-size:11px;font-weight:700;text-align:left;cursor:pointer}
.onto-rail-item:hover{border-color:var(--line)}
.onto-rail-item.on{background:var(--t-chip-bg);border-color:var(--accent);color:var(--accent);font-weight:900}
.onto-mock{margin:10px 8px 0;padding:8px 9px;border:1px dashed var(--line);border-radius:5px;
  color:var(--muted);font-size:9.5px;line-height:1.6}
.onto-main{min-width:0;display:flex;flex-direction:column;background:var(--bg)}
.onto-main-head{padding:11px 14px;border-bottom:1px solid var(--line);background:var(--panel)}
.onto-main-head b{font-size:13px}
.onto-main-head span{display:block;margin-top:3px;color:var(--muted);font-size:10px;line-height:1.6}
.onto-main-body{flex:1;min-height:0;overflow:auto;padding:14px}
/* 클래스 카드 */
.onto-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(178px,1fr));gap:10px}
.onto-card{--k:var(--muted);position:relative;padding:11px 12px 12px;border:1px solid var(--line);
  border-radius:6px;background:color-mix(in srgb,var(--k) 7%,var(--panel));text-align:left;cursor:pointer}
.onto-card:hover{border-color:var(--accent)}
.onto-card.on{border-color:var(--k);background:color-mix(in srgb,var(--k) 20%,var(--panel))}
.onto-card-cat{display:inline-block;padding:1px 6px;border-radius:999px;background:color-mix(in srgb,var(--k) 16%,transparent);
  color:var(--k);font-size:8px;font-weight:900}
.onto-card b{display:block;margin-top:6px;font-size:13px;font-weight:900}
.onto-card code{display:block;margin-top:2px;color:var(--muted);font-size:9.5px}
.onto-card-count{position:absolute;right:11px;bottom:10px;color:var(--ink);font-size:15px;font-weight:900;
  font-variant-numeric:tabular-nums}
.onto-card-count em{margin-left:2px;color:var(--muted);font-style:normal;font-size:9px;font-weight:700}
/* 표 */
.onto-table{width:100%;border-collapse:collapse;font-size:11px}
.onto-table th{padding:7px 8px;border-bottom:1px solid var(--line);color:var(--muted);font-size:9px;
  font-weight:900;letter-spacing:.06em;text-align:left;white-space:nowrap}
.onto-table td{padding:7px 8px;border-bottom:1px solid var(--line);color:var(--ink);line-height:1.5;vertical-align:top}
.onto-table tbody tr{cursor:pointer}
.onto-table tbody tr:hover{background:var(--t-surface2)}
.onto-table tbody tr.on{background:var(--t-chip-bg)}
.onto-no{color:var(--muted);font-variant-numeric:tabular-nums}
.onto-sys{padding:1px 7px;border-radius:999px;font-size:9px;font-weight:900;white-space:nowrap}
.onto-sys.geo{background:#e3f1e9;color:#2f6b4c}
.onto-sys.topo{background:#e6e6f5;color:#4b4b8a}
body.utwin-dark .onto-sys.geo{background:#22392f;color:#8fd4b1}
body.utwin-dark .onto-sys.topo{background:#2a2a3d;color:#b3b3e6}
.onto-state{padding:1px 7px;border-radius:999px;font-size:9px;font-weight:900}
.onto-state.ok{background:#e3f1e9;color:#2f6b4c}
.onto-state.off{background:#f0efe9;color:#8a7f52}
body.utwin-dark .onto-state.ok{background:#22392f;color:#8fd4b1}
body.utwin-dark .onto-state.off{background:#3a3527;color:#e0cf95}
/* 인스턴스 요약 */
.onto-inst-sum{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:11px}
.onto-inst-sum span{padding:6px 10px;border:1px solid var(--line);border-radius:5px;background:var(--panel);
  color:var(--muted);font-size:10px}
.onto-inst-sum b{margin-right:4px;color:var(--ink);font-size:14px;font-variant-numeric:tabular-nums}
.onto-note{margin:9px 0 0;color:var(--muted);font-size:10px;line-height:1.65}
/* 에이전트 자원 */
.onto-agents{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px}
.onto-agent{padding:12px;border:1px solid var(--line);border-radius:6px;background:var(--panel)}
.onto-agent-head{display:flex;align-items:center;gap:7px}
.onto-agent-head b{flex:1;font-size:12px}
.onto-kind{padding:1px 7px;border-radius:999px;background:var(--t-surface2);color:var(--muted);
  font-size:8.5px;font-weight:900}
.onto-agent p{margin:7px 0 10px;color:var(--muted);font-size:10.5px;line-height:1.6}
.onto-agent-foot{display:flex;align-items:center;justify-content:space-between;gap:8px;color:var(--muted);font-size:9px}
/* 우측 세부 정보 */
.onto-side{min-width:0;display:flex;flex-direction:column;border-left:1px solid var(--line);background:var(--panel)}
.onto-side-body{flex:1;min-height:0;overflow:auto;padding:12px 13px}
.onto-detail-cap{margin:12px 0 6px;color:var(--muted);font-size:8.5px;font-weight:900;letter-spacing:.1em}
.onto-detail-cap:first-child{margin-top:0}
.onto-field{display:flex;align-items:baseline;gap:8px;margin-bottom:5px;font-size:11px}
.onto-field span{flex:0 0 78px;color:var(--muted);font-size:9.5px}
.onto-field b{font-size:12px}
.onto-constraint{display:flex;align-items:center;gap:8px;padding:8px 9px;border:1px solid var(--line);
  border-radius:5px;background:var(--t-surface2)}
.onto-constraint i{color:var(--muted);font-style:normal}
.onto-rel-list{margin:0;padding-left:15px;color:var(--ink);font-size:10.5px;line-height:1.75}
.onto-rel-list code{color:var(--muted)}
@media(max-width:1180px){.onto-wrap{grid-template-columns:170px minmax(0,1fr)}.onto-side{display:none}}
/* AI 스튜디오는 머리 · 모드 탭 · 본문 세 줄이다 */
.db-shell.ai-shell{grid-template-rows:auto auto 1fr}

/* ===== 스튜디오 세 화면은 테마를 따르지 않는다 (요청) =====
   AI 시나리오 스튜디오 · 재난 시나리오(같은 창) · 자동제어 분석 플로우 스튜디오는 다크에서
   면과 글씨가 모두 어두워져 노드·표가 잘 안 보였다. 이 세 화면은 라이트 화면을 그대로 쓴다.
   방법 — ① 창 안에서 토큰을 라이트 값으로 되돌리고(컴포넌트는 토큰만 보므로 대부분 이걸로 끝난다)
          ② 다크에서 값을 직접 박아 둔 몇 곳(격자 점 · 노드 머리 · 경고 상자 · 딱지)만 되돌린다.
   ②의 선택자는 `body.utwin-themed .force-light …` 로 한 단계 더 좁혀 원래 규칙을 이긴다. */
#aiOverlay{
  --bg:#dcdfdb;--ink:#161817;--accent:#1b1e1d;--muted:#5d6260;
  --panel:#ffffff;--line:#dfe1df;--line2:#ecedeb;--ink3:#686d6a;
  --surface2:#f4f5f3;--surface3:#ebedea;--accent-soft:#eceeed;--accent-ink:#ffffff;
  --t-input:#ffffff;--t-surface2:#f4f5f3;--t-chip-bg:#eceeed;--t-chip-text:#1b1e1d;
  --t-glass:rgba(255,255,255,.93);--t-glass2:rgba(255,255,255,.97);
  color-scheme:light;
  /* ★ 토큰만 바꾸면 body 의 다크 글자색이 그대로 내려온다 — 창 안의 기본 글자색도 여기서 정한다 */
  color:#161817;
}
body.utwin-themed .force-light .db-shell,
body.utwin-themed .force-light .db-node-stage,
body.utwin-themed .force-light .db-viewport{background-color:#f7f8f7!important}
body.utwin-themed .force-light .db-head,
body.utwin-themed .force-light .db-col,
body.utwin-themed .force-light .db-inspector,
body.utwin-themed .force-light .db-add-popover,
body.utwin-themed .force-light .db-node{background:#fff!important;color:#161817!important;border-color:#dfe1df!important}
body.utwin-themed .force-light .db-board{background-image:radial-gradient(circle,#bdc8c1 1px,transparent 1.2px)!important}
body.utwin-themed .force-light .db-shell b,body.utwin-themed .force-light .db-shell span,
body.utwin-themed .force-light .db-shell small,body.utwin-themed .force-light .db-shell label,
body.utwin-themed .force-light .db-shell code,body.utwin-themed .force-light .db-shell h2,
body.utwin-themed .force-light .db-shell h3{color:inherit!important}
/* 노드 머리 — 라이트 색조 그대로 */
body.utwin-themed .force-light .db-node-head{background:#e8f2ed!important;color:#176b54!important}
body.utwin-themed .force-light .db-node.source .db-node-head{background:#f6e7de!important;color:#9b4e2f!important}
body.utwin-themed .force-light .db-node.equipment .db-node-head{background:#dfeee8!important;color:#176b54!important}
body.utwin-themed .force-light .db-node.aggregate .db-node-head{background:#dfedf3!important;color:#245f78!important}
body.utwin-themed .force-light .db-node.chart .db-node-head{background:#eee7f5!important;color:#674c82!important}
body.utwin-themed .force-light .db-node.folder .db-node-head{background:#fbf3dd!important;color:#7a5a12!important}
body.utwin-themed .force-light .db-node.ai-feed .db-node-head{background:#e2eef9!important;color:#2a5b86!important}
body.utwin-themed .force-light .db-node.ai-group .db-node-head{background:#e9eef1!important;color:#3f5a68!important}
body.utwin-themed .force-light .db-node.ai-device .db-node-head{background:#dde8f5!important;color:#2b4f78!important}
body.utwin-themed .force-light .db-node.ai-trigger .db-node-head{background:#e3f1e9!important;color:#2f6b4c!important}
body.utwin-themed .force-light .db-node.ai-condition .db-node-head{background:#dfedf3!important;color:#245f78!important}
body.utwin-themed .force-light .db-node.ai-action .db-node-head{background:#fbeadb!important;color:#8a5b2f!important}
body.utwin-themed .force-light .db-node.ai-wait .db-node-head{background:#ecefed!important;color:#5d6560!important}
body.utwin-themed .force-light .db-node.ai-check .db-node-head{background:#eee7f5!important;color:#674c82!important}
body.utwin-themed .force-light .db-node.ai-notify .db-node-head{background:#fbe1de!important;color:#8c3f36!important}
body.utwin-themed .force-light .db-node.ai-chart .db-node-head{background:#e7e2f7!important;color:#4b3f86!important}
body.utwin-themed .force-light .db-node.ai-sop .db-node-head{background:#f6efd8!important;color:#7a6320!important}
/* 다크에서 색을 직접 박아 둔 나머지 — 경고 상자 · 선택 배경 · 딱지 */
body.utwin-dark .force-light .ai-sc-caution{background:#fdecea!important;color:#8f2c20!important}
body.utwin-dark .force-light .ai-sc-item.on{background:color-mix(in srgb,#176b54 14%,transparent)!important;border-color:#176b54!important}
body.utwin-dark .force-light .ai-sc-group[data-group="재난 대응"] .ai-sc-item.on{background:color-mix(in srgb,#b03a2c 15%,transparent)!important;border-color:#b03a2c!important}
body.utwin-dark .force-light .ai-sc-recommend{background:color-mix(in srgb,#176b54 12%,transparent)!important}
body.utwin-dark .force-light .ai-sc-doc[data-tone="fire"] .ai-sc-recommend{background:color-mix(in srgb,#b03a2c 12%,transparent)!important}
body.utwin-dark .force-light .onto-sys.geo{background:#e3f1e9!important;color:#2f6b4c!important}
body.utwin-dark .force-light .onto-sys.topo{background:#e6e6f5!important;color:#4b4b8a!important}
body.utwin-dark .force-light .onto-state.ok{background:#e3f1e9!important;color:#2f6b4c!important}
body.utwin-dark .force-light .onto-state.off{background:#f0efe9!important;color:#8a7f52!important}
body.utwin-themed .force-light .ai-sc-item{background:#fff!important;border-color:#dfe1df!important}
body.utwin-themed .force-light .ai-sc-item-type,body.utwin-themed .force-light .ai-sc-item-watch{color:#5d6260!important}

/* 촬영본(3DGS) 줄의 '실제 가로(m)' 칸 — 3DGS 는 좌표 단위가 임의라 실제 길이를 한 번 받아야
   아이콘 크기·장비 반경·클릭 판정이 맞는다(model/floors.js). 다른 형식 줄에는 나오지 않는다. */
.pk-splat-width{display:inline-flex;align-items:center;gap:4px;flex:0 0 auto;padding:2px 6px;
  border:1px solid var(--line);border-radius:8px;background:var(--t-glass);font-size:11px;color:var(--muted)}
.pk-splat-width input{width:56px;padding:2px 4px;border:1px solid var(--line);border-radius:6px;
  background:transparent;color:inherit;font:inherit;text-align:right}
.pk-splat-width em{font-style:normal;opacity:.75}

/* 3DGS 만 고른 경우의 +경량화 — 꺼져 있다는 것이 보여야 한다(눌러도 안 되는 이유를 옆에 적는다) */
.pk-opt.off{opacity:.55}
.pk-opt.off input{cursor:not-allowed}

/* 설정창의 두 갈래 버튼(직교/투시 등) — 드롭다운보다 한 번에 눌린다.
   고른 것은 **배경색으로만** 표시한다(박스 앞 세로선을 쓰지 않는 우리 규칙). */
.seg{display:inline-flex;gap:2px;padding:2px;border:1px solid var(--line);border-radius:999px;background:var(--t-glass)}
.seg-b{padding:4px 12px;border:0;border-radius:999px;background:transparent;color:var(--muted);
  font:inherit;font-size:12px;cursor:pointer;transition:background .12s,color .12s}
.seg-b:hover{background:rgba(127,127,127,.14)}
.seg-b.on{background:var(--accent);color:var(--accent-ink)}

/* 촬영본 맞추기 막대 — 3D 를 보면서 조작해야 하므로 서랍 밖, 화면 아래 가운데에 둔다.
   맞추는 동안에만 보인다(splat-align.js 가 show 를 붙인다). */
#splatAlignBar{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:22;
  display:none;align-items:center;gap:10px;padding:8px 12px;border:1px solid var(--line);
  border-radius:999px;background:var(--t-glass2);box-shadow:0 10px 30px rgba(16,20,18,.16);
  backdrop-filter:blur(16px);white-space:nowrap}
#splatAlignBar.show{display:flex}
#splatAlignBar b{font-size:12px}
#splatAlignBar button:not(.seg-b){padding:5px 12px;border:1px solid var(--line);border-radius:999px;
  background:transparent;color:inherit;font:inherit;font-size:12px;cursor:pointer}
#splatAlignBar button.p{background:var(--accent);color:var(--accent-ink);border-color:transparent}

.sim-settings-bar{flex:0 0 auto;padding:6px 0}
.sim-settings-bar .cfg-note{margin:4px 0;font-size:10px}

.sim-settings-bar .evac-placement-hint{border:2px solid currentColor;padding:8px;font-size:12px;font-weight:700;line-height:1.6}

#evacAnchorList{display:grid;gap:8px;margin:12px 0}
.evac-anchor-card{min-width:0;padding:10px;border:1px solid var(--line);border-radius:8px;background-color:var(--t-surface2)}
.evac-anchor-header{display:flex;align-items:center;justify-content:space-between;gap:8px}
.evac-anchor-name{min-width:0;color:var(--ink);font-size:11px;font-weight:700;line-height:1.5;overflow-wrap:anywhere}
.evac-anchor-card button,#evacDiagnostics button,.sim-panel button[data-action="evac-door-add"],.sim-panel button[data-action="evac-observe-next"]{box-sizing:border-box;min-height:30px;padding:6px 9px;border:1px solid var(--line);border-radius:6px;background-color:var(--t-input);color:var(--ink);font:inherit;font-size:10px;font-weight:600;cursor:pointer}
.evac-anchor-header button{flex:0 0 auto;color:var(--muted)}
.evac-anchor-card button:hover,#evacDiagnostics button:hover{border-color:var(--accent);color:var(--accent)}
.evac-anchor-card button:focus-visible,.evac-anchor-card summary:focus-visible,.evac-coordinates input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.evac-anchor-card details{margin-top:8px;border-top:1px solid var(--line);padding-top:8px}
.evac-anchor-card summary{color:var(--muted);font-size:10px;cursor:pointer;line-height:1.8}
.evac-coordinates{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;margin:10px 0}
.evac-coordinates label{display:flex;flex-direction:column;gap:5px;min-width:0;color:var(--muted);font-size:10px;font-weight:700}
.evac-coordinates input{box-sizing:border-box;width:100%;min-width:0;height:32px;padding:5px;border:1px solid var(--line);border-radius:5px;background-color:var(--t-input);color:var(--ink);font:inherit;font-size:11px;font-variant-numeric:tabular-nums}
.evac-anchor-actions{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.5fr);gap:6px}
#evacDiagnostics>div{margin-top:8px;padding:10px;border:1px solid var(--line);border-radius:8px}
#evacDiagnostics button{display:block;width:100%;margin-top:6px;text-align:left;white-space:normal;line-height:1.5}
.sim-panel button[data-action="evac-door-add"],.sim-panel button[data-action="evac-observe-next"]{width:100%;margin-top:8px}
#evacCongestionStatus{white-space:pre-line;line-height:1.8}
.last-work-status{font-size:10px;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.action-feedback{--feedback-line:var(--warn);--feedback-bg:var(--warn-bg);--feedback-ink:var(--warn-ink);box-sizing:border-box;margin:7px 0;padding:7px 9px;border:1px solid var(--feedback-line);border-radius:7px;background-color:var(--feedback-bg);color:var(--feedback-ink);font-size:10px;font-weight:500;line-height:1.5;overflow-wrap:anywhere}
.action-feedback[data-feedback-tone="error"]{--feedback-line:var(--bad);--feedback-bg:var(--bad-bg);--feedback-ink:var(--bad-ink)}
.action-feedback[data-feedback-tone="success"]{--feedback-line:var(--ok);--feedback-bg:var(--ok-bg);--feedback-ink:var(--ok-ink)}
.action-feedback::before{content:"안내";display:block;margin-bottom:3px;font-size:9px;font-weight:700;letter-spacing:.02em}
.action-feedback[data-feedback-tone="error"]::before{content:"주의"}
.action-feedback[data-feedback-tone="success"]::before{content:"완료"}
.action-feedback:empty,.action-feedback[hidden]{display:none}

.fds-source-dimensions{grid-template-columns:repeat(3,minmax(0,1fr))}
