/* =====================================================================
   SAVER O2 IoT 관제센터 — 영업 시연용 데모 스타일
   제품(SaverO2IotControlCenter)의 디자인 토큰을 그대로 계승
   ===================================================================== */

:root{
  --bg:#eef1f6;
  --card:#ffffff;
  --line:#e6eaf1;
  --line-strong:#d7dce6;
  --text:#111827;
  --text-2:#374151;
  --muted:#6b7280;
  --muted-2:#94a3b8;

  --blue:#1070dd;
  --blue-2:#2563eb;
  --blue-soft:#7bbfff;
  --blue-bg:#eff6ff;
  --led:#7bbfff;

  --gray:#99a1af;
  --gray-2:#d1d5dc;
  --green:#16a34a;
  --amber:#f59e0b;
  --red:#ed4545;

  --shadow:0 2px 6px rgba(15,23,42,.08);
  --shadow-md:0 6px 18px rgba(15,23,42,.10);
  --shadow-lg:0 14px 38px rgba(15,23,42,.14);
  --radius:14px;
  --radius-sm:10px;
}

*{ box-sizing:border-box; }

html,body{ margin:0; padding:0; }

body{
  font-family:'Pretendard',system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  color:var(--text);
  background:
    radial-gradient(1100px 520px at 78% -8%, rgba(16,112,221,.10), transparent 60%),
    radial-gradient(820px 460px at 8% 4%, rgba(123,191,255,.12), transparent 60%),
    var(--bg);
  background-attachment:fixed;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

/* 세로 정렬(H): 존 한 개를 전체폭으로 위→아래 스택 */
.app{ max-width:1480px; margin:0 auto; padding:18px 26px 40px; }
/* 가로 정렬(V1·V2): 1·2·3층 존(컨트롤러 카드)을 좌우로 나란히 → 넓게 펴서 3개가 한 화면에 */
.app.app-v{ max-width:1880px; padding:18px 20px 40px; }
/* 3D 보기는 방 이미지 한 장이라 풀폭이면 너무 큼 → 가운데 정렬로 적당한 크기 유지 */
.app.app-3d{ max-width:1180px; }

a{ color:inherit; }

/* ====================== 상단 바 ====================== */
.topbar{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  background:#fff;
  border:1px solid var(--line);
  border-radius:14px;
  padding:14px 20px;
  box-shadow:var(--shadow);
  position:sticky; top:10px; z-index:50;
}

.brand{ display:flex; align-items:center; gap:14px; min-width:0; }
.brand-logo{ height:36px; width:auto; object-fit:contain; }
.brand-fallback{
  display:none; align-items:center; height:36px; padding:0 12px;
  font-weight:900; letter-spacing:.06em; color:var(--blue);
  background:var(--blue-bg); border-radius:8px; font-size:18px;
}
.brand-divider{ width:1px; height:28px; background:#e5e7eb; }
.brand-title{ font-size:18px; font-weight:700; letter-spacing:-.01em; }
.brand-sub{ font-size:12px; color:var(--muted); margin-top:2px; }

.topbar-right{ display:flex; align-items:center; gap:14px; flex-shrink:0; }

.conn-status{
  display:inline-flex; align-items:center; gap:7px;
  font-size:12.5px; font-weight:600; color:#15803d; white-space:nowrap;
}
.conn-dot{ width:8px; height:8px; border-radius:50%; background:var(--green); animation:livePulse 1.6s ease-in-out infinite; }

.topbar-clock{
  display:inline-flex; align-items:center; gap:7px;
  font-size:13px; font-weight:600; color:var(--text-2);
  padding:7px 12px; border-radius:8px; background:#f3f4f6;
  font-variant-numeric:tabular-nums;
}
.topbar-clock i{ color:var(--muted-2); }

.topbar-user{ display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:700; color:var(--text-2);
  padding:7px 11px; border-radius:8px; background:#f3f4f6; font-variant-numeric:tabular-nums; white-space:nowrap; }
.topbar-user i{ color:var(--muted-2); }
.topbar-logout{ display:inline-flex; align-items:center; gap:6px; font-family:inherit; font-size:12.5px; font-weight:700;
  color:#64748b; background:#fff; border:1px solid var(--line-strong); border-radius:8px; padding:7px 12px; cursor:pointer;
  white-space:nowrap; transition:all .12s ease; }
.topbar-logout:hover{ border-color:var(--red); color:var(--red); background:#fef2f2; }
.topbar-logout i{ font-size:12px; }

/* ====================== 요약 카드 ====================== */
.summary{
  display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin:16px 0;
}
.sum-card{
  background:var(--card); border-radius:var(--radius); padding:15px 18px;
  box-shadow:var(--shadow); display:flex; align-items:center; gap:14px;
  border:1px solid var(--line);
}
.sum-ic{
  width:44px; height:44px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center; font-size:17px; color:#fff;
}
.sum-ic.ctrl{ background:#475569; }
.sum-ic.unit{ background:var(--blue); }
.sum-ic.run { background:var(--green); }
.sum-ic.o2  { background:var(--blue); }
.sum-info{ display:flex; flex-direction:column; gap:3px; min-width:0; }
.sum-label{ font-size:12px; color:var(--muted); font-weight:600; }
.sum-val{ display:flex; align-items:baseline; gap:3px; }
.sum-num{ font-size:23px; font-weight:800; color:var(--text); font-variant-numeric:tabular-nums; line-height:1; }
.sum-unit{ font-size:13px; font-weight:600; color:var(--text-2); }

/* ====================== 전체 제어 ====================== */
.master{
  background:var(--card); border-radius:var(--radius); padding:14px 18px;
  box-shadow:var(--shadow); border:1px solid var(--line);
  display:flex; align-items:center; gap:22px; flex-wrap:wrap; margin-bottom:20px;
}
.master-title{ font-size:14px; font-weight:800; color:var(--text-2); display:flex; align-items:center; gap:8px; }
.master-title i{ color:var(--blue); }
.master-groups{ display:flex; gap:26px; flex-wrap:wrap; flex:1; }
.mgroup{ display:flex; align-items:center; gap:12px; }
.mgroup-label{ font-size:12.5px; font-weight:700; color:var(--muted); }
.mgroup-btns{ display:flex; gap:8px; }
.mbtn{
  border:none; cursor:pointer; font-weight:800; font-size:13px; letter-spacing:.02em;
  padding:9px 16px; border-radius:9px; color:#fff;
  box-shadow:0 1px 3px rgba(15,23,42,.14);
  transition:transform .08s ease, filter .12s ease, box-shadow .12s ease;
}
.mbtn:hover{ transform:translateY(-1px); filter:brightness(1.05); box-shadow:var(--shadow-md); }
.mbtn:active{ transform:translateY(1px) scale(.98); }
.mbtn.on{ background:var(--blue); }
.mbtn.off{ background:var(--gray); }
.mbtn.led-on{ background:var(--led); }
.mbtn.led-off{ background:var(--gray); }

/* 배치 선택 콤보박스 */
.layout-switch{ margin-left:auto; }
.layout-switch .mgroup-label{ display:inline-flex; align-items:center; gap:6px; }
.layout-switch .mgroup-label i{ color:var(--blue); }
.layout-select{
  font-family:inherit; font-size:13px; font-weight:700; color:var(--text-2);
  background:#fff; border:1px solid #d7dce6; border-radius:9px; padding:9px 32px 9px 12px;
  cursor:pointer; appearance:none; -webkit-appearance:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4.5l4 4 4-4' stroke='%23667085' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 11px center;
  transition:border-color .15s ease, box-shadow .15s ease;
}
.layout-select:hover{ border-color:var(--blue); }
.layout-select:focus{ outline:none; border-color:var(--blue); box-shadow:0 0 0 3px rgba(16,112,221,.12); }

/* ====================== 존 섹션 ====================== */
/* 모든 배치에서 1·2·3층 존은 전체폭으로 위→아래 스택. 존 내부 레이아웃만 모드별로 다름. */
.zone{
  background:var(--card); border-radius:12px; border:1px solid #e1e5ec;
  box-shadow:0 1px 2px rgba(15,23,42,.04); padding:16px 20px 20px; margin-bottom:16px;
  position:relative; overflow:hidden;
}
.zone::before{
  content:""; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--blue);
}

/* 가로 정렬(V1·V2): 1·2·3층 존(컨트롤러 카드)을 좌→우로 3개 나란히 배치 */
.zones.layout-v1, .zones.layout-v2{
  display:grid; grid-template-columns:repeat(3,1fr); gap:16px; align-items:start;
}
.zones.layout-v1 .zone, .zones.layout-v2 .zone{ margin-bottom:0; }
/* 가로 정렬에선 강조선을 왼쪽 → 상단으로 (존이 좌우로 나란히 서므로 윗변 강조가 자연스러움) */
.zones.layout-v1 .zone::before, .zones.layout-v2 .zone::before{
  left:0; right:0; top:0; bottom:auto; width:auto; height:3px;
}

.zone-head{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  padding-bottom:14px; margin-bottom:2px; border-bottom:1px solid #eef0f4;
  flex-wrap:wrap;
}
.zone-title{ display:flex; align-items:center; gap:13px; min-width:0; }
.zone-ic{
  width:42px; height:42px; border-radius:12px; flex-shrink:0;
  display:flex; align-items:center; justify-content:center; font-size:18px; color:#fff;
  background:#334155;
}
.zt-name{ font-size:17px; font-weight:800; letter-spacing:-.01em; display:inline-flex; align-items:center; gap:6px; }
.zt-sub{ font-size:12px; color:var(--muted); margin-top:2px; font-variant-numeric:tabular-nums; }

/* 이름 인라인 편집 — 연필(항상 은은히 보임·호버 시 강조) + 입력창 */
.name-edit{
  border:none; background:transparent; cursor:pointer; padding:2px 3px; line-height:1;
  color:var(--muted-2); font-size:10px; opacity:.4; border-radius:5px;
  transition:opacity .12s ease, color .12s ease, background .12s ease; flex:0 0 auto;
}
.zt-name:hover .name-edit, .ctrl-name:hover .name-edit, .cpop-head:hover .name-edit{ opacity:.85; }
.name-edit:hover{ opacity:1; color:var(--blue); background:rgba(16,112,221,.10); }
.cpop-head .name-edit{ font-size:9px; }
.name-input{
  font:inherit; font-weight:800; letter-spacing:-.01em; color:#111827;
  border:1.5px solid var(--blue); border-radius:6px; padding:1px 7px;
  width:180px; max-width:100%; min-width:7ch; background:#fff; outline:none;
  box-shadow:0 0 0 3px rgba(16,112,221,.14);
}
.cpop-head .name-input{ width:140px; font-size:12.5px; }
.zone-meta{ display:flex; align-items:center; gap:14px; }
.zmeta{ font-size:12.5px; font-weight:700; color:var(--text-2); display:inline-flex; align-items:center; gap:7px; }
.zmeta.comm{ color:#15803d; }
.zmeta .cdot{ width:8px; height:8px; border-radius:50%; background:var(--green); animation:livePulse 1.4s ease-in-out infinite; }
.zmeta.avg b{ font-size:15px; color:var(--blue); font-variant-numeric:tabular-nums; }

/* ── 토폴로지 [가로 버전] 컨트롤러(좌상) + 토출기 그룹(우상) → 실외기 가로 3열 ── */
.zone-topology{
  position:relative; display:grid;
  grid-template-columns: minmax(360px, 410px) 1fr;
  grid-template-areas: "ctrl dcg" "units units";
  align-items:start; gap:46px 90px; padding:16px 4px 6px;
}
.zone-topology > .controller-node{ grid-area:ctrl; align-self:start; }
.zone-topology > .discharger-group{ grid-area:dcg; align-self:stretch; }
.zone-topology > .units-row{ grid-area:units; }
.wires{
  position:absolute; inset:0; width:100%; height:100%;
  pointer-events:none; z-index:1; overflow:visible;
}
/* 토출기 그룹 연결선 (컨트롤러에 물려있음을 강조 — 굵은 선 + 흐르는 패킷) */
.wire-dcg-line{ fill:none; stroke:var(--blue); stroke-width:3; opacity:.7; stroke-linecap:round; }
.wire-dcg-dot{ fill:#fff; stroke:var(--blue); stroke-width:3.5; opacity:.95; }

/* ════════════════════════════════════════════════════════════════
   컨트롤러 카드 (개편) — 세 섹션: ① 신원  ② 라이브 상태  ③ 제어
   ════════════════════════════════════════════════════════════════ */
.controller-node{
  position:relative; z-index:2; align-self:start;
  display:flex; flex-direction:column; gap:13px;
  background:#eff5ff; border:1px solid #d4e4fb; border-radius:16px;
  padding:15px 16px 17px;
}

/* ── ① 신원: 장치 칩 + 이름/ID·존 + 온라인 배지 ── */
.ctrl-head{ display:flex; align-items:center; gap:12px; }
.ctrl-chip{
  position:relative; flex-shrink:0; width:54px; height:54px; border-radius:13px;
  background:#f3f7fd; border:1px solid #d4ddeb;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px;
}
.ctrl-chip .radar{
  position:absolute; left:50%; top:50%; width:54px; height:54px; border-radius:50%;
  border:1.5px solid rgba(16,112,221,.26);
  transform:translate(-50%,-50%) scale(.5); opacity:0;
  animation:radar 2.9s ease-out infinite; pointer-events:none;
}
.ctrl-chip .fa-tower-broadcast{ font-size:20px; color:var(--blue); }
.ctrl-chip .ctrl-signal{ display:flex; align-items:flex-end; gap:2.5px; height:10px; }
.ctrl-chip .ctrl-signal span{ width:3.5px; border-radius:2px; background:var(--blue-soft); }
.ctrl-chip .ctrl-signal span:nth-child(1){ height:4px; }
.ctrl-chip .ctrl-signal span:nth-child(2){ height:6px; }
.ctrl-chip .ctrl-signal span:nth-child(3){ height:8px; }
.ctrl-chip .ctrl-signal span:nth-child(4){ height:10px; background:var(--blue); animation:sigBlink 1.1s steps(1) infinite; }
.ctrl-chip-led{ position:absolute; right:6px; top:6px; width:8px; height:8px; border-radius:50%;
  background:var(--green); animation:ledBlink 1.6s ease-in-out infinite; }

.ctrl-idblock{ flex:1; min-width:0; }
.ctrl-name{ font-size:16px; font-weight:800; letter-spacing:-.01em; line-height:1.2; display:inline-flex; align-items:center; gap:6px; }
.ctrl-sub{ font-size:12px; color:var(--muted); margin-top:2px; font-variant-numeric:tabular-nums;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ctrl-online{ flex-shrink:0; display:inline-flex; align-items:center; gap:6px;
  font-size:12px; font-weight:700; color:#15803d;
  background:#e8f8ef; border:1px solid #c6efd6; border-radius:999px; padding:4px 10px; white-space:nowrap; }
.ctrl-online .dot{ width:7px; height:7px; border-radius:50%; background:var(--green); animation:livePulse 1.5s ease-in-out infinite; }

/* ── ② 라이브 상태: 라벨 위·값 아래 3칸 스트립(셀 구분선) ── */
.ctrl-stats{ display:grid; grid-template-columns:repeat(3,1fr);
  background:#fff; border:1px solid #e1e9f5; border-radius:11px; overflow:hidden; }
.cstat{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:4px; padding:9px 12px; min-width:0; }
.cstat + .cstat{ border-left:1px solid #eaf0f9; }
.cstat-k{ font-size:11px; color:var(--muted); font-weight:600; display:inline-flex; align-items:center; gap:5px; white-space:nowrap; }
.cstat-k i{ color:var(--muted-2); font-size:10px; }
.cstat-v{ font-size:13.5px; font-weight:800; color:var(--text-2); font-variant-numeric:tabular-nums;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cstat-v.is-idle{ color:var(--muted-2); font-weight:700; }
.ctrl-timer .cstat-v b{ color:var(--blue); font-weight:800; }
/* 클릭형 셀 (예약/타이머 설정) — 호버 시 연한 배경 + 연필 아이콘 */
.cstat-btn{ font-family:inherit; border:none; background:transparent; cursor:pointer; transition:background .12s ease; }
.cstat-btn:hover{ background:#eef5ff; }
.cstat-btn:focus-visible{ outline:2px solid var(--blue); outline-offset:-2px; }
.cstat-edit{ font-size:8.5px !important; color:var(--muted-2) !important; opacity:0; transition:opacity .12s ease; }
.cstat-btn:hover .cstat-edit{ opacity:.85; }

/* ── ③ 제어: 주요 토글 2개 + 보조(공간 시뮬레이터) ── */
.ctrl-controls{ display:flex; flex-direction:column; gap:8px; }
.ctrl-actions{ display:flex; gap:8px; }
.ctrl-toggle{
  flex:1; border:none; cursor:pointer; font-weight:800; font-size:13px; letter-spacing:-.01em; color:#fff;
  padding:11px 12px; border-radius:10px; background:var(--gray); box-shadow:none; white-space:nowrap;
  display:inline-flex; align-items:center; justify-content:center;
  transition:filter .12s ease, transform .08s ease, background .15s ease;
}
.ctrl-toggle.is-on{ background:var(--blue); box-shadow:0 2px 8px rgba(16,112,221,.26); }
.ctrl-toggle:hover{ filter:brightness(1.06); }
.ctrl-toggle:active{ transform:scale(.98); }
.ctrl-ledbtn{
  flex:1; border:1px solid #d7dce6; cursor:pointer; font-weight:800; font-size:13px; letter-spacing:-.01em; white-space:nowrap;
  color:#64748b; background:#fff; border-radius:10px; padding:11px 12px;
  display:inline-flex; align-items:center; justify-content:center; gap:6px; transition:all .12s ease;
}
.ctrl-ledbtn i{ font-size:12px; }
.ctrl-ledbtn:hover{ border-color:var(--blue); }
.ctrl-ledbtn.is-active{ background:var(--led); border-color:var(--led); color:#fff; }
.ctrl-sim{
  width:100%; border:1px solid #cfdcf2; background:#f3f8ff; color:#1d6fd0; cursor:pointer;
  font-weight:700; font-size:12.5px; padding:9px 12px; border-radius:10px;
  display:inline-flex; align-items:center; justify-content:center; gap:7px; transition:all .12s ease;
}
.ctrl-sim i{ font-size:12px; }
.ctrl-sim:hover{ background:#e7f1ff; border-color:var(--blue); transform:translateY(-1px); }

.ctrl-port{
  position:absolute; left:50%; bottom:0; transform:translate(-50%,50%);
  width:14px; height:14px; border-radius:50%;
  background:#fff; border:3px solid var(--blue);
  box-shadow:0 0 0 4px rgba(16,112,221,.14);
  z-index:3;
}

/* ── 실외기 행 (가로 3열) ── */
.units-row{
  width:100%; display:grid; grid-template-columns:repeat(3,1fr); gap:16px;
  z-index:2; position:relative;
}

.unit-card{
  position:relative; background:#fff; border:1px solid #e3e6ec;
  border-radius:12px; box-shadow:0 1px 2px rgba(15,23,42,.03);
  display:flex; flex-direction:column; gap:8px;
  padding:12px 14px; overflow:hidden;
  transition:border-color .2s ease, box-shadow .2s ease;
}
.unit-card.running{ border-color:#bcd6f6; box-shadow:0 3px 12px rgba(16,112,221,.10); }
.unit-card.pulse{ animation:cardPulse .55s ease; }

.unit-port{
  position:absolute; left:50%; top:0; transform:translate(-50%,-50%);
  width:13px; height:13px; border-radius:50%;
  background:#fff; border:3px solid var(--gray-2); z-index:5;
  transition:border-color .2s ease, box-shadow .2s ease;
}
.unit-card.running .unit-port{ border-color:var(--blue); box-shadow:0 0 0 4px rgba(16,112,221,.12); }

/* ── 카드 헤더 (상태 + ID) ── */
.unit-cardhead{ display:flex; align-items:center; justify-content:space-between; gap:8px; }

/* ── 실외기 일러스트 ── */
.unit-illust{ position:relative; align-self:center; width:98px; height:64px; display:flex; align-items:flex-end; justify-content:center; }
.unit-machine{
  position:relative; width:86px; height:58px; border-radius:8px;
  background:#eef1f6;
  border:1px solid #d4d9e2;
  box-shadow:none;
  overflow:hidden;
}
.unit-machine::before{ /* 상단 흡기 슬릿 */
  content:""; position:absolute; left:10px; right:10px; top:8px; height:3px; border-radius:3px;
  background:repeating-linear-gradient(90deg,#c4ccd9 0 7px,transparent 7px 11px);
  opacity:.7;
}
.unit-feet{ position:absolute; left:50%; bottom:-6px; transform:translateX(-50%); width:96px; height:6px; }
.unit-feet::before,.unit-feet::after{ content:""; position:absolute; bottom:0; width:14px; height:6px; border-radius:0 0 3px 3px; background:#9aa6b6; }
.unit-feet::before{ left:6px; } .unit-feet::after{ right:6px; }

/* 팬 */
.unit-fan{ position:absolute; left:50%; top:52%; transform:translate(-50%,-50%); width:46px; height:46px; }
.grille-face{ fill:#eef2f7; stroke:#cdd6e2; stroke-width:1.5; }
.fan-blades{ transform-origin:50px 50px; animation:fanSpin 2.6s linear infinite; animation-play-state:paused; }
.unit-card.running .fan-blades{ animation-play-state:running; animation-duration:1.1s; }
.fan-blades ellipse{ fill:#c2ccd9; }
.unit-card.running .fan-blades ellipse{ fill:#6aa6ea; }
.grille{ fill:none; }
.grille circle, .grille line{ fill:none; stroke:#aeb9c9; stroke-width:1.4; stroke-linecap:round; }
.fan-hub{ fill:#8593a4; }
.unit-card.running .fan-hub{ fill:#1070dd; }

/* 상태 LED */
.unit-statuslight{
  position:absolute; right:9px; top:8px; width:8px; height:8px; border-radius:50%;
  background:#b8c2d0; z-index:3;
}
.unit-card.running .unit-statuslight{ background:var(--green); box-shadow:0 0 7px rgba(34,197,94,.8); animation:ledBlink 1.5s ease-in-out infinite; }

/* LED 언더글로우 */
.unit-underglow{
  position:absolute; left:8px; right:8px; bottom:5px; height:7px; border-radius:7px;
  background:var(--led); opacity:0; filter:blur(3px); transition:opacity .25s ease; z-index:2;
}
.unit-card.led-on .unit-underglow{ opacity:.9; animation:ledGlow 2.2s ease-in-out infinite; }
.unit-machine .led-bar{
  position:absolute; left:14px; right:14px; bottom:7px; height:3px; border-radius:3px;
  background:#cdd6e3; transition:background .25s ease, box-shadow .25s ease; z-index:3;
}
.unit-card.led-on .unit-machine .led-bar{ background:var(--led); box-shadow:0 0 8px rgba(123,191,255,.9); }

/* 산소 버블 */
.bubbles{ position:absolute; left:0; right:0; bottom:40px; height:44px; pointer-events:none; z-index:4; opacity:0; transition:opacity .3s ease; }
.unit-card.running .bubbles{ opacity:1; }
.bubble{ position:absolute; bottom:0; width:7px; height:7px; border-radius:50%;
  background:radial-gradient(circle at 35% 30%, rgba(255,255,255,.95), rgba(123,191,255,.55) 60%, rgba(16,112,221,.25));
  border:1px solid rgba(16,112,221,.18);
  animation:bubbleRise 2.6s ease-in infinite; }
.bubble:nth-child(1){ left:24%; width:6px; height:6px; animation-delay:0s; }
.bubble:nth-child(2){ left:42%; width:9px; height:9px; animation-delay:.5s; }
.bubble:nth-child(3){ left:56%; width:5px; height:5px; animation-delay:1s; }
.bubble:nth-child(4){ left:68%; width:8px; height:8px; animation-delay:1.5s; }
.bubble:nth-child(5){ left:35%; width:6px; height:6px; animation-delay:2s; }

/* 상태 칩 (작동중/정지) */
.unit-state{ display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:800;
  color:#64748b; background:#eef1f5; border:1px solid #e3e6ec; padding:4px 10px 4px 8px; border-radius:999px; }
.unit-state .sdot{ width:7px; height:7px; border-radius:50%; background:var(--gray); }
.unit-card.running .unit-state{ color:#0e8a4f; background:#e8f8ef; border-color:#c6efd6; }
.unit-card.running .unit-state .sdot{ background:var(--green); animation:livePulse 1.3s ease-in-out infinite; }

/* ID 라벨 */
.unit-tag{ font-size:11px; font-weight:700; color:#64748b; background:#f1f3f6; border:1px solid #e3e6ec;
  padding:3px 7px; border-radius:5px; display:inline-flex; align-items:center; gap:5px; flex-shrink:0;
  letter-spacing:.02em; }
.unit-tag i{ color:var(--muted-2); font-size:10px; }

/* 이름 */
.unit-name{ font-size:13.5px; font-weight:800; color:var(--text); text-align:center;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }

/* 산소순도 박스 */
.unit-o2box{ background:#f6f7f9; border:1px solid #ebedf1; border-radius:10px; padding:9px 12px;
  display:flex; flex-direction:column; gap:6px; transition:background .25s ease, border-color .25s ease; }
.unit-card.running .unit-o2box{ background:#eef5ff; border-color:#d3e4fb; }
.unit-o2head{ display:flex; align-items:baseline; justify-content:space-between; gap:8px; }
.unit-o2-label{ font-size:12px; font-weight:700; color:var(--muted); }
.unit-o2{ display:flex; align-items:baseline; gap:1px; line-height:1; color:var(--gray); transition:color .3s ease; }
.unit-o2 b{ font-size:24px; font-weight:800; font-variant-numeric:tabular-nums; }
.unit-o2 i{ font-size:12px; font-weight:700; font-style:normal; }
.unit-card.running .unit-o2{ color:var(--blue); }
.unit-o2.bump b{ animation:numBump .45s ease; }

.progress{ position:relative; width:100%; height:8px; border-radius:999px; background:#e4e8ee; overflow:hidden; }
.progress-fill{ position:absolute; inset:0; border-radius:inherit; background:var(--gray-2);
  transform-origin:left center; transform:scaleX(0);
  transition:transform .6s cubic-bezier(.4,0,.2,1), background .3s ease; }
.unit-card.running .progress-fill{ background:linear-gradient(90deg,#0a5fd6,#46a8ff); }

.unit-meta{ display:flex; align-items:center; justify-content:center; gap:14px; font-size:11.5px;
  color:var(--muted); font-variant-numeric:tabular-nums; }
.unit-meta .um{ display:inline-flex; align-items:center; gap:6px; }
.unit-meta i{ color:var(--muted-2); }
.unit-meta b{ font-weight:700; color:var(--text-2); }

/* ── 실외기 제어 버튼 (가로 3개) ── */
.unit-ctrl{ display:flex; gap:7px; margin-top:1px; }
.unit-ctrl .ubtn{ flex:1; }
.ubtn{
  border:none; cursor:pointer; font-size:12.5px; font-weight:800; letter-spacing:.02em;
  padding:8px 10px; border-radius:8px; color:#fff; background:var(--gray);
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  box-shadow:0 1px 2px rgba(15,23,42,.12);
  transition:transform .08s ease, filter .12s ease, opacity .12s ease;
}
.ubtn:hover{ filter:brightness(1.06); transform:translateY(-1px); }
.ubtn:active{ transform:translateY(1px) scale(.98); }
.ubtn.on.is-active{ background:var(--blue); }
.ubtn.off.is-active{ background:var(--gray); }
.ubtn.led{ background:#cbd5e1; color:#475569; }
.ubtn.led.is-active{ background:var(--led); color:#fff; }
.ubtn.is-idle{ background:#e5e9f0; color:#9aa3b2; box-shadow:none; }
.ubtn i{ font-size:11px; }

/* ── pending 오버레이 (제품 동작 재현) ── */
.unit-overlay{
  position:absolute; inset:0; border-radius:14px; z-index:8;
  background:rgba(15,23,42,.52); backdrop-filter:blur(2px);
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  color:#fff; opacity:0; visibility:hidden; transition:opacity .15s ease, visibility .15s ease;
}
.unit-overlay.show{ opacity:1; visibility:visible; }
.ovl-spin{ width:26px; height:26px; border-radius:50%;
  border:3px solid rgba(255,255,255,.35); border-top-color:#fff; animation:spin .8s linear infinite; }
.unit-overlay span{ font-size:12.5px; font-weight:700; letter-spacing:.04em; }

/* ============================================================
   가로 정렬(H · 컨트롤러 위)은 기존 UI 그대로 — 별도 오버라이드 없음.
   존 가로 배치(V): 1·2·3층 존을 가로 3열로 나란히 두고,
   한 존 안에서는 컨트롤러를 위(가로 바)에, 실외기 3대를 아래로 세로 스택.
   제어는 컨트롤러 한 곳(전체 작동/중지 + 전체 LED)에서.
   ============================================================ */
/* 공통 (V1·V2): 실외기는 '세로 스택'(위→아래). 토출기 그룹 세로 박스.
   ── ver.1: 컨트롤러(위)+실외기(아래) 좌측, 토출기 우측 → 컨트롤러 하단에서 왼쪽 버스로 분기
   ── ver.2(시안): 컨트롤러(좌상)+토출기(좌하) 좌측, 실외기 우측 → 컨트롤러 우측에서 실외기로 분기 */
.layout-v1 .units-row, .layout-v2 .units-row{ display:flex; flex-direction:column; gap:12px; }
.layout-v1 .units-row{ padding-left:36px; }   /* 좌측 세로 버스 거터 */
.layout-v2 .units-row{ padding-left:14px; }    /* 버스는 컬럼 간격에 위치 */
.layout-v1 .discharger-group, .layout-v2 .discharger-group{ justify-content:center; gap:16px; padding:18px 10px; }
.layout-v1 .dcg-header, .layout-v2 .dcg-header{ flex-direction:column; align-items:center; gap:2px; }
.layout-v1 .dcg-types, .layout-v2 .dcg-types{ flex-direction:column; align-items:center; gap:18px; }
.layout-v1 .dcg-illusts, .layout-v2 .dcg-illusts{ justify-content:center; gap:10px; }
.layout-v1 .dcg-title, .layout-v2 .dcg-title{ font-size:17px; }
.layout-v1 .controller-node, .layout-v2 .controller-node{ align-self:start; }
/* 와이어 포트: V1=컨트롤러 하단중앙 / V2=컨트롤러 우측중앙. 실외기는 둘 다 좌측중앙 진입 */
.layout-v1 .ctrl-port{ left:50%; right:auto; top:auto; bottom:0; transform:translate(-50%,50%); }
.layout-v2 .ctrl-port{ left:auto; right:0; top:50%; bottom:auto; transform:translate(50%,-50%); }
.layout-v1 .unit-port, .layout-v2 .unit-port{ left:0; top:50%; right:auto; transform:translate(-50%,-50%); }

/* ── ver.1: 토출기 그룹 우측 ── 좌: 컨트롤러(위)+실외기 가로 3열(아래) | 우: 토출기 세로 박스 */
.layout-v1 .zone-topology{
  display:grid; grid-template-columns: minmax(0,2.2fr) minmax(0,1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas: "ctrl dcg" "units dcg";
  align-items:start; gap:20px 58px; padding:14px 4px 6px;
}
.layout-v1 .zone-topology > .controller-node{ grid-area:ctrl; }
.layout-v1 .zone-topology > .units-row{ grid-area:units; }
.layout-v1 .zone-topology > .discharger-group{ grid-area:dcg; align-self:stretch; }

/* ── ver.2: 토출기 그룹 좌측 ── 좌: 토출기 세로 박스 | 우: 컨트롤러(위)+실외기 가로 3열(아래) */
.layout-v2 .zone-topology{
  display:grid; grid-template-columns: minmax(0,1.18fr) minmax(0,1fr);
  grid-template-rows: auto 1fr;
  grid-template-areas: "ctrl units" "dcg units";
  align-items:start; gap:20px 50px; padding:14px 4px 6px;
}
.layout-v2 .zone-topology > .controller-node{ grid-area:ctrl; }
.layout-v2 .zone-topology > .discharger-group{ grid-area:dcg; align-self:stretch; }
.layout-v2 .zone-topology > .units-row{ grid-area:units; }

/* ============================================================
   3D 아이소메트릭 입체 보기 (layout-3d)
   - 코드로 그린 플레이스홀더 씬 + 빛나는 파이프/산소 분사
   - 디자인팀 3D 렌더 이미지는 .scene-render 로 교체 가능
   ============================================================ */
.scene-3d{ position:relative; width:100%; margin:10px 0 2px; }
.scene-svg{ width:100%; height:auto; display:block; overflow:visible; }

/* 매장 구조 PNG 배경 (세로 합성본이면 방 렌더 부분만 크롭). 연결선 모션은 .scene-flow 로 위에 얹음 */
.scene-render{ width:100%; aspect-ratio:1000/650; object-fit:cover; object-position:center top;
  display:block; border-radius:14px; background:#eef2f7; }
.scene-3d.render-fail .scene-render{ visibility:hidden; }

/* ── 연결선 위 '움직이는 모션' (구조 PNG 위에 코드로 그림 → 항상 선명) ── */
.scene-flow{ position:absolute; inset:0; width:100%; height:100%; pointer-events:none; z-index:5; overflow:visible; }
.sflow-ref{ fill:none; stroke:none; }
/* 베이스 선 (PNG에 선이 없을 때만; SCENE_LINE_IN_PNG=true 면 코드에서 미출력) */
.sflow-glow{ fill:none; stroke-width:9; stroke-linecap:round; stroke-linejoin:round; }
.sflow-glow.on{ stroke:#37c0ff; opacity:.30; }
.sflow-glow.off{ stroke:#9aa6b6; opacity:.18; }
.sflow-core{ fill:none; stroke-width:2.6; stroke-linecap:round; stroke-linejoin:round; }
.sflow-core.on{ stroke:#e2f3ff; opacity:.95; }
.sflow-core.off{ stroke:#cdd6e3; opacity:.65; }
/* 선을 따라 흐르는 빛 (작동 중) */
.sflow-dash{ fill:none; stroke:#ffffff; stroke-width:2.6; stroke-linecap:round;
  stroke-dasharray:2 13; animation:dashFlow3d 1s linear infinite; }
/* 흐르는 패킷 (정/역 양방향) */
.sflow-packet .core{ fill:#ffffff; }
.sflow-packet .halo{ fill:rgba(55,192,255,.50); }
.sflow-packet.rev .core{ fill:#bfe7ff; }
.sflow-packet.rev .halo{ fill:rgba(123,191,255,.42); }

/* 렌더 이미지 위 라이트 스윕 (정지 렌더에 생동감) */
.scene-fx{ position:absolute; inset:0; display:none; pointer-events:none; border-radius:14px; overflow:hidden; }
.scene-3d.has-render .scene-fx{ display:block; }
.scene-fx::before{ content:""; position:absolute; top:-25%; left:-30%; width:60%; height:150%;
  background:linear-gradient(115deg, transparent 42%, rgba(120,200,255,.16) 50%, transparent 58%);
  animation:sceneSweep 5s linear infinite; }
@keyframes sceneSweep{ 0%{ transform:translateX(-40%); } 100%{ transform:translateX(280%); } }

/* 빛나는 파이프 */
.pipe-glow{ fill:none; stroke:#37c0ff; stroke-width:11; stroke-linecap:round; stroke-linejoin:round; opacity:.55; filter:url(#glow3d); }
.pipe-core{ fill:none; stroke:#bfe7ff; stroke-width:3.6; stroke-linecap:round; stroke-linejoin:round; }
.pipe-flow{ fill:none; stroke:#ffffff; stroke-width:3.4; stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:1.5 15; animation:dashFlow3d 1s linear infinite; }
@keyframes dashFlow3d{ to{ stroke-dashoffset:-16.5; } }

/* 파이프 위 흐르는 패킷 */
.scene-packet .core{ fill:#ffffff; }
.scene-packet .halo{ fill:rgba(55,192,255,.45); }
.scene-packet.rev .core{ fill:#bfe7ff; }
.scene-packet.rev .halo{ fill:rgba(123,191,255,.40); }

/* 산소 분사 (작동 중인 유닛만 .on) */
.o2glow{ opacity:0; transition:opacity .5s ease; }
.o2glow.on{ opacity:1; }
.o2glow .rays{ animation:o2pulse 2.8s ease-in-out infinite; transform-box:fill-box; transform-origin:center bottom; }
@keyframes o2pulse{ 0%,100%{ opacity:.45; } 50%{ opacity:1; } }

/* 3D 모드: 카드는 화면에서 숨기고(데이터/로직용으로 DOM엔 유지) GIF 위 핀으로만 제어 */
.layout-3d .units-row{ display:none; }
.scene-3d{ margin-bottom:6px; }

/* ── GIF 위 유닛 제어 핀 + 팝오버 ── */
.scene-pins{ position:absolute; inset:0; pointer-events:none; z-index:6; }
.scene-pin-wrap{ position:absolute; transform:translate(-50%,-50%); pointer-events:auto; }

.scene-pin{
  display:inline-flex; align-items:center; gap:5px; cursor:pointer;
  background:rgba(255,255,255,.94); border:1px solid #d3dcea; border-radius:999px;
  padding:4px 9px 4px 6px; box-shadow:0 4px 12px rgba(15,23,42,.20);
  font-family:inherit; font-weight:800; font-size:12px; color:#64748b;
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
  transition:transform .1s ease, box-shadow .15s ease; line-height:1;
}
.scene-pin:hover{ transform:translateY(-1px); box-shadow:0 6px 16px rgba(15,23,42,.26); }
.scene-pin .pin-dot{ width:9px; height:9px; border-radius:50%; background:#9aa6b6; flex-shrink:0; }
.scene-pin .pin-name{ color:#374151; max-width:92px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.scene-pin .pin-o2{ color:#111827; font-variant-numeric:tabular-nums; margin-left:1px; }
.scene-pin .pin-pct{ font-size:10px; color:#94a3b8; margin-left:-2px; }
.scene-pin-wrap.running .scene-pin{ color:#0e6fd0; border-color:#bcd9fb; background:rgba(239,246,255,.96); }
.scene-pin-wrap.running .pin-name{ color:#0e6fd0; }
.scene-pin-wrap.running .pin-o2{ color:#1070dd; }
.scene-pin-wrap.running .pin-dot{ background:#16a34a; box-shadow:0 0 0 0 rgba(22,163,74,.5); animation:pinPulse 1.7s ease-out infinite; }
@keyframes pinPulse{ 0%{ box-shadow:0 0 0 0 rgba(22,163,74,.5);} 100%{ box-shadow:0 0 0 9px rgba(22,163,74,0);} }

/* 전송 중: 상태 점 → 스피너 */
.scene-pin-wrap.pending .scene-pin{ pointer-events:none; }
.scene-pin-wrap.pending .pin-dot{ width:11px; height:11px; background:transparent !important;
  border:2px solid rgba(16,112,221,.3); border-top-color:#1070dd; animation:spin .7s linear infinite; box-shadow:none; }

/* 연결선 (핀 → 디퓨저 지점 느낌의 작은 꼬리) */
.scene-pin-wrap::after{
  content:""; position:absolute; left:50%; top:calc(50% + 11px); width:1px; height:14px;
  background:linear-gradient(#9aa6b6, transparent); transform:translateX(-50%); opacity:.5; pointer-events:none;
}
.scene-pin-wrap.running::after{ background:linear-gradient(#1070dd, transparent); opacity:.6; }

.scene-pop{
  position:absolute; bottom:calc(100% + 10px); left:50%; transform:translateX(-50%) scale(.95);
  width:208px; background:#fff; border:1px solid #e3e6ec; border-radius:13px;
  box-shadow:0 16px 38px rgba(15,23,42,.24); padding:12px 13px;
  opacity:0; visibility:hidden; transition:opacity .14s ease, transform .14s ease; z-index:20;
}
.scene-pin-wrap.open .scene-pop{ opacity:1; visibility:visible; transform:translateX(-50%) scale(1); }
.scene-pop::after{ content:""; position:absolute; top:100%; left:50%; transform:translateX(-50%);
  border:7px solid transparent; border-top-color:#fff; filter:drop-shadow(0 1px 0 #e3e6ec); }

.pop-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:9px; }
.pop-name{ font-size:13.5px; font-weight:800; color:#111827; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.pop-code{ font-size:10px; font-weight:700; color:#64748b; background:#f1f3f6; border:1px solid #e3e6ec;
  padding:2px 6px; border-radius:5px; display:inline-flex; align-items:center; gap:4px; flex-shrink:0; }
.pop-code i{ color:#94a3b8; font-size:9px; }

.pop-o2row{ display:flex; align-items:flex-end; justify-content:space-between; gap:8px; }
.pop-state-chip{ display:inline-flex; align-items:center; gap:5px; font-size:11.5px; font-weight:800;
  color:#64748b; background:#eef1f5; border:1px solid #e3e6ec; padding:3px 9px 3px 7px; border-radius:999px; }
.pop-state-chip .sdot{ width:6px; height:6px; border-radius:50%; background:#9aa6b6; }
.scene-pin-wrap.running .pop-state-chip{ color:#0e8a4f; background:#e8f8ef; border-color:#c6efd6; }
.scene-pin-wrap.running .pop-state-chip .sdot{ background:#16a34a; }
.pop-o2{ display:flex; align-items:baseline; gap:2px; line-height:1; color:#9aa6b6; }
.pop-o2-label{ font-size:10px; font-weight:700; color:#94a3b8; margin-right:3px; }
.pop-o2 b{ font-size:23px; font-weight:800; font-variant-numeric:tabular-nums; }
.pop-o2 i{ font-size:12px; font-weight:700; font-style:normal; }
.scene-pin-wrap.running .pop-o2{ color:#1070dd; }

.pop-prog{ position:relative; width:100%; height:7px; border-radius:999px; background:#e4e8ee; overflow:hidden; margin:8px 0; }
.pop-fill{ position:absolute; inset:0; border-radius:inherit; background:#d1d5dc; transform-origin:left center; transform:scaleX(0);
  transition:transform .6s cubic-bezier(.4,0,.2,1), background .3s ease; }
.scene-pin-wrap.running .pop-fill{ background:linear-gradient(90deg,#0a5fd6,#46a8ff); }

.pop-meta{ display:flex; align-items:center; justify-content:space-between; gap:8px;
  font-size:11px; color:#6b7280; font-variant-numeric:tabular-nums; margin-bottom:10px; }
.pop-meta i{ color:#94a3b8; }
.pop-meta b{ font-weight:700; color:#374151; }

.pop-btns{ display:flex; gap:6px; }
.pop-btns .ubtn{ flex:1; padding:8px 0; font-size:12px; }
.pop-btns .ubtn.led{ flex:0 0 56px; }

/* ── 실외기 상태 배지 (표시 전용) ── */
.scene-badge{
  position:absolute; transform:translate(-50%,-50%); pointer-events:none; white-space:nowrap;
  display:inline-flex; align-items:center; gap:5px; line-height:1;
  background:rgba(255,255,255,.94); border:1px solid #d3dcea; border-radius:999px;
  padding:4px 9px 4px 6px; box-shadow:0 4px 12px rgba(15,23,42,.18);
  font-weight:800; font-size:12px; color:#64748b;
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
}
.scene-badge .bdot{ width:8px; height:8px; border-radius:50%; background:#9aa6b6; flex-shrink:0; }
.scene-badge .bname{ color:#374151; max-width:96px; overflow:hidden; text-overflow:ellipsis; }
.scene-badge .bo2{ color:#111827; font-variant-numeric:tabular-nums; margin-left:1px; }
.scene-badge .bpct{ font-size:10px; color:#94a3b8; margin-left:-1px; }
.scene-badge.running{ border-color:#bcd9fb; background:rgba(239,246,255,.96); }
.scene-badge.running .bname{ color:#0e6fd0; }
.scene-badge.running .bo2{ color:#1070dd; }
.scene-badge.running .bdot{ background:#16a34a; box-shadow:0 0 0 0 rgba(22,163,74,.5); animation:pinPulse 1.7s ease-out infinite; }

/* ── 컨트롤러 핀 (제어는 여기 한 곳) ── */
.scene-pin.ctrl{ background:#1070dd; color:#fff; border-color:#1d6fd0; padding:6px 12px 6px 10px;
  box-shadow:0 6px 16px rgba(16,112,221,.42); animation:ctrlPulse 2.2s ease-out infinite; }
.scene-pin.ctrl i{ font-size:12px; }
.scene-pin.ctrl .pin-name{ color:#fff; }
.scene-pin.ctrl:hover{ filter:brightness(1.05); }
@keyframes ctrlPulse{
  0%{ box-shadow:0 6px 16px rgba(16,112,221,.42), 0 0 0 0 rgba(16,112,221,.5); }
  70%{ box-shadow:0 6px 16px rgba(16,112,221,.42), 0 0 0 13px rgba(16,112,221,0); }
  100%{ box-shadow:0 6px 16px rgba(16,112,221,.42), 0 0 0 0 rgba(16,112,221,0); }
}

/* ── 컨트롤러 제어 패널 (아래로 펼침) ── */
.ctrl-pop{ bottom:auto; top:calc(100% + 12px); width:248px; padding:12px 13px; }
.ctrl-pop::after{ top:auto; bottom:100%; border-top-color:transparent; border-bottom-color:#fff;
  filter:drop-shadow(0 -1px 0 #e3e6ec); }
.cpop-head{ display:flex; align-items:center; gap:7px; font-size:12.5px; font-weight:800; color:#111827;
  padding-bottom:9px; margin-bottom:9px; border-bottom:1px solid #eef0f4; }
.cpop-head i{ color:#1070dd; }
.cpop-sub{ margin-left:auto; font-size:11px; font-weight:700; color:#94a3b8; }
.cpop-rows{ display:flex; flex-direction:column; gap:9px; }
.ctrl-row{ display:flex; align-items:center; gap:9px; }
.ctrl-row .cr-dot{ width:8px; height:8px; border-radius:50%; background:#9aa6b6; flex-shrink:0; }
.ctrl-row.running .cr-dot{ background:#16a34a; box-shadow:0 0 0 0 rgba(22,163,74,.5); animation:pinPulse 1.7s ease-out infinite; }
.ctrl-row .cr-name{ font-size:12.5px; font-weight:700; color:#374151; flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ctrl-row .cr-o2{ font-size:13px; font-weight:800; color:#9aa6b6; font-variant-numeric:tabular-nums; flex-shrink:0; }
.ctrl-row.running .cr-o2{ color:#1070dd; }

/* 제어: 버튼 하나로 전체 작동/중지 + 전체 LED */
.cpop-actions{ display:flex; gap:7px; margin-top:11px; padding-top:11px; border-top:1px solid #eef0f4; }
.cpop-toggle{ flex:1; border:none; cursor:pointer; font-weight:800; font-size:13px; color:#fff;
  padding:10px 0; border-radius:9px; background:var(--blue); box-shadow:0 2px 6px rgba(16,112,221,.25);
  transition:filter .12s ease, transform .08s ease; }
.cpop-toggle:hover{ filter:brightness(1.06); }
.cpop-toggle:active{ transform:scale(.98); }
.cpop-toggle.is-stop{ background:var(--gray); box-shadow:none; }
.cpop-led{ flex:0 0 66px; border:1px solid #d7dce6; cursor:pointer; font-weight:800; font-size:12px;
  color:#64748b; background:#fff; border-radius:9px; display:inline-flex; align-items:center; justify-content:center; gap:5px;
  transition:all .12s ease; }
.cpop-led i{ font-size:12px; }
.cpop-led:hover{ border-color:var(--blue); }
.cpop-led.is-active{ background:var(--led); border-color:var(--led); color:#fff; }

/* ====================================================================
   ver.2 수정서 — 토출기 일러스트 · 실외기 카드 개편 · 컨트롤러 개편
   ==================================================================== */

/* ── 토출기 일러스트 (코드로 그린 SAVER 토출기 · 실외기와 동일한 단일 회색) ── */
.discharger{ display:block; overflow:visible; }
.discharger .dc-body{ fill:#ffffff; stroke:#cfd6e0; stroke-width:1.4; }
.discharger.round .dc-body{ filter:drop-shadow(0 2px 4px rgba(15,23,42,.16)); }
.discharger.pill  .dc-body{ filter:drop-shadow(0 2px 4px rgba(15,23,42,.14)); }
.discharger .dc-ring{ fill:none; stroke:#22d3ee; stroke-width:2.6; opacity:.55; }            /* 천장형 시안 액센트 링 */
.discharger .dc-slits line{ stroke:#aeb9c9; stroke-width:1.9; stroke-linecap:round; }        /* 천장형 빗살 구멍 */
.discharger .dc-grilleline{ fill:none; stroke:#cdd6e2; stroke-width:1.3; }                   /* 천장형 그릴 외곽선 */
.discharger .dc-window{ fill:none; stroke:#22d3ee; stroke-width:2.2; opacity:.7; }           /* 비타에어 시안 창 */
.discharger .dc-fan ellipse{ fill:#c2ccd9; }                                                  /* 비타에어 팬(회색 단일색) */
.discharger .dc-hub{ fill:#8593a4; }
.discharger .dc-logo{ fill:#aeb6c2; font-family:inherit; font-size:8px; font-weight:800; letter-spacing:.4px; }
.discharger.round.mini{ width:44px; height:44px; }
.discharger.pill.mini{ width:27px; height:44px; }
.discharger.round.big{ width:48px; height:48px; }
.discharger.pill.big{ width:54px; height:88px; }

/* ── 토출기 그룹 박스 (기획안: 벽걸이 2대 / 천정형 4대 — 타입별 묶음 + 개수 라벨) ── */
.discharger-group{ display:flex; flex-direction:column; align-items:center; gap:14px;
  background:linear-gradient(180deg,#fbfdff,#f2f6fc); border:1.5px solid #cfe0f6; border-radius:20px;
  padding:16px 24px; }
.dcg-header{ display:flex; align-items:baseline; justify-content:center; flex-wrap:wrap; gap:4px 10px; }
.dcg-title{ font-size:20px; font-weight:800; color:#1d3a63; letter-spacing:-.01em; white-space:nowrap; }
.dcg-total{ font-size:12.5px; color:#7c8694; font-weight:700; white-space:nowrap; }
.dcg-types{ display:flex; flex-wrap:wrap; align-items:flex-end; justify-content:center; gap:18px 30px; }
.dcg-type{ display:flex; flex-direction:column; align-items:center; gap:9px; }
.dcg-illusts{ display:flex; align-items:flex-end; justify-content:center; gap:12px; flex-wrap:wrap; }
.dcg-illust{ display:flex; align-items:flex-end; justify-content:center; }
.dcg-typelabel{ font-size:13px; font-weight:700; color:#334155; letter-spacing:-.01em; }
.dcg-typelabel b{ color:var(--blue); font-weight:800; }

/* 실외기 식별 (실외기 N) */
.unit-id{ display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:800; color:#334155; letter-spacing:-.01em; }
.unit-id i{ color:var(--blue); font-size:11px; }

/* 상태칩: 에러코드 */
.unit-card.has-error .unit-state{ color:#b91c1c; background:#fee2e2; border-color:#fbb4ad; }
.unit-card.has-error .unit-state .sdot{ background:var(--red); box-shadow:none; animation:none; }
.unit-card.has-error{ border-color:#f7c8c3; }

/* 산소농도 미니 그래프 (스파크라인) */
.unit-graph{ height:22px; margin-top:1px; }
.spark{ width:100%; height:22px; display:block; }
.spark-line{ fill:none; stroke:#9aa6b6; stroke-width:1.7; stroke-linejoin:round; }
.spark-area{ fill:rgba(148,163,184,.10); stroke:none; }
.spark.on .spark-line{ stroke:#1070dd; }
.spark.on .spark-area{ fill:rgba(16,112,221,.12); }

/* 실외기 지표 (누적 사용시간 / 산소필터 잔여 / 흡입필터 잔여 / 실외기 상태) */
.unit-stats{ display:flex; flex-direction:column; gap:4px; padding-top:8px; margin-top:1px; border-top:1px solid #eef0f4; }
.ust-row{ display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:11px; line-height:1.2; }
.ust-k{ color:var(--muted); font-weight:600; display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
.ust-k i{ color:var(--muted-2); font-size:10px; width:13px; text-align:center; }
.ust-v{ color:var(--text-2); font-weight:700; font-variant-numeric:tabular-nums; white-space:nowrap; text-align:right; }
.ust-v.is-error{ color:var(--red); }

/* (컨트롤러 카드 스타일은 위 '컨트롤러 카드 (개편)' 섹션으로 통합됨) */

/* ── 공간 시뮬레이터 모달 ── */
.sim-backdrop{ position:fixed; inset:0; z-index:100; background:rgba(15,23,42,.42);
  display:flex; align-items:center; justify-content:center; padding:20px;
  opacity:0; visibility:hidden; transition:opacity .16s ease, visibility .16s ease; }
.sim-backdrop.show{ opacity:1; visibility:visible; }
.sim-card{ width:440px; max-width:100%; max-height:88vh; overflow:auto; background:#fff; border-radius:16px;
  box-shadow:0 24px 60px rgba(15,23,42,.30); padding:20px 22px 18px; transform:translateY(8px) scale(.98); transition:transform .16s ease; }
.sim-backdrop.show .sim-card{ transform:none; }
.sim-h{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.sim-h b{ font-size:16px; font-weight:800; color:var(--text); display:flex; align-items:center; gap:8px; }
.sim-h b i{ color:var(--blue); font-size:14px; }
.sim-h span{ display:block; font-size:12px; color:var(--muted); margin-top:3px; font-variant-numeric:tabular-nums; }
.sim-x{ border:none; background:#f1f3f6; width:30px; height:30px; border-radius:8px; cursor:pointer;
  font-size:20px; line-height:1; color:#64748b; flex-shrink:0; }
.sim-x:hover{ background:#e5e9f0; color:#111827; }
.sim-desc{ font-size:12.5px; color:var(--text-2); line-height:1.55; margin:12px 0 14px; }
.sim-desc b{ color:var(--blue); } .sim-desc em{ color:var(--muted-2); font-style:normal; font-size:11.5px; }
.sim-spaces{ display:flex; flex-direction:column; gap:12px; }
.sim-space{ background:#f7f8fb; border:1px solid #eaedf2; border-radius:11px; padding:11px 13px; }
.sim-space-head{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:8px; }
.sim-space-head b{ font-size:13.5px; font-weight:800; color:var(--text); }
.sim-space-head .on{ font-size:11px; font-weight:800; color:#0e8a4f; background:#e8f8ef; border:1px solid #c6efd6; padding:2px 8px; border-radius:999px; }
.sim-space-head .off{ font-size:11px; font-weight:800; color:#64748b; background:#eef1f5; border:1px solid #e3e6ec; padding:2px 8px; border-radius:999px; }
.sim-bar{ position:relative; height:9px; border-radius:999px; background:#e4e8ee; overflow:hidden; }
.sim-fill{ position:absolute; inset:0 auto 0 0; border-radius:inherit; background:var(--gray-2); transition:width .5s cubic-bezier(.4,0,.2,1); }
.sim-fill.on{ background:linear-gradient(90deg,#0a5fd6,#46a8ff); }
.sim-meta{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:7px; font-size:11.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.sim-meta b{ color:var(--text-2); }
.sim-foot{ margin-top:14px; font-size:11px; color:var(--muted-2); line-height:1.5; }

/* ── 공간 시뮬레이터: 산소농도 계산기 앱 임베드(거의 전체화면) ── */
.sim-card-app{ width:1180px; max-width:96vw; height:92vh; max-height:92vh;
  padding:14px 16px; display:flex; flex-direction:column; overflow:hidden; }
.sim-frame-wrap{ flex:1; margin-top:12px; border:1px solid #e6eaf1; border-radius:12px; overflow:hidden; background:#fff; }
.sim-frame{ width:100%; height:100%; border:0; display:block; }
body.modal-open{ overflow:hidden; }

/* ====================== 예약 / 타이머 설정 모달 ====================== */
.cm-backdrop{ position:fixed; inset:0; z-index:120; background:rgba(15,23,42,.42);
  display:flex; align-items:center; justify-content:center; padding:20px;
  opacity:0; visibility:hidden; transition:opacity .16s ease, visibility .16s ease; }
.cm-backdrop.show{ opacity:1; visibility:visible; }
.cm-card{ width:400px; max-width:100%; max-height:90vh; overflow:auto; background:#fff; border-radius:16px;
  box-shadow:0 24px 60px rgba(15,23,42,.30); padding:20px 22px 18px;
  transform:translateY(8px) scale(.98); transition:transform .16s ease; }
.cm-backdrop.show .cm-card{ transform:none; }
.cm-h{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:16px; }
.cm-h b{ font-size:16px; font-weight:800; color:var(--text); display:flex; align-items:center; gap:8px; }
.cm-h b i{ color:var(--blue); font-size:14px; }
.cm-h span{ display:block; font-size:12px; color:var(--muted); margin-top:3px; font-variant-numeric:tabular-nums; }
.cm-x{ border:none; background:#f1f3f6; width:30px; height:30px; border-radius:8px; cursor:pointer;
  font-size:20px; line-height:1; color:#64748b; flex-shrink:0; }
.cm-x:hover{ background:#e5e9f0; color:#111827; }

.cm-switchrow{ display:flex; align-items:center; justify-content:space-between; gap:12px;
  font-size:13.5px; font-weight:700; color:var(--text-2); padding:4px 0 14px; border-bottom:1px solid #eef0f4; margin-bottom:16px; }
.cm-switch{ position:relative; width:44px; height:24px; border-radius:999px; background:#cbd5e1; cursor:pointer; transition:background .15s ease; flex-shrink:0; }
.cm-switch.on{ background:var(--blue); }
.cm-knob{ position:absolute; top:3px; left:3px; width:18px; height:18px; border-radius:50%; background:#fff; transition:left .15s ease; box-shadow:0 1px 2px rgba(0,0,0,.2); }
.cm-switch.on .cm-knob{ left:23px; }

.cm-body{ display:flex; flex-direction:column; gap:16px; }
.cm-body.is-disabled{ opacity:.4; pointer-events:none; }
.cm-field{ display:flex; flex-direction:column; gap:8px; }
.cm-field > label{ font-size:12px; font-weight:700; color:var(--muted); }
.cm-times{ display:flex; align-items:center; gap:10px; }
.cm-time{ font-family:inherit; font-size:15px; font-weight:700; color:var(--text); font-variant-numeric:tabular-nums;
  border:1px solid #d7dce6; border-radius:9px; padding:9px 11px; background:#fff; }
.cm-time:focus{ outline:none; border-color:var(--blue); box-shadow:0 0 0 3px rgba(16,112,221,.12); }
.cm-time-lg{ font-size:20px; padding:12px 14px; width:100%; text-align:center; }
.cm-tilde{ color:var(--muted-2); font-weight:700; }
.cm-days{ display:flex; gap:6px; }
.cm-day{ flex:1; border:1px solid #d7dce6; background:#fff; color:#64748b; font-family:inherit; font-weight:700; font-size:13px;
  padding:9px 0; border-radius:9px; cursor:pointer; transition:all .12s ease; }
.cm-day.on{ background:var(--blue); border-color:var(--blue); color:#fff; }
.cm-day:hover{ border-color:var(--blue); }
.cm-quick, .cm-durs{ display:flex; gap:6px; flex-wrap:wrap; margin-top:2px; }
.cm-qbtn, .cm-dur{ border:1px solid #cfdcf2; background:#f3f8ff; color:#1d6fd0; font-family:inherit; font-weight:700; font-size:12px;
  padding:7px 13px; border-radius:8px; cursor:pointer; transition:all .12s ease; }
.cm-qbtn:hover, .cm-dur:hover{ background:#e7f1ff; border-color:var(--blue); }

.cm-foot{ display:flex; gap:8px; margin-top:20px; }
.cm-cancel{ flex:1; border:1px solid #d7dce6; background:#fff; color:#64748b; font-family:inherit; font-weight:800; font-size:13px;
  padding:11px 0; border-radius:10px; cursor:pointer; transition:all .12s ease; }
.cm-cancel:hover{ border-color:#94a3b8; }
.cm-clear{ color:#b91c1c; border-color:#f3c9c4; }
.cm-clear:hover{ border-color:var(--red); background:#fef2f2; }
.cm-save{ flex:1.4; border:none; background:var(--blue); color:#fff; font-family:inherit; font-weight:800; font-size:13px;
  padding:11px 0; border-radius:10px; cursor:pointer; box-shadow:0 2px 8px rgba(16,112,221,.26); transition:filter .12s ease; }
.cm-save:hover{ filter:brightness(1.06); }

/* ====================== SVG 와이어 (양방향 통신) ====================== */
.wire-base{ fill:none; stroke:#e4e8ef; stroke-width:4.5; stroke-linecap:round; }
.wire-ref{ fill:none; stroke:none; }
.wire-line{ fill:none; stroke-width:2.2; stroke-linecap:round; }
.wire-line.active{ stroke:#1070dd; opacity:.85; }
.wire-line.idle{ stroke:#cbd5e1; }

/* 컨트롤러 → 실외기 : 명령/폴링 (진한 블루) */
.packet .halo{ fill:rgba(16,112,221,.18); }
.packet .core{ fill:#1070dd; }
/* 실외기 → 컨트롤러 : 센서·응답 (연한 블루) */
.packet.rev .halo{ fill:rgba(123,191,255,.22); }
.packet.rev .core{ fill:#7bbfff; }
/* 정지 상태 하트비트 (회색) */
.packet.idle .halo{ fill:rgba(148,163,184,.14); }
.packet.idle .core{ fill:#a3acbb; }
/* 클릭 시 명령 패킷 (강조) */
.packet.command .halo{ fill:rgba(16,112,221,.42); }
.packet.command .core{ fill:#fff; stroke:#1070dd; stroke-width:1.5; }

/* ====================== 푸터 ====================== */
.foot{ margin-top:22px; padding:18px 6px 6px; border-top:1px solid var(--line); }
.foot-row{ display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;
  font-size:12.5px; color:var(--muted); }
.foot-tel{ font-weight:700; color:var(--text-2); display:inline-flex; align-items:center; gap:7px; }
.foot-tel i{ color:var(--blue); }
.foot-tel a{ color:var(--blue); text-decoration:none; font-weight:800; }
.foot-copy{ margin-top:8px; font-size:11.5px; color:var(--muted-2); }
.foot-copy b{ color:var(--text-2); }

/* ====================== 애니메이션 ====================== */
@keyframes spin{ to{ transform:rotate(360deg); } }
@keyframes fanSpin{ to{ transform:rotate(360deg); } }
@keyframes dashFlow{ to{ stroke-dashoffset:-12.4; } }
@keyframes radar{
  0%{ transform:translate(-50%,-50%) scale(.45); opacity:.7; }
  80%{ opacity:0; }
  100%{ transform:translate(-50%,-50%) scale(1.5); opacity:0; }
}
@keyframes livePulse{ 0%,100%{ opacity:1; transform:scale(1); } 50%{ opacity:.45; transform:scale(.82); } }
@keyframes ledBlink{ 0%,100%{ opacity:1; } 50%{ opacity:.35; } }
@keyframes sigBlink{ 0%{ opacity:1; } 50%{ opacity:.25; } 100%{ opacity:1; } }
@keyframes ledGlow{ 0%,100%{ opacity:.9; } 50%{ opacity:.45; } }
@keyframes bubbleRise{
  0%{ transform:translateY(8px) scale(.6); opacity:0; }
  15%{ opacity:.9; }
  100%{ transform:translateY(-52px) scale(1); opacity:0; }
}
@keyframes numBump{ 0%{ transform:scale(1); } 40%{ transform:scale(1.18); } 100%{ transform:scale(1); } }
@keyframes cardPulse{ 0%{ box-shadow:0 0 0 0 rgba(16,112,221,.35); } 100%{ box-shadow:0 0 0 12px rgba(16,112,221,0); } }
@keyframes legendFlow{ to{ transform:translateX(24px); } }

/* ====================== 반응형 ====================== */
/* 가로 정렬: 화면이 좁으면 3개 나란히 대신 존을 1열로 스택 */
@media (max-width:1280px){
  .zones.layout-v1, .zones.layout-v2{ grid-template-columns:1fr; }
  .app.app-v{ max-width:1480px; }
}
@media (max-width:1100px){
  .summary{ grid-template-columns:repeat(2,1fr); }
}
@media (max-width:880px){
  .app{ padding:12px 14px 32px; }
  .topbar{ flex-direction:column; align-items:stretch; gap:12px; }
  .topbar-right{ justify-content:flex-start; flex-wrap:wrap; gap:10px; }
  .master{ flex-direction:column; align-items:stretch; gap:14px; }
  .master-groups{ flex-direction:column; gap:14px; }

  /* 좁은 화면: 와이어 숨김, 모든 배치를 단일 열 스택으로 (컨트롤러 → 토출기 → 실외기) */
  .zone-topology{ display:flex !important; flex-direction:column; align-items:stretch; gap:16px; padding:8px 0 0; }
  .wires{ display:none; }
  .controller-node{ width:100%; justify-content:center; }
  .ctrl-port{ display:none; }
  .units-row{ grid-template-columns:1fr; }
  .unit-port{ display:none; }
}
@media (max-width:560px){
  .summary{ grid-template-columns:1fr 1fr; gap:10px; }
  .controller-node{ flex-direction:column; text-align:center; }
  .ctrl-meta{ text-align:center; }
}

/* 모션 최소화 선호 */
@media (prefers-reduced-motion:reduce){
  *{ animation-duration:.001ms !important; animation-iteration-count:1 !important; }
}
