:root{--ut-dashboard-right:0px}
#dashboardRoot{--ut-left-width:360px;--ut-right-width:360px;--ut-bottom-height:270px;--ut-bottom-left:0px;--ut-bottom-right:0px;position:fixed;z-index:7;inset:0;pointer-events:none;color:var(--ink);font-family:var(--font-ui)}
#dashboardRoot.catalog-open{z-index:40}
body.dashboard-editing .ut-dashboard-toggle{background:var(--accent-soft);color:var(--accent)}
body.dashboard-editing .ut-dashboard-toggle span{background:var(--accent);color:var(--accent-ink)}
/* 위젯 추가 — 편집 중에만 보인다. 존마다 있던 '＋' 버튼을 대신해 판 전체에 하나 둔다. */
.ut-dashboard-add{display:none;position:absolute;top:61px;right:300px;height:35px;padding:0 14px;border:1px solid var(--accent);border-radius:5px;background:var(--accent);color:var(--accent-ink);box-shadow:var(--shadow);font-size:12px;font-weight:900;cursor:pointer;pointer-events:auto}
#dashboardRoot.editing .ut-dashboard-add{display:block}
/* 화면 프리셋 — 편집 중에만 보인다. ＋위젯 추가 왼쪽에 둔다. */
.ut-dashboard-preset{display:none;position:absolute;top:61px;right:412px;height:35px;padding:0 13px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--ink);box-shadow:var(--shadow);font-size:12px;font-weight:900;cursor:pointer;pointer-events:auto}
#dashboardRoot.editing .ut-dashboard-preset{display:block}
.ut-dashboard-preset:hover{border-color:var(--accent)}
.ut-preset-menu{position:absolute;z-index:41;top:100px;right:412px;width:280px;padding:8px;border:1px solid var(--line);border-radius:5px;background:var(--panel);box-shadow:var(--shadow);pointer-events:auto}
.ut-preset-menu[hidden]{display:none}
.ut-preset-cap{margin:2px 4px 7px;color:var(--ink3);font-size:9px;font-weight:900;letter-spacing:.06em}
.ut-preset-cap small{color:var(--muted);font-size:9px;font-weight:800;letter-spacing:0}
.ut-preset-menu button{display:block;width:100%;margin-bottom:5px;padding:8px 9px;border:1px solid var(--line);border-radius:5px;background:var(--surface2);text-align:left;cursor:pointer}
.ut-preset-menu button:hover{border-color:var(--accent);background:var(--accent-soft)}
.ut-preset-menu b{display:block;color:var(--ink);font-size:11px;font-weight:900}
.ut-preset-menu span{display:block;margin-top:2px;color:var(--muted);font-size:9.5px}
.ut-preset-menu em{display:block;margin-top:3px;color:var(--ink3);font-style:normal;font-size:8.5px;font-weight:800}
.ut-preset-note{margin:6px 4px 2px;color:var(--ink3);font-size:8.5px;line-height:1.5}
.ut-dashboard-add:hover{filter:brightness(1.08)}
.ut-dashboard-demo{display:none;position:absolute;top:61px;right:155px;height:35px;padding:0 13px;border:1px solid var(--warn);border-radius:5px;background:var(--warn-bg);box-shadow:var(--shadow);color:var(--warn-ink);font-size:12px;font-weight:900;cursor:pointer;pointer-events:auto;backdrop-filter:blur(12px)}#dashboardRoot.editing .ut-dashboard-demo{display:block}.ut-dashboard-demo:hover{background:var(--warn);color:var(--panel)}
.ut-dashboard-status{position:absolute;z-index:4;top:58px;left:50%;transform:translateX(-50%);min-width:0;padding:0 12px;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:11px;font-weight:800;line-height:30px;opacity:0;transition:.2s;pointer-events:none}
.ut-dashboard-status:not(:empty){opacity:1}.ut-dashboard-status.error{background:var(--bad-bg);color:var(--bad-ink)}
.ut-dashboard-workspace{position:absolute;left:10px;right:10px;top:80px;bottom:22px;pointer-events:none}
/* 좌·우·하단 존은 1단계에서 없어졌다 — 위젯은 판 좌표(#utGrid)가 자리를 정한다.
   존 사각형·폭 조절 손잡이·존 버튼(위치·정렬)·빈 영역 안내는 함께 지웠다.
   layout.left/right/bottom 숫자는 남는다: 옛 저장본을 좌표로 옮길 때(migrateToGrid) 쓰인다. */
/* ===== AI 자동 운영 안내 카드 (방문객용) =====
   도서관에 온 방문객이 몇 초 안에 "AI가 지금 뭘 하고 있구나" 를 읽어야 한다.
   그래서 큰 글씨는 두 곳뿐이다 — 한 줄 설명과 탄소 성과. 단계는 점으로 잇고 지금 단계만 키운다. */
.ut-aiop{display:flex;flex-direction:column;gap:7px;min-height:0;overflow:auto;color:var(--ink)}
.ut-aiop-top{display:flex;align-items:center;gap:7px}
.ut-aiop-badge{padding:2px 8px;border-radius:999px;background:var(--line,rgba(27,30,29,.1));color:var(--muted);font-size:9px;font-weight:900;white-space:nowrap}
.ut-aiop-badge.live{background:var(--accent);color:var(--accent-ink);animation:ut-aiop-pulse 1.8s ease-in-out infinite}
.ut-aiop-badge.good{background:var(--ok-bg);color:var(--ok-ink)}
.ut-aiop-badge.warn{background:var(--warn-bg);color:var(--warn-ink)}
@keyframes ut-aiop-pulse{0%,100%{opacity:1}50%{opacity:.5}}
.ut-aiop-phase{flex:1;min-width:0;font-size:11px;font-weight:900;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ut-aiop-clock{color:var(--muted);font-size:10px;font-weight:800;font-variant-numeric:tabular-nums}
.ut-aiop-lead{margin:0;font-size:12px;font-weight:700;line-height:1.5}
/* 다섯 단계를 선으로 잇는다 — 지난 단계는 채워지고 지금 단계는 커진다 */
.ut-aiop-steps{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);margin:2px 0 0;padding:0;list-style:none}
.ut-aiop-steps li{position:relative;display:flex;flex-direction:column;align-items:center;gap:3px;min-width:0}
.ut-aiop-steps li::before{content:"";position:absolute;left:0;right:50%;top:6px;height:2px;background:var(--line,rgba(27,30,29,.14))}
.ut-aiop-steps li:first-child::before{display:none}
.ut-aiop-steps em{width:12px;height:12px;border-radius:50%;border:2px solid var(--line,rgba(27,30,29,.22));background:var(--panel,#fff);z-index:1}
.ut-aiop-steps b{max-width:100%;font-size:9.5px;font-weight:900;color:var(--muted);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ut-aiop-steps i{font-style:normal;font-size:8.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.ut-aiop-done em{border-color:var(--ok);background:var(--ok)}
.ut-aiop-done::before{background:var(--ok)}
.ut-aiop-now em{width:16px;height:16px;margin-top:-2px;border-color:var(--accent);background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}
.ut-aiop-now b{color:var(--accent)}
.ut-aiop-now::before{background:var(--accent)}
/* 지금 무엇을 하는 중인지 한 줄 */
p.ut-aiop-now{display:flex;align-items:baseline;gap:6px;margin:1px 0 0;color:var(--accent);font-size:11px;font-weight:800}
p.ut-aiop-now span{padding:1px 6px;border-radius:999px;background:var(--accent-soft);font-size:9px;font-weight:900}
.ut-aiop-live{display:flex;flex-wrap:wrap;gap:4px 12px;padding-top:5px;border-top:1px solid var(--line,rgba(27,30,29,.1))}
.ut-aiop-live span{display:flex;align-items:baseline;gap:5px}
.ut-aiop-live i{font-style:normal;font-size:9.5px;font-weight:800;color:var(--muted)}
.ut-aiop-live b{font-size:12px;font-weight:900;font-variant-numeric:tabular-nums}
.ut-aiop-green{padding:6px 8px;border-radius:5px;background:var(--ok-bg)}
.ut-aiop-green b{display:block;color:var(--ok-ink);font-size:11.5px;font-weight:900;line-height:1.45}
.ut-aiop-green span{display:block;margin-top:2px;color:var(--ok-soft);font-size:9.5px;line-height:1.45}
.ut-aiop-note{margin:0;color:var(--muted);font-size:8.5px;line-height:1.45}
/* ===== AI 사전 추천 문서 카드 =====
   차트 옆에 나란히 두는 읽는 카드다. 카드는 작아질 수 있으므로 안에서 스크롤되게 하고,
   글자 크기는 위젯 본문 기준(11px)보다 한 단계 작게 잡아 한 화면에 더 들어가게 한다.
   색은 토큰만 쓴다 — 라이트·다크 테마가 함께 따라온다. */
.ut-aisc{min-height:0;overflow:auto;font-size:10.5px;line-height:1.55;color:var(--ink)}
.ut-aisc-head{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin-bottom:4px}
.ut-aisc-tag{padding:2px 6px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:9px;font-weight:900;white-space:nowrap}
.ut-aisc-head b{font-size:12px;font-weight:900}
.ut-aisc-say{margin:0 0 7px;padding:7px 9px;border-radius:6px;background:color-mix(in srgb,var(--accent-soft) 70%,transparent);color:var(--ink);font-size:10.5px}
/* ===== 단계 스테퍼 =====
   단계마다 색을 준다(k-<종류>) — 자동제어 스튜디오 노드와 같은 색조라 두 화면이 같은 말을 한다.
     발동 초록 · 조건 파랑 · 제어 주황 · 대기 회색 · 확인 보라 · 알림 붉은색 · 후속 진보라
   잇는 것은 선이 아니라 **화살표**다. 흐름의 방향이 보여야 절차로 읽힌다.
   끝난 단계는 색을 채우고, 지금 단계는 같은 색 링을 둘러 한 칸만 도드라지게 한다. */
.ut-aisc-flow{margin:2px 0 9px;padding:11px 11px 8px;border:1px solid var(--line);border-radius:5px;background:color-mix(in srgb,var(--surface2) 55%,transparent)}
/* 한 줄에 다 담는다 — 가로로 밀어 보게 두면 '오늘의 절차' 를 한눈에 볼 수 없다(요청).
   칸은 남는 만큼 나눠 갖고(flex:1 1 0), 좁아지면 글자만 줄인다. 전체 문구는 title 로 남는다. */
/* ===== 오늘의 절차 =====
   전에는 단계를 flex 로 균등 분할해 13단계에서 칸이 24px 가 되고 한글 라벨이 음절 중간에서
   쪼개졌다(지적). 지금은 점 레일 + 지금·다음 두 줄 구조다. 글자는 한 줄에 담고 넘치면 … 로 자른다. */
.ut-aisc-rail{display:flex;align-items:center;gap:0;margin:0 0 9px;padding:0;list-style:none}
.ut-aisc-dot{--k:var(--muted);position:relative;flex:1 1 0;min-width:0;display:flex;justify-content:center}
.ut-aisc-dot::before{content:"";position:absolute;left:0;right:0;top:8px;height:2px;background:var(--line)}
.ut-aisc-dot:first-child::before{left:50%}
.ut-aisc-dot:last-child::before{right:50%}
.ut-aisc-dot.is-done::before,.ut-aisc-dot.is-now::before{background:color-mix(in srgb,var(--k) 55%,transparent)}
.ut-aisc-dot em{position:relative;z-index:1;display:grid;place-items:center;width:18px;height:18px;
  border:2px solid var(--k);border-radius:50%;background:var(--panel);color:var(--k);
  font-size:8.5px;font-weight:900;font-style:normal;line-height:1}
.ut-aisc-dot.is-done em{background:color-mix(in srgb,var(--k) 18%,transparent)}
.ut-aisc-dot.is-now em{width:22px;height:22px;background:var(--k);color:#fff;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--k) 22%,transparent)}
.ut-aisc-dot.is-wait em{border-color:var(--line);color:var(--ink3)}
/* 지금·다음 — 카드에서 실제로 읽는 두 줄 */
.ut-aisc-nowbox{margin:0 0 8px;padding:8px 9px;border:1px solid var(--line);border-radius:6px;
  background:var(--t-surface2)}
.ut-aisc-nowcap{color:var(--muted);font-size:8px;font-weight:900;letter-spacing:.1em}
.ut-aisc-nowcap.next{margin-top:7px;padding-top:6px;border-top:1px solid var(--line)}
.ut-aisc-stepline{display:flex;align-items:center;gap:6px;margin-top:4px;min-width:0}
.ut-aisc-stepline b{flex:1;min-width:0;font-size:11.5px;font-weight:800;line-height:1.4;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;word-break:keep-all}
.ut-aisc-stepline.is-next b{font-weight:600;color:var(--muted)}
.ut-aisc-stepno{--k:var(--muted);display:grid;place-items:center;width:17px;height:17px;flex:0 0 auto;
  border-radius:5px;background:var(--k);color:#fff;font-size:8.5px;font-weight:900}
.ut-aisc-stepkind{flex:0 0 auto;padding:1px 5px;border-radius:999px;background:var(--panel);
  border:1px solid var(--line);color:var(--muted);font-size:8px;font-weight:800}
.ut-aisc-dot.k-feed,.ut-aisc-stepno.k-feed{--k:#3c8ccd}
.ut-aisc-dot.k-group,.ut-aisc-stepno.k-group{--k:#7891a0}
.ut-aisc-dot.k-trigger,.ut-aisc-stepno.k-trigger{--k:#4ba57e}
.ut-aisc-dot.k-condition,.ut-aisc-stepno.k-condition{--k:#358fb8}
.ut-aisc-dot.k-action,.ut-aisc-stepno.k-action{--k:#e08042}
.ut-aisc-dot.k-wait,.ut-aisc-stepno.k-wait{--k:#9aa5a0}
.ut-aisc-dot.k-check,.ut-aisc-stepno.k-check{--k:#8b6fc0}
.ut-aisc-dot.k-notify,.ut-aisc-stepno.k-notify{--k:#ba5146}
.ut-aisc-dot.k-sop,.ut-aisc-stepno.k-sop{--k:#6f5ec0}
/* 현재 AI 판단 — 카드에서 가장 먼저 읽히는 자리 */
.ut-aisc-judge{display:flex;align-items:center;gap:10px;margin:0 0 8px;padding:8px 10px;border:1px solid var(--line);border-radius:5px;background:color-mix(in srgb,var(--accent-soft) 60%,transparent)}
.ut-aisc-judge>div:first-child{flex:1;min-width:0}
.ut-aisc-judge span{display:block;color:var(--ink3);font-size:9px;font-weight:800}
.ut-aisc-judge b{display:block;margin-top:2px;font-size:13px;font-weight:900;line-height:1.35}
.ut-aisc-judge small{display:block;margin-top:3px;color:var(--muted);font-size:9.5px;line-height:1.45}
.ut-aisc-judge-conf{flex:none;padding-left:10px;border-left:1px solid var(--line);text-align:right}
.ut-aisc-judge-conf b{font-size:17px;color:var(--accent)}
/* AI 캐릭터 — 이 카드가 AI 가 맡은 것임을 한눈에 보이게. 강조색 원 안에 선 얼굴. */
.ut-ai-face{flex:none;display:grid;place-items:center;width:22px;height:22px;border-radius:50%;
  background:var(--accent-soft);color:var(--accent)}
.ut-ai-face svg{width:15px;height:15px}
/* 헤더의 적용 상태 딱지 */
.ut-aisc-apply{padding:2px 7px;border-radius:999px;background:var(--line);color:var(--muted);font-size:9px;font-weight:900;white-space:nowrap}
.ut-aisc-apply.live{background:var(--accent);color:var(--accent-ink)}
/* 상황·근거·추천·승인 — 작은 카드 넷 */
.ut-aisc-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:6px;margin:0 0 8px}
.ut-aisc-fact{min-width:0;padding:7px 9px;border:1px solid var(--line);border-radius:5px;background:color-mix(in srgb,var(--surface2) 60%,transparent)}
.ut-aisc-fact>span{display:block;margin-bottom:3px;color:var(--ink3);font-size:9px;font-weight:900;letter-spacing:.04em}
.ut-aisc-fact>b{display:block;color:var(--ink);font-size:11px;font-weight:800;line-height:1.5}
.ut-aisc-chips{display:flex;flex-wrap:wrap;gap:4px;margin:0;padding:0;list-style:none}
.ut-aisc-chips li{padding:2px 6px;border-radius:999px;background:var(--accent-soft);color:var(--ink);font-size:9.5px;font-weight:800}
/* 가상 결과 — 전 → 후 */
.ut-aisc-results{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:6px;margin-bottom:8px}
.ut-aisc-res{min-width:0;padding:7px 9px;border:1px solid var(--line);border-radius:5px}
.ut-aisc-res>span{display:block;margin-bottom:4px;color:var(--ink3);font-size:9px;font-weight:800;line-height:1.4}
.ut-aisc-delta{display:flex;align-items:baseline;gap:6px;min-width:0}
.ut-aisc-delta em{color:var(--muted);font-style:normal;font-size:11px;font-weight:800;text-decoration:line-through}
.ut-aisc-delta i{color:var(--ink3);font-style:normal;font-size:11px}
.ut-aisc-delta b{color:var(--ok);font-size:14px;font-weight:900}
.ut-aisc-delta-one{color:var(--ink);font-size:12px;font-weight:900}
/* 진행 바 — 얇게. 여기서 색을 쓰면 스테퍼의 '지금 단계' 와 강조가 겹친다 */
.ut-aisc-progress{display:flex;align-items:center;gap:8px;margin-top:7px}
.ut-aisc-progress i{flex:1;height:3px;border-radius:999px;background:var(--line);overflow:hidden}
.ut-aisc-progress em{display:block;height:100%;border-radius:999px;background:color-mix(in srgb,var(--accent) 55%,transparent);transition:width .3s ease}
.ut-aisc-progress span{flex:none;color:var(--ink3);font-size:9px;font-weight:800;font-variant-numeric:tabular-nums}
/* 가정 한 줄 — 있는 줄 알면 되는 정보라 가장 작게 둔다 */
.ut-aisc-flow-note{margin:6px 0 0;color:var(--ink3);font-size:8.5px;line-height:1.45}
.ut-aisc-dl{display:grid;grid-template-columns:52px minmax(0,1fr);gap:3px 8px;margin:0}
.ut-aisc-dl dt{color:var(--muted);font-size:9.5px;font-weight:900}
.ut-aisc-dl dd{margin:0;min-width:0}
.ut-aisc-dl ul{margin:0;padding-left:14px}
.ut-aisc-warn{color:var(--bad);font-weight:700}
.ut-aisc-cap{margin:9px 0 4px;color:var(--muted);font-size:9px;font-weight:900}
.ut-aisc-cap em{font-style:normal;font-weight:600}
.ut-aisc-table{width:100%;border-collapse:collapse}
.ut-aisc-table th,.ut-aisc-table td{padding:3px 0;border-bottom:1px solid var(--line,rgba(27,30,29,.1));text-align:left;vertical-align:top}
.ut-aisc-table th{color:var(--muted);font-weight:600}
.ut-aisc-table td{width:44%;color:var(--ink);font-weight:800;text-align:right;font-variant-numeric:tabular-nums}
.ut-aisc-conf{display:flex;align-items:center;gap:6px;margin-top:9px}
.ut-aisc-conf span{color:var(--muted);font-size:9.5px;font-weight:900}
.ut-aisc-conf b{font-size:11px;font-weight:900}
.ut-aisc-bar{flex:1;min-width:40px;height:5px;border-radius:999px;background:var(--accent-soft);overflow:hidden}
.ut-aisc-bar em{display:block;height:100%;border-radius:999px;background:var(--accent)}
.ut-aisc-check{margin:0;padding-left:16px}
/* ===== AI 에너지 제어 =====
   기존 'AI 사전 추천' 문서 카드와 다른 독립 운영 위젯이다. 작은 정보 카드들을 겹치지 않고,
   절감 목표 → 계통 연동 → 안전 경계 → 전후 그래프 순서로 한 장에서 읽는다. */
.ut-aiec{min-height:0;overflow:auto;padding:9px 10px;border-radius:5px;background-color:var(--panel);color:var(--ink);font-size:10.5px}
.ut-aiec-head{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin:0 0 12px;padding:2px 2px 11px;border-bottom:1px solid var(--line)}
.ut-aiec-title{min-width:0}
.ut-aiec-title span{display:block;color:var(--accent);font-size:8.5px;font-weight:900;letter-spacing:.12em}
.ut-aiec-title b{display:block;margin-top:4px;font-size:17px;font-weight:900;line-height:1.25;word-break:keep-all}
.ut-aiec-saving{flex:none;text-align:right}
.ut-aiec-saving strong{display:block;color:var(--ok);font-size:25px;font-weight:900;line-height:1;font-variant-numeric:tabular-nums}
.ut-aiec-saving span{display:block;margin-top:3px;color:var(--muted);font-size:8.5px;font-weight:800}
.ut-aiec-section-label{margin:0 2px 6px;color:var(--ink3);font-size:8.5px;font-weight:900;letter-spacing:.06em}
/* 설비 세 대가 따로 놀지 않고 하나의 절감 흐름이라는 것이 이 위젯의 핵심이다. */
.ut-aiec-flowline{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:28px;margin:0 2px 10px}
.ut-aiec-flowline>div{position:relative;min-width:0}
.ut-aiec-flowline>div:not(:last-child)::after{content:"→";position:absolute;right:-20px;top:18px;color:var(--accent);font-size:16px;font-weight:500}
.ut-aiec-flowline em{display:block;color:var(--accent);font-style:normal;font-size:8px;font-weight:900;letter-spacing:.08em}
.ut-aiec-flowline span{display:block;margin-top:2px;color:var(--muted);font-size:8.5px;font-weight:800}
.ut-aiec-flowline b{display:block;margin-top:2px;font-size:10.5px;font-weight:900;line-height:1.35;word-break:keep-all}
.ut-aiec-outcomes{display:flex;align-items:center;gap:12px;margin:0 2px 9px;padding:7px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.ut-aiec-outcomes>b{flex:none;color:var(--ink3);font-size:8.5px;font-weight:900;letter-spacing:.05em}
.ut-aiec-outcomes ul{display:flex;flex-wrap:wrap;gap:5px 16px;margin:0;padding:0;list-style:none}
.ut-aiec-outcomes li{position:relative;padding-left:11px;font-size:9.5px;font-weight:800}
.ut-aiec-outcomes li::before{content:"";position:absolute;left:0;top:5px;width:5px;height:5px;border-radius:50%;background-color:var(--ok)}
.ut-aiec-guard{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:0 2px 9px}
.ut-aiec-guard>b{margin-right:2px;color:var(--ink3);font-size:8.5px;font-weight:900;letter-spacing:.05em}
.ut-aiec-guard span{padding:3px 7px;border:1px solid var(--line);border-radius:999px;color:var(--muted);font-size:8.5px;font-weight:800;white-space:nowrap}
.ut-aiec-chart{padding:9px 10px 7px;border-top:2px solid var(--accent);background-color:color-mix(in srgb,var(--surface2) 48%,transparent)}
.ut-aiec-chart-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.ut-aiec-chart-head>div:first-child b{display:block;font-size:11px;font-weight:900}
.ut-aiec-chart-head>div:first-child span{display:block;margin-top:1px;color:var(--muted);font-size:8.5px}
.ut-aiec-legend{display:flex;gap:9px;color:var(--muted);font-size:8.5px;font-weight:800}
.ut-aiec-legend span::before{content:"";display:inline-block;width:13px;height:2px;margin-right:4px;vertical-align:middle;background-color:var(--muted)}
.ut-aiec-legend .after::before{background-color:var(--ok)}
.ut-aiec-chart svg{display:block;width:100%;height:128px;overflow:visible}
.ut-aiec-grid line{stroke:var(--line);stroke-width:1}
.ut-aiec-grid text,.ut-aiec-tick,.ut-aiec-unit{fill:var(--muted);font-family:var(--font-ui);font-size:8px}
.ut-aiec-grid text{text-anchor:end}.ut-aiec-tick{text-anchor:middle}.ut-aiec-unit{font-weight:900}
.ut-aiec-line{fill:none;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.ut-aiec-line.before{stroke:var(--muted);stroke-dasharray:6 5;opacity:.72}
.ut-aiec-line.after{stroke:var(--ok)}
.ut-aiec-dot.before{fill:var(--panel);stroke:var(--muted);stroke-width:2}
.ut-aiec-dot.after{fill:var(--panel);stroke:var(--ok);stroke-width:2}
.ut-aiec-chart-result{display:flex;align-items:baseline;justify-content:center;gap:7px;margin-top:-4px;font-variant-numeric:tabular-nums}
.ut-aiec-chart-result span{color:var(--muted);font-size:9px;font-weight:800}
.ut-aiec-chart-result em{color:var(--muted);font-style:normal;font-size:12px;font-weight:800;text-decoration:line-through}
.ut-aiec-chart-result i{color:var(--ink3);font-style:normal}.ut-aiec-chart-result b{color:var(--ok);font-size:15px;font-weight:900}
.ut-aiec-chart-result strong{padding:2px 6px;border-radius:999px;background-color:var(--ok-bg);color:var(--ok-ink);font-size:9px;font-weight:900}
.ut-aiec-chart>p{margin:4px 0 0;color:var(--muted);font-size:8px;line-height:1.35;text-align:center}
@media(max-width:980px){.ut-aiec-flowline{gap:18px}.ut-aiec-flowline>div:not(:last-child)::after{right:-13px}.ut-aiec-chart svg{height:112px}}
/* ===== 좌표 판 (1단계) =====
   판 하나가 화면(작업영역) 전체를 32열 × 88행으로 나눈다. 위젯은 grid-area 로 자기 칸에 놓인다.
   판은 클릭을 먹지 않는다 — 카드만 먹으므로 빈 칸은 전부 3D 로 통과한다.
   칸 사이 간격은 gap 이 아니라 카드 여백으로 준다: gap 은 열·행 사이 119곳에 모두 들어가
   칸 크기를 다 잡아먹는다(32열이면 465px). */
.ut-grid{position:absolute;inset:0;display:grid;
  grid-template-columns:repeat(32,minmax(0,1fr));
  grid-template-rows:repeat(88,minmax(0,1fr));
  pointer-events:none}
.ut-grid>[data-card]{margin:7px}
/* 편집 중에는 판이 끌어 놓기를 받아야 한다. 열 눈금을 옅게 보여 자리를 가늠하게 한다
   (행은 88개라 선을 그리면 지저분하다). */
#dashboardRoot.editing .ut-grid{pointer-events:auto;
  background:repeating-linear-gradient(to right,color-mix(in srgb,var(--ink) 8%,transparent) 0 1px,transparent 1px calc(100% / 32))}
.ut-grid>.ut-widget-placeholder{margin:7px}
.ut-widget-placeholder.blocked{border-color:var(--bad);background:var(--bad-bg)}
.ut-widget.blocked{outline:2px solid var(--bad);outline-offset:-2px}
/* 끌고 있는 자리를 막고 있는 카드 — 무엇 때문에 못 놓는지 보여 준다 */
.ut-widget.collides{outline:2px dashed var(--bad);outline-offset:-2px}
/* 존 사각형은 1단계에서 더 이상 위젯을 담지 않는다 — 편집 모드에서도 그리지 않는다.
   폭 조절 손잡이·존 버튼도 좌표가 대신하므로 감춘다(2단계에서 정리). */
.ut-widget.dragging{opacity:.35}
/* 시뮬레이션 창이 열리면 위젯이 화면 밖으로 밀려 나간다(dashboard.js tuckWidgets).
   방향은 카드 마크업의 data-tuck 에 박혀 있다 — 다시 그려도 자리를 잃지 않는다.
   ★ 시간은 0.9초다(요청: 지금의 2배 느리게). 아래 [data-card] 의 0.45초 전환이 뒤에 있어
     같은 특정도면 그쪽이 이긴다 — [data-tuck] 을 붙여 특정도를 한 칸 높여 이 값이 쓰이게 한다.
     투명도에도 전환을 따로 적는다. 전에는 [data-card] 에 transform·box-shadow 만 있어서
     사라지는 순간 투명도가 **툭 꺼졌다**. */
.ut-widget[data-tuck]{transition:transform .9s cubic-bezier(.22,1,.36,1),opacity .9s ease}
body.sim-tucked .ut-widget{opacity:0;pointer-events:none}
body.sim-tucked .ut-widget[data-tuck="left"]{transform:translate3d(-115vw,0,0)}
body.sim-tucked .ut-widget[data-tuck="right"]{transform:translate3d(115vw,0,0)}
body.sim-tucked .ut-widget[data-tuck="down"]{transform:translate3d(0,115vh,0)}
.ut-widget.dragging{transition:none}
.ut-widget-placeholder{border:2px dashed var(--accent);border-radius:5px;background:color-mix(in srgb,var(--ink) 10%,transparent);min-height:60px;box-sizing:border-box}
.split-left,.split-right{top:0;bottom:0;width:10px;cursor:col-resize}.split-left{left:var(--ut-left-width)}.split-right{right:var(--ut-right-width)}.split-left::after,.split-right::after{top:12px;bottom:12px;left:4px;width:2px}.split-bottom{left:var(--ut-bottom-left);right:var(--ut-bottom-right);bottom:var(--ut-bottom-height);height:10px;cursor:row-resize}.split-bottom::after{left:14px;right:14px;top:4px;height:2px}.split-bottom-left,.split-bottom-right{bottom:0;width:10px;height:var(--ut-bottom-height);cursor:col-resize}.split-bottom-left{left:calc(var(--ut-bottom-left) - 5px)}.split-bottom-right{right:calc(var(--ut-bottom-right) - 5px)}.split-bottom-left::after,.split-bottom-right::after{top:10px;bottom:10px;left:4px;width:2px}
/* 배경만 10% 투명하게 둔다 — 뒤의 3D 화면이 아주 살짝 비친다.
   opacity 를 쓰지 않는 이유: 글자·차트까지 함께 흐려지고, 레이어가 생겨 스포트라이트 확대 때
   글자가 뿌옇게 된다. color-mix 로 배경색만 반투명하게 만들면 내용은 완전히 선명하다.
   테마별로 값을 두 번 쓰지 않아도 --panel 을 그대로 따라간다. */
/* 카드 배경 진하기는 화면 제어의 '위젯 배경 진하기' 가 --ut-card-alpha 로 넣는다.
   투명도를 opacity 로 주면 글자·차트까지 같이 흐려져 읽기 어렵다. 그래서 **배경색의 알파만**
   낮춘다 — 글자는 var(--ink) 그대로다. */
.ut-widget{position:relative;display:flex;flex-direction:column;min-width:0;min-height:0;border:0;border-radius:5px;background:color-mix(in srgb,var(--panel) var(--ut-card-alpha,90%),transparent);box-shadow:var(--shadow);overflow:hidden;pointer-events:auto}
/* 방금 올린 위젯 — 어느 영역에 붙었는지 바로 보이게 1.4초 반짝인다 */
@keyframes utJustAdded{
  0%{box-shadow:0 0 0 0 rgba(27,30,29,.85),0 12px 28px rgba(16,20,18,.13)}
  18%{transform:scale(1.02)}
  60%{box-shadow:0 0 0 16px rgba(27,30,29,0),0 12px 28px rgba(16,20,18,.13);transform:scale(1)}
  100%{box-shadow:0 0 0 0 rgba(27,30,29,0),0 12px 28px rgba(16,20,18,.13)}
}
.ut-widget.just-added{animation:utJustAdded 700ms ease-out 2;border-color:var(--accent);z-index:3}
@media (prefers-reduced-motion:reduce){.ut-widget.just-added{animation:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(27,30,29,.45)}}
.ut-widget.dragging{opacity:.48}
.ut-widget.resizing{user-select:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--ink) 36%,transparent),0 16px 34px rgba(16,20,18,.18)}
.ut-widget>header{position:static;z-index:auto;display:flex;width:auto;align-items:center;gap:8px;min-height:44px;padding:0 14px;border-bottom:0;background:transparent}
/* 뱃지는 한글이다(SCHED·DEMD 같은 약자는 시연에서 아무도 못 읽었다).
   7px 고정폭 글꼴은 한글에 맞지 않아 UI 글꼴로 바꾸고 한 단계 키운다. */
.ut-widget-code{flex:none;padding:4px 6px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font:800 9px/1 var(--font-ui);letter-spacing:0;white-space:nowrap}
.ut-widget>header>div:nth-child(2){min-width:0;flex:1}.ut-widget>header b,.ut-widget>header small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ut-widget>header b{font-size:12px}.ut-widget>header small{margin-top:2px;color:var(--ink3);font-size:9px}
.ut-widget-actions{display:none;position:relative;z-index:5;gap:3px;pointer-events:auto}#dashboardRoot.editing .ut-widget-actions{display:flex}.ut-widget-actions button{position:relative;z-index:6;height:23px;padding:0 5px;border:1px solid var(--line);border-radius:5px;background:var(--panel);color:var(--muted);font-size:9px;font-weight:800;cursor:pointer;pointer-events:auto}.ut-widget-actions button:hover{border-color:var(--accent);color:var(--accent)}
/* 이동 손잡이는 글자("이동") 대신 이동 아이콘을 쓴다 — 세 버튼이 나란히 있어 글자로는 좁고,
   집어서 끄는 것이라는 게 아이콘 쪽이 분명하다. svg 가 들어가므로 가운데 정렬과 폭을 맞춘다. */
.ut-widget-actions [data-widget-drag]{display:flex;align-items:center;justify-content:center;width:23px;padding:0;cursor:grab}
.ut-widget-actions [data-widget-drag] svg{width:12px;height:12px;pointer-events:none}
.ut-widget-actions [data-widget-drag]:active{cursor:grabbing}
/* 카드가 존 높이에 맞춰 커지므로 본문도 남는 높이를 나눠 가진다 — 그러지 않으면 내용이
   위에 몰려 카드 속이 텅 빈 것처럼 보인다(행을 1fr 로 바꾼 뒤 실제로 그랬다). */
.ut-widget-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:10px;overflow:hidden;gap:8px;justify-content:stretch}
.ut-widget-body>*{flex:1 1 auto;min-height:0}
.ut-widget.manual-height .ut-widget-body{height:calc(100% - 43px);min-height:0;overflow:hidden}/* 크기 조절 손잡이 — 네 모서리. 모양은 원래 있던 오른쪽 아래 손잡이 그대로,
   방향만 각 모서리를 향한다. 편집 중에만 보인다. */
.ut-widget-resize{display:none;position:absolute;z-index:9;width:26px;height:26px;padding:0;border:0;border-radius:5px;background:color-mix(in srgb,var(--ink) 16%,transparent);pointer-events:auto}
#dashboardRoot.editing .ut-widget-resize{display:block}
.ut-widget-resize.grip-nw{left:3px;top:3px;cursor:nwse-resize}
.ut-widget-resize.grip-ne{right:3px;top:3px;cursor:nesw-resize}
.ut-widget-resize.grip-sw{left:3px;bottom:3px;cursor:nesw-resize}
.ut-widget-resize.grip-se{right:3px;bottom:3px;cursor:nwse-resize}
.ut-widget-resize::before,.ut-widget-resize::after{content:"";position:absolute;opacity:.72;border:0 solid var(--accent)}
.ut-widget-resize::before{width:9px;height:9px}
.ut-widget-resize::after{width:4px;height:4px}
.ut-widget-resize.grip-se::before,.ut-widget-resize.grip-se::after{right:3px;bottom:3px;border-right-width:2px;border-bottom-width:2px}
.ut-widget-resize.grip-nw::before,.ut-widget-resize.grip-nw::after{left:3px;top:3px;border-left-width:2px;border-top-width:2px}
.ut-widget-resize.grip-ne::before,.ut-widget-resize.grip-ne::after{right:3px;top:3px;border-right-width:2px;border-top-width:2px}
.ut-widget-resize.grip-sw::before,.ut-widget-resize.grip-sw::after{left:3px;bottom:3px;border-left-width:2px;border-bottom-width:2px}
.ut-widget-resize:hover::before,.ut-widget-resize:hover::after{opacity:1}
/* 위쪽 두 모서리는 머리글 자리다 — 편집 중에는 머리글 내용을 손잡이 폭만큼 안으로 들인다.
   그러지 않으면 왼쪽 손잡이가 코드 뱃지를, 오른쪽 손잡이가 삭제 버튼을 덮는다. */
#dashboardRoot.editing .ut-widget>header{padding:0 33px}
.zone-bottom .ut-widget.size-s{grid-column:span 2}.zone-bottom .ut-widget.size-m{grid-column:span 3}.zone-bottom .ut-widget.size-l{grid-column:span 6}
.ut-widget.size-s .ut-widget-kpis{gap:3px}.ut-widget.size-s .ut-widget-kpis>div{min-height:62px}.ut-widget.size-s .ut-widget-kpis b{font-size:13px}.ut-widget.size-s .ut-widget-kpis span{font-size:8px}
.ut-widget-kpis{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px}
.ut-widget-kpis>div{display:flex;flex-direction:column;align-items:center;justify-content:center}.ut-widget-kpis>div{display:flex;min-height:72px;align-items:center;justify-content:center;flex-direction:column;border:1px solid var(--line);border-radius:5px;background:var(--surface2);text-align:center}.ut-widget-kpis b{color:var(--accent);font-size:17px;letter-spacing:-.04em}.ut-widget-kpis span{margin-top:4px;color:var(--ink3);font-size:9px}.ut-widget-kpis .warn b,.warn{color:var(--warn)!important}.ut-widget-kpis .bad b,.bad{color:var(--bad-ink)!important}.good{color:var(--accent)!important}
.ut-widget-rows{display:grid;gap:5px}.ut-widget-rows>div{display:flex;align-items:center;justify-content:space-between;gap:8px;min-height:25px;padding:0 7px;border-bottom:1px solid var(--line2);font-size:10px}.ut-widget-rows span{overflow:hidden;color:var(--muted);text-overflow:ellipsis;white-space:nowrap}.ut-widget-rows b{color:var(--ink);font-size:10px;white-space:nowrap}
/* 접은 줄을 감춘다. `.ut-widget-rows>div{display:flex}` 가 브라우저 기본 [hidden]{display:none} 보다
   세서, 이 줄이 없으면 hidden 을 걸어도 그대로 보인다(줄을 접어도 카드가 안 줄었다). */
.ut-widget-rows>div[hidden]{display:none}
/* '외 N건' — 카드에 다 못 담은 줄이 있다는 표시. 줄 목록과 같은 폭에 오른쪽 정렬. */
.ut-widget-more{flex:none;padding:4px 7px 0;color:var(--ink3);font-size:9px;font-weight:700;text-align:right}
.ut-widget-empty{display:flex;min-height:95px;align-items:center;justify-content:center;color:var(--ink3);font-size:10px;text-align:center}
.ut-solar-widget{display:grid;gap:5px;height:100%;min-height:120px}.ut-solar-arc{position:relative;height:100px;overflow:hidden;border-radius:5px;background:var(--sky)}.ut-solar-arc svg{position:absolute;inset:2px 7px 0;width:calc(100% - 14px);height:100%;overflow:visible}.ut-solar-arc path{fill:none;stroke-linecap:round;stroke-width:8}.ut-solar-arc-base{stroke:var(--line)}.ut-solar-arc-progress{stroke:#3188f4}.ut-solar-marker{fill:#ffc400;stroke:#fff;stroke-width:2;filter:drop-shadow(0 2px 3px rgba(213,142,0,.38))}.ut-solar-marker.off{fill:#aeb8bd;filter:none}.ut-solar-center{position:absolute;left:50%;top:48px;transform:translateX(-50%);text-align:center;white-space:nowrap}.ut-solar-center small{display:block;color:var(--muted);font-size:9px}.ut-solar-center b{display:block;margin-top:3px;color:var(--ink);font-size:14px;font-variant-numeric:tabular-nums}.ut-solar-rise,.ut-solar-set{position:absolute;bottom:3px;color:var(--muted);font-size:8px}.ut-solar-rise{left:8px}.ut-solar-set{right:8px;text-align:right}.ut-solar-rise small,.ut-solar-set small{display:block}.ut-solar-rise b,.ut-solar-set b{display:block;margin-top:1px;color:var(--ink);font-size:10px;font-variant-numeric:tabular-nums}.ut-solar-summary{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.ut-solar-summary span{padding:6px 3px;border:1px solid var(--line);border-radius:5px;background:var(--surface2);text-align:center}.ut-solar-summary b{display:block;color:var(--accent);font-size:11px;white-space:nowrap}.ut-solar-summary small{display:block;margin-top:2px;color:var(--ink3);font-size:8px}.ut-solar-widget>p{margin:0;color:var(--ink3);font-size:8px;text-align:right}
.ut-analysis-meta{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:5px}.ut-analysis-meta span{padding:3px 5px;border-radius:5px;background:var(--accent-soft);color:var(--accent);font-size:9px;font-weight:800}/* 그림의 최소 높이 — '지금 값' 줄(약 24px)이 위에 붙으면서 90px 로는 작은 카드가 넘쳤다 */
.ut-analysis-canvas{display:block;width:100%;flex:1 1 auto;min-height:70px}
/* 차트 아래 축 설명 — 가로·세로가 무엇인지 한 줄로. 차트를 가리지 않게 작고 흐리게 둔다. */
/* 차트 위 '지금 값' — 큰 숫자 + 단위 + 직전 대비. 높이를 나눠 갖지 않게 flex:none 이다. */
.ut-chart-now{flex:none;display:flex;align-items:baseline;gap:5px;margin:1px 0 4px}
.ut-chart-now[hidden]{display:none}
.ut-chart-now b{color:var(--ink);font-size:20px;font-weight:800;letter-spacing:-.04em;line-height:1}
.ut-chart-now>span{color:var(--ink3);font-size:10px;font-weight:700}
.ut-chart-now em{margin-left:auto;color:var(--ink3);font-size:9px;font-style:normal;font-weight:700}
.ut-chart-now em[data-dir=up]{color:var(--warn-ink)}
.ut-chart-now em[data-dir=down]{color:var(--ok-ink)}
.ut-analysis-axis{flex:none;margin-top:4px;color:var(--ink3);font-size:9px;line-height:1.5}
.ut-analysis-axis b{color:var(--muted);font-weight:900}
.ut-analysis-axis em{display:inline-block;width:14px;height:0;margin-right:3px;vertical-align:middle;border-top:1.5px dashed var(--muted)}.ut-widget.size-l .ut-analysis-canvas{min-height:120px}
.ut-video-placeholder,.ut-external-placeholder{display:flex;min-height:115px;align-items:center;justify-content:center;flex-direction:column;gap:5px;border-radius:5px;background:linear-gradient(145deg,#1e2b26,#43544d);color:#fff}.ut-video-placeholder span{padding:3px 6px;border-radius:5px;background:#d34e43;font-size:9px;font-weight:900}.ut-video-placeholder b,.ut-external-placeholder b{font:800 11px var(--font-ui);font-variant-numeric:tabular-nums}.ut-video-placeholder small,.ut-external-placeholder span{color:var(--ink3);font-size:9px}
/* 가상 CCTV 라이브 피드 (오프라인 시뮬레이션) */
.ut-cctv{position:relative;min-height:130px;border-radius:5px;overflow:hidden;background:#0b0f0d;font:800 10px var(--font-ui);font-variant-numeric:tabular-nums;color:#e8f0ec}
/* 통신 단절 카메라 — 화면을 어둡게 죽이고 REC 대신 NO SIGNAL 을 빨갛게 표시한다 */
.ut-cctv.offline .ut-cctv-scene{filter:grayscale(1) brightness(.45)}
.ut-cctv.offline .ut-cctv-rec{background:#b4432f;animation:none}
.ut-cctv.offline .ut-cctv-scan{display:none}
.ut-cctv-scene{position:absolute;inset:0;background:radial-gradient(120% 90% at 50% 8%,#243a33,#0b0f0d 72%)}
.ut-cctv-floor{position:absolute;left:-25%;right:-25%;bottom:0;height:56%;background-image:linear-gradient(rgba(236,238,237,.18) 1px,transparent 1px),linear-gradient(90deg,rgba(236,238,237,.18) 1px,transparent 1px);background-size:22px 22px;transform:perspective(120px) rotateX(56deg);transform-origin:bottom;opacity:.5}
.ut-cctv-fig{position:absolute;bottom:27%;width:11px;height:28px;border-radius:5px;background:#0c110f;box-shadow:0 0 0 1px rgba(236,238,237,.24);opacity:.9}
.ut-cctv-fig::before{content:"";position:absolute;top:-8px;left:2px;width:7px;height:7px;border-radius:50%;background:#0c110f;box-shadow:0 0 0 1px rgba(236,238,237,.24)}
.ut-cctv-fig.a{animation:ut-cctv-walk 9s linear infinite}
.ut-cctv-fig.b{transform:scale(.82);animation:ut-cctv-walk 13s linear infinite reverse}
@keyframes ut-cctv-walk{0%{left:12%}50%{left:74%}100%{left:12%}}
.ut-cctv-scan{position:absolute;left:0;right:0;height:22%;background:linear-gradient(rgba(236,238,237,.10),transparent);animation:ut-cctv-scan 3.2s linear infinite}
@keyframes ut-cctv-scan{0%{top:-22%}100%{top:100%}}
.ut-cctv-grain{position:absolute;inset:0;box-shadow:inset 0 0 42px rgba(0,0,0,.6);background:repeating-linear-gradient(transparent,transparent 2px,rgba(0,0,0,.09) 3px)}
.ut-cctv-hud{position:absolute;left:8px;right:8px;display:flex;justify-content:space-between;align-items:center;text-shadow:0 1px 2px #000}
.ut-cctv-top{top:7px}.ut-cctv-bottom{bottom:7px;color:var(--accent-soft);font-weight:700}
.ut-cctv-rec{padding:2px 6px;border-radius:5px;background:#d34e43;color:#fff;animation:ut-cctv-blink 1.1s steps(1) infinite}
.ut-cctv-rec::before{content:"● "}
@keyframes ut-cctv-blink{50%{opacity:.25}}
.ut-widget-catalog{display:none;position:absolute;z-index:12;inset:0;align-items:center;justify-content:center;padding:24px;background:rgba(18,30,24,.42);pointer-events:auto;backdrop-filter:blur(4px)}.ut-widget-catalog.show{display:flex}/* 색은 전부 테마 토큰으로 쓴다 — 배경만 #fff 로 박아 두었더니 다크 테마에서 글자색(--ink)이
   밝아지면서 흰 배경에 흰 글자가 됐다("글자가 배경에 묻혀 안 보임"). */
.ut-catalog-box{width:min(1060px,94vw);max-height:90vh;display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--line);border-radius:5px;background:var(--panel,#fff);color:var(--ink);box-shadow:0 30px 80px rgba(17,30,24,.32)}
/* 스튜디오 버튼 색은 스튜디오 것이다 — 대시보드 다크 테마 정리 때 함께 토큰으로 바꿨다가
   되돌렸다. 스튜디오 화면의 색은 여기서 손대지 않는다. */
/* ★ 바탕을 밝은 민트로 박아 두고 글자는 --accent 를 쓰던 자리 — 다크에서 accent 가 밝아져
   밝은 글자 + 밝은 바탕이 되어 **글자가 사라졌다**(실측 대비 1.04). 바탕도 토큰으로 만든다:
   accent 를 12%만 섞어 '게시' 라는 성격은 남기고, 대비는 테마가 알아서 맞추게 한다. */
.db-chart-publish{border-color:var(--accent)!important;color:var(--accent)!important;
  background:color-mix(in srgb,var(--accent) 12%,var(--t-surface2))!important}
.ut-catalog-box>header{position:static;z-index:auto;pointer-events:auto;display:flex;width:auto;align-items:flex-start;gap:12px;padding:17px 19px 13px;border-bottom:1px solid var(--line)}.ut-catalog-box>header>div{flex:1}.ut-catalog-box h2{margin:0;color:var(--accent);font-size:18px}.ut-catalog-box header p{margin:4px 0 0;color:var(--ink3);font-size:11px}.ut-catalog-box header button{width:31px;height:31px;border:1px solid var(--line);border-radius:5px;background:var(--t-input,#fff);color:var(--ink);font-size:20px;cursor:pointer}
/* 구역을 2열로 흘린다 — 스크롤을 없애기 위한 것.
   실측(1920×1080): 구역 9개가 각각 79px 씩 쌓여 내용 1081px, 보이는 높이 970px → 111px 넘침.
   창이 900 이면 273px 넘쳤다. 그중 5개 구역(환경·공간·안전·영상·연동)은 항목이 1개인데도
   제목 + 한 줄을 통째로 써서 395px 을 차지했다.
   설명글을 줄이는 것으로는 안 된다 — 설명은 이미 한 줄로 잘려 있고(nowrap + ellipsis)
   버튼 높이 62px 은 글자 수와 무관하다. 줄일 것은 '구역이 쌓이는 높이' 다.
   columns 로 흘리면 짧은 구역들이 옆 열에 나란히 붙어 높이가 대략 절반이 된다.
   break-inside:avoid 로 구역이 열 경계에서 쪼개지지 않게 한다. */
/* 창이 아주 낮으면(1366×768 등) 이 목록만 스크롤한다 — 상자 전체가 스크롤되면 머리글과
   아래 [위젯 추가] 버튼까지 밀려 올라가 무엇을 누를지 알 수 없다. */
/* 구역 폭을 **항목 수에 맞춘다** — 제목은 그대로 두고, 한 항목짜리 구역(환경·공간·안전·영상·연동)이
   한 줄을 통째로 쓰지 않고 서로 나란히 앉는다.
   판을 '버튼 6칸' 으로 본다: 항목 N개인 구역은 N칸을 차지하고, 그 안에서 버튼을 한 줄로 늘어놓는다
   (auto-flow:column). 그래서 버튼 폭이 어느 구역에서나 같다 — 처음엔 판을 3칸으로 잡았더니
   버튼이 338px 로 두 배가 되고 높이가 717 → 877px 로 오히려 늘었다.
   dense 로 남는 칸을 뒤 구역이 메운다 — 안 그러면 한 항목짜리 구역 옆이 계속 빈다.
   그래서 화면 순서가 목록 순서와 조금 달라질 수 있다(대신 촘촘하다). */
.ut-catalog-types{flex:1 1 auto;min-height:0;overflow:auto;padding:10px 16px;background:var(--surface2,var(--surface2));
  display:grid;grid-template-columns:repeat(6,minmax(0,1fr));grid-auto-flow:row dense;grid-auto-rows:min-content;gap:7px 14px;align-content:start;align-items:start}
.ut-catalog-types section{grid-column:span 6;margin:0}
.ut-catalog-types h3{margin:0 0 3px;color:var(--muted);font-size:10px;line-height:1.3;letter-spacing:.08em}
.ut-catalog-types section+section{margin-top:0}
.ut-catalog-types section>div{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:6px}
.ut-catalog-types section:has(button:nth-child(5)):not(:has(button:nth-child(6))){grid-column:span 5}
.ut-catalog-types section:has(button:nth-child(4)):not(:has(button:nth-child(5))){grid-column:span 4}
.ut-catalog-types section:has(button:nth-child(3)):not(:has(button:nth-child(4))){grid-column:span 3}
.ut-catalog-types section:has(button:nth-child(2)):not(:has(button:nth-child(3))){grid-column:span 2}
.ut-catalog-types section:has(button:only-child){grid-column:span 1}
.ut-catalog-types button{min-width:0;padding:6px 7px;border:1px solid var(--line);border-radius:5px;background:var(--panel,#fff);text-align:left;cursor:pointer}.ut-catalog-types button.selected{border-color:var(--accent);box-shadow:0 0 0 2px color-mix(in srgb,var(--ink) 14%,transparent);background:var(--accent-soft,var(--accent-soft))}.ut-catalog-types button b,.ut-catalog-types button span,.ut-catalog-types button small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ut-catalog-types button b{color:var(--accent);font:800 7px var(--font-ui);font-variant-numeric:tabular-nums}.ut-catalog-types button span{margin-top:5px;color:var(--ink);font-size:11px;font-weight:900}.ut-catalog-types button small{margin-top:2px;color:var(--ink3);font-size:9px}
.ut-catalog-config{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:11px 18px}.ut-catalog-config label{display:block}.ut-catalog-config label.wide{grid-column:1/-1}.ut-catalog-config label[hidden]{display:none}.ut-catalog-config span{display:block;margin-bottom:5px;color:var(--muted);font-size:10px;font-weight:900}.ut-catalog-config input,.ut-catalog-config select{width:100%;height:34px;padding:0 8px;border:1px solid var(--line);border-radius:5px;background:var(--t-input,#fff);color:var(--ink);font-size:11px}
.ut-catalog-box>footer{display:flex;align-items:center;gap:7px;padding:10px 18px;border-top:1px solid var(--line)}.ut-catalog-box footer span{min-width:0;flex:1;color:var(--muted);font-size:10px}.ut-catalog-box footer button{height:34px;padding:0 14px;border:1px solid var(--line);border-radius:5px;background:var(--panel,#fff);color:var(--ink);font-size:11px;font-weight:900;cursor:pointer}.ut-catalog-box footer button.primary{border-color:var(--accent);background:var(--accent);color:var(--accent-ink)}
/* 좁은 창에서는 구역을 전체 폭으로 되돌리고 버튼을 3열로 접는다 — 나란히 놓으면 너무 좁아진다 */
@media(max-width:1100px){
  .ut-catalog-types section,
  .ut-catalog-types section:has(button:nth-child(2)):not(:has(button:nth-child(3))),
  .ut-catalog-types section:has(button:nth-child(3)):not(:has(button:nth-child(4))),
  .ut-catalog-types section:has(button:nth-child(4)):not(:has(button:nth-child(5))),
  .ut-catalog-types section:has(button:nth-child(5)):not(:has(button:nth-child(6))),
  .ut-catalog-types section:has(button:only-child){grid-column:span 6}
  .ut-catalog-types section>div{grid-auto-flow:row;grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media(max-width:900px){
  /* 좁은 화면 — 판 하나이므로 존을 세로로 쌓던 규칙은 필요 없다. 여백만 줄인다. */
  .ut-dashboard-workspace{left:12px;right:12px;top:105px}
}

/* ===== 위젯 순환 스포트라이트 (데모 시연 연출) =====
   3초마다 카드 하나가 '제 차례'를 갖는다. 데모 모드에서만 켜진다(dashboard.js).
   인라인 스타일로 하이라이트를 넣지 않는다 — el.style.X="" 로 되돌리면 원래 선언까지 지워져
   카드 배경·그림자가 사라진다. 그래서 속성(data-live) 토글 + CSS 로만 처리한다. */
[data-card]{transition:transform .45s cubic-bezier(.22,1,.36,1),box-shadow .45s ease}
[data-card][data-live]{z-index:3;transform:translateY(-3px) scale(1.05);box-shadow:var(--shadow-live)}
/* will-change:transform 을 일부러 쓰지 않는다 — 레이어를 고정 배율로 굳혀 확대된 글자가 뿌옇게
   된다(전에 그 증상이 있었다). 지정하지 않으면 크로미움이 전환이 끝난 배율로 글자를 다시 그린다. */

/* 커진 직후 유리창을 닦는 듯한 빛이 한 번 지나간다.
   카드(.ut-widget)에 overflow:hidden 이 있어 모서리 라운드 안쪽으로 잘린다.
   지연 .45s 는 위의 확대 전환 시간과 같다 — 다 커진 뒤에 지나가게 맞춘 값이다.
   스포트라이트는 3초마다 옮겨 가므로 .45+.85=1.3s 는 한 차례 안에 끝난다. */
[data-card][data-live]::after{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;border-radius:inherit;
  background:linear-gradient(105deg,transparent 26%,var(--sheen-soft) 43%,var(--sheen-core) 50%,var(--sheen-soft) 57%,transparent 74%);
  transform:translateX(-140%);
  animation:ut-sheen .85s cubic-bezier(.38,0,.2,1) .45s 1 both;
}
@keyframes ut-sheen{from{transform:translateX(-140%)}to{transform:translateX(140%)}}
@media (prefers-reduced-motion:reduce){
  [data-card]{transition:none}
  [data-card][data-live]{transform:none}
  [data-card][data-live]::after{display:none}
}

/* 자동 순회도 위 data-live 를 그대로 쓴다 — 따로 반짝임을 만들지 않는다.
   전에는 순회 전용 tour-flash 를 뒀는데, 사람들이 아는 불(data-live)과 모양이 달라
   "임의로 하이라이트한다" 로 읽혔다(지적 받음). 순회는 그 불의 자리만 몰고 간다. */
