.react-grid-layout{position:relative;transition:height .2s ease}.react-grid-item{transition:all .2s ease;transition-property:left,top,width,height}.react-grid-item img{pointer-events:none;-webkit-user-select:none;user-select:none}.react-grid-item.cssTransforms{transition-property:transform,width,height}.react-grid-item.resizing{transition:none;z-index:1;will-change:width,height}.react-grid-item.react-draggable-dragging{transition:none;z-index:3;will-change:transform}.react-grid-item.dropping{visibility:hidden}.react-grid-item.react-grid-placeholder{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.react-grid-item.react-grid-placeholder.placeholder-resizing{transition:none}.react-grid-item>.react-resizable-handle{position:absolute;width:20px;height:20px}.react-grid-item>.react-resizable-handle:after{content:"";position:absolute;right:3px;bottom:3px;width:5px;height:5px;border-right:2px solid rgba(0,0,0,.4);border-bottom:2px solid rgba(0,0,0,.4)}.react-resizable-hide>.react-resizable-handle{display:none}.react-grid-item>.react-resizable-handle.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-w,.react-grid-item>.react-resizable-handle.react-resizable-handle-e{top:50%;margin-top:-10px;cursor:ew-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-n,.react-grid-item>.react-resizable-handle.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}.react-resizable{position:relative}.react-resizable-handle{position:absolute;width:20px;height:20px;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+);background-position:bottom right;padding:0 3px 3px 0}.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-resizable-handle-w,.react-resizable-handle-e{top:50%;margin-top:-10px;cursor:ew-resize}.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-resizable-handle-n,.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}:root{--bg: #eef3fb;--card: #ffffff;--ink: #2d3436;--ink-soft: #7f8c9b;--primary: #6c5ce7;--primary-dark: #5646c9;--sensor: #0984e3;--sensor-soft: #e3f2fd;--good: #00b894;--control: #e17055;--control-soft: #ffe8de;--alert: #d63031;--radius: 18px;--shadow: 0 4px 14px rgba(45, 52, 84, .1);font-size:16px}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:Segoe UI,Microsoft JhengHei,PingFang TC,system-ui,sans-serif}button{font-family:inherit}.app{display:flex;flex-direction:column;height:100vh}.topbar{display:flex;align-items:center;gap:14px;padding:10px 20px;background:var(--card);box-shadow:var(--shadow);z-index:10}.topbar .logo{display:flex;align-items:center;gap:10px;font-weight:800;font-size:1.15rem;color:var(--primary)}.topbar .logo svg{width:28px;height:28px}.topbar .class-name{background:var(--sensor-soft);color:var(--sensor);border-radius:999px;padding:4px 14px;font-weight:700;font-size:.9rem}.topbar .spacer{flex:1}.conn-dot{display:flex;align-items:center;gap:7px;font-size:.85rem;color:var(--ink-soft);font-weight:600}.conn-dot .dot{width:12px;height:12px;border-radius:50%;background:#b2bec3}.conn-dot .dot.on{background:var(--good);box-shadow:0 0 6px var(--good)}.body{display:flex;flex:1;overflow:hidden}.sidebar{width:96px;padding:16px 10px;display:flex;flex-direction:column;gap:10px;background:var(--card);box-shadow:var(--shadow)}.sidebar a{display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 4px;border-radius:14px;text-decoration:none;color:var(--ink-soft);font-size:.78rem;font-weight:700}.sidebar a svg{width:26px;height:26px}.sidebar a.active{background:var(--primary);color:#fff}.sidebar a:not(.active):hover{background:#f0edff;color:var(--primary)}main.content{flex:1;overflow:auto;padding:20px}.btn{border:none;border-radius:999px;padding:10px 20px;font-size:.95rem;font-weight:700;cursor:pointer;background:#e8ecf4;color:var(--ink);transition:transform .08s ease,box-shadow .15s ease}.btn:hover{transform:translateY(-1px);box-shadow:var(--shadow)}.btn:active{transform:translateY(0)}.btn.primary{background:var(--primary);color:#fff}.btn.primary:hover{background:var(--primary-dark)}.btn.danger{background:#ffe3e3;color:var(--alert)}.btn.small{padding:6px 14px;font-size:.82rem}.card{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px}.page-head{display:flex;align-items:center;gap:14px;margin-bottom:18px}.page-head h1{font-size:1.4rem;margin:0;flex:1}label.field{display:block;margin-bottom:12px;font-weight:700;font-size:.88rem;color:var(--ink-soft)}label.field>input,label.field>select{display:block;width:100%;margin-top:5px;padding:10px 12px;border:2px solid #dfe6f1;border-radius:12px;font-size:1rem;font-family:inherit;color:var(--ink);background:#fff}label.field>input:focus,label.field>select:focus{outline:none;border-color:var(--primary)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#1e223c73;display:flex;align-items:center;justify-content:center;z-index:100}.modal{background:var(--card);border-radius:22px;padding:24px;width:min(560px,92vw);max-height:88vh;overflow:auto;box-shadow:0 18px 50px #1418324d}.modal h2{margin:0 0 16px;font-size:1.2rem}.modal .modal-actions{display:flex;justify-content:flex-end;gap:10px;margin-top:18px}.empty-state{text-align:center;color:var(--ink-soft);padding:60px 20px;font-size:1.05rem;font-weight:600}.widget{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);height:100%;display:flex;flex-direction:column;overflow:hidden;border-top:6px solid var(--sensor)}.widget.control-widget{border-top-color:var(--control)}.widget-head{display:flex;align-items:center;gap:8px;padding:10px 14px 0}.widget-title{font-weight:800;font-size:.92rem;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.widget-device{font-size:.72rem;color:var(--ink-soft);font-weight:600}.widget-remove{border:none;background:#ffe3e3;color:var(--alert);border-radius:8px;width:26px;height:26px;cursor:pointer;font-weight:800;line-height:1}.widget-body{flex:1;display:flex;align-items:center;justify-content:center;padding:10px 14px 14px;min-height:0}.widget .waiting{color:var(--ink-soft);font-size:.85rem;font-weight:600}.big-number{font-size:2.6rem;font-weight:800;color:var(--sensor);transition:color .2s}.big-number .unit{font-size:1rem;color:var(--ink-soft);margin-left:6px}.toggle{position:relative;width:84px;height:44px;border-radius:999px;border:none;cursor:pointer;background:#dfe6f1;transition:background .2s}.toggle.on{background:var(--good)}.toggle .knob{position:absolute;top:4px;left:4px;width:36px;height:36px;border-radius:50%;background:#fff;box-shadow:0 2px 6px #00000040;transition:left .18s ease}.toggle.on .knob{left:44px}.slider-wrap{width:100%;display:flex;flex-direction:column;align-items:center;gap:8px}.slider-wrap input[type=range]{width:100%;accent-color:var(--control);height:34px}.slider-value{font-size:1.4rem;font-weight:800;color:var(--control)}.push-btn{border:none;border-radius:16px;background:var(--control);color:#fff;font-size:1.05rem;font-weight:800;padding:16px 30px;cursor:pointer;box-shadow:0 4px #b05237;transition:transform .06s,box-shadow .06s}.push-btn:active{transform:translateY(4px);box-shadow:0 0 #b05237}.led-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:6px}.led-cell{width:30px;height:30px;border-radius:7px;border:none;cursor:pointer;background:#dfe6f1;transition:background .1s,box-shadow .1s}.led-cell.on{background:#ff5252;box-shadow:0 0 8px #ff5252}.gauge-svg text{font-family:inherit}.text-sender{display:flex;gap:8px;width:100%}.text-sender input{flex:1;min-width:0;padding:10px 12px;border:2px solid #dfe6f1;border-radius:12px;font-size:1rem;font-family:inherit}.react-grid-item.react-grid-placeholder{background:var(--primary);opacity:.18;border-radius:var(--radius)}.layout-editing .widget{outline:2px dashed #c3b8ff;outline-offset:2px}.react-grid-item>.react-resizable-handle{z-index:5}.device-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:16px}.device-card{display:flex;flex-direction:column;gap:8px}.device-card .dev-head{display:flex;align-items:center;gap:10px}.device-card .dev-name{font-weight:800;font-size:1.05rem;flex:1}.status-dot{width:13px;height:13px;border-radius:50%;background:#b2bec3;flex:none}.status-dot.on{background:var(--good);box-shadow:0 0 6px var(--good)}.device-card .dev-meta{font-size:.8rem;color:var(--ink-soft);font-weight:600}.device-card .dev-code{background:#f4f1ff;color:var(--primary);border-radius:12px;padding:8px 12px;font-weight:800;letter-spacing:2px;font-size:1.1rem;display:flex;justify-content:space-between;align-items:center}.device-card .dev-code .code-label{font-size:.7rem;letter-spacing:0;color:var(--ink-soft)}.device-card .dev-actions{display:flex;gap:8px;margin-top:4px}.pairing-reveal{text-align:center;background:#f4f1ff;border-radius:16px;padding:18px;margin:14px 0}.pairing-reveal .code{font-size:2.2rem;font-weight:800;letter-spacing:8px;color:var(--primary)}.pairing-reveal .dev-id{font-size:1.1rem;font-weight:700;color:var(--sensor);margin-top:6px}.rule-list{display:flex;flex-direction:column;gap:12px}.rule-card{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.rule-sentence{flex:1;min-width:240px;font-size:.98rem;line-height:1.7}.rule-chip{display:inline-block;border-radius:8px;padding:2px 9px;font-weight:700;margin:0 3px}.rule-chip.if{background:var(--sensor-soft);color:var(--sensor)}.rule-chip.then{background:var(--control-soft);color:var(--control)}.rule-chip.kw{background:#f4f1ff;color:var(--primary)}.rule-disabled .rule-sentence{opacity:.45}.rule-form-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}.rule-form-grid .full{grid-column:1 / -1}.if-then-tag{display:inline-block;font-weight:800;border-radius:8px;padding:3px 12px;margin:8px 0}.if-then-tag.if{background:var(--sensor-soft);color:var(--sensor)}.if-then-tag.then{background:var(--control-soft);color:var(--control)}.toasts{position:fixed;right:18px;bottom:18px;display:flex;flex-direction:column;gap:10px;z-index:200}.toast{background:var(--alert);color:#fff;border-radius:14px;padding:13px 18px;font-weight:700;box-shadow:0 8px 22px #d6303166;max-width:340px;animation:slide-in .25s ease}@keyframes slide-in{0%{transform:translate(120%)}to{transform:translate(0)}}.palette-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}.palette-item{border:2px solid #dfe6f1;border-radius:14px;padding:12px;cursor:pointer;background:#fff;text-align:left;font-family:inherit}.palette-item:hover{border-color:var(--primary)}.palette-item.selected{border-color:var(--primary);background:#f4f1ff}.palette-item .p-name{font-weight:800;font-size:.95rem}.palette-item .p-desc{font-size:.75rem;color:var(--ink-soft);margin-top:4px;font-weight:600}.palette-item.sensor-type .p-name{color:var(--sensor)}.palette-item.control-type .p-name{color:var(--control)}.value-flash{animation:flash .4s ease}@keyframes flash{0%{color:var(--good)}}.login-screen{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#6c5ce7,#0984e3);position:relative}.login-lang{position:absolute;top:18px;right:18px}.login-card{background:var(--card);border-radius:26px;padding:36px 34px;width:min(400px,92vw);box-shadow:0 20px 60px #14183259;text-align:center}.login-card h1{margin:0 0 4px;font-size:1.5rem}.login-subtitle{color:var(--ink-soft);font-weight:600;margin:0 0 22px;font-size:.92rem}.login-card .field{text-align:left}.login-logo{width:64px;height:64px;margin:0 auto 14px;border-radius:20px;background:#f4f1ff;color:var(--primary);display:flex;align-items:center;justify-content:center}.login-logo svg{width:38px;height:38px}.login-btn{width:100%;padding:12px;font-size:1.05rem;margin-top:6px}.login-error{background:#ffe3e3;color:var(--alert);border-radius:12px;padding:10px 14px;font-weight:700;font-size:.88rem;margin-bottom:12px}.user-chip{display:flex;align-items:center;gap:8px}.user-chip-name{font-weight:700;font-size:.9rem}.role-badge{border-radius:999px;padding:3px 12px;font-size:.75rem;font-weight:800;white-space:nowrap}.role-sysadmin{background:#2d3436;color:#fff}.role-school_admin{background:#6c5ce7;color:#fff}.role-teacher{background:#e17055;color:#fff}.role-student{background:#00b894;color:#fff}.user-row{display:flex;align-items:center;gap:14px}.user-row .user-info{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}
