diff --git a/custom_components/houseplan/frontend/houseplan-card.js b/custom_components/houseplan/frontend/houseplan-card.js
index f3721a61..ae0f145f 100755
--- a/custom_components/houseplan/frontend/houseplan-card.js
+++ b/custom_components/houseplan/frontend/houseplan-card.js
@@ -1,4 +1,4 @@
-globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825898642dedcccc78443fe6cf5df23";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},a=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=t=>t,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,T=document,R=()=>T.createComment(""),I=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"")),s]};class X{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&I(this._$AH)?this._$AA.nextSibling.data=t:this.T(T.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new X(t)),e}k(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=Z(this,t,e,0),o=!I(t)||t!==this._$AH&&t!==U,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const pt=2,ut=t=>(...e)=>({_$litDirective$:t,values:e});let _t=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const mt={},gt=ut(class extends _t{constructor(){super(...arguments),this.ot=mt}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return U}else if(this.ot===e)return U;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),{I:ft}=at,vt=t=>t,yt=()=>document.createComment(""),bt=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(yt(),n),o=s.insertBefore(yt(),n);i=new ft(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=vt(t).nextSibling;vt(s).insertBefore(t,n),t=e}}}return i},wt=(t,e,i=t)=>(t._$AI(e,i),t),kt={},xt=(t,e=kt)=>t._$AH=e,$t=t=>{t._$AR(),t._$AA.remove()},St=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},Mt=ut(class extends _t{constructor(t){if(super(t),t.type!==pt)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=wt(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=wt(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=wt(n[d],o[_]),bt(t,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=wt(n[p],o[u]),bt(t,n[d],n[p]),p--,u++;else if(void 0===c&&(c=St(a,u,_),h=St(r,d,p)),c.has(r[d]))if(c.has(r[p])){const e=h.get(a[u]),i=void 0!==e?n[e]:null;if(null===i){const e=bt(t,n[d]);wt(e,o[u]),l[u]=e}else l[u]=wt(i,o[u]),bt(t,n[d],i),n[e]=null;u++}else $t(n[p]),p--;else $t(n[d]),d++;for(;u<=_;){const e=bt(t,l[_+1]);wt(e,o[u]),l[u++]=e}for(;d<=p;){const t=n[d++];null!==t&&$t(t)}return this.ut=a,xt(t,l),U}}),Ct=new WeakMap;let Dt=0;class Pt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Dt,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=Ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),Ct.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?Ct.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=Ct.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void Ct.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="70f73686ca2a09aa7ca18ae77f6e6bdddfac7e9cb8cc46a784d01c351e036ef6";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},a=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=t=>t,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,T=document,R=()=>T.createComment(""),I=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"")),s]};class X{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&I(this._$AH)?this._$AA.nextSibling.data=t:this.T(T.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new X(t)),e}k(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=Z(this,t,e,0),o=!I(t)||t!==this._$AH&&t!==U,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const pt=2,ut=t=>(...e)=>({_$litDirective$:t,values:e});let _t=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const mt={},gt=ut(class extends _t{constructor(){super(...arguments),this.ot=mt}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return U}else if(this.ot===e)return U;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),{I:ft}=at,vt=t=>t,yt=()=>document.createComment(""),bt=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(yt(),n),o=s.insertBefore(yt(),n);i=new ft(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=vt(t).nextSibling;vt(s).insertBefore(t,n),t=e}}}return i},wt=(t,e,i=t)=>(t._$AI(e,i),t),kt={},xt=(t,e=kt)=>t._$AH=e,$t=t=>{t._$AR(),t._$AA.remove()},St=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},Mt=ut(class extends _t{constructor(t){if(super(t),t.type!==pt)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=wt(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=wt(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=wt(n[d],o[_]),bt(t,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=wt(n[p],o[u]),bt(t,n[d],n[p]),p--,u++;else if(void 0===c&&(c=St(a,u,_),h=St(r,d,p)),c.has(r[d]))if(c.has(r[p])){const e=h.get(a[u]),i=void 0!==e?n[e]:null;if(null===i){const e=bt(t,n[d]);wt(e,o[u]),l[u]=e}else l[u]=wt(i,o[u]),bt(t,n[d],i),n[e]=null;u++}else $t(n[p]),p--;else $t(n[d]),d++;for(;u<=_;){const e=bt(t,l[_+1]);wt(e,o[u]),l[u++]=e}for(;d<=p;){const t=n[d++];null!==t&&$t(t)}return this.ut=a,xt(t,l),U}}),Ct=new WeakMap;let Dt=0;class Pt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Dt,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=Ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),Ct.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?Ct.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=Ct.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void Ct.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W`
${this.icon?W``:G}
${this.title}
`;return this._useHaDialog?W` span { flex: 1; min-width: 0; }
- .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
- .habindingbanner.limited {
- border-color: var(--secondary-text-color, #9aa0aa);
- background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
- }
- .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
.dev.sel {
--device-ring-color: #F0A00C;
--device-ring-width: calc(var(--dev-size) * 0.0375);
@@ -2927,39 +2591,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
:host([data-pointer-hover]) .tab:hover .tabedit {
opacity: 0.9;
}
- .srcrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- font-size: var(--fs-m);
- cursor: pointer;
- padding: var(--sp-1) 0;
- }
- .dispsection {
- margin-top: var(--sp-5) !important;
- padding-top: var(--sp-4);
- border-top: 1px solid var(--hp-line);
- font-weight: 600;
- color: var(--hp-txt) !important;
- }
- .colorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .gsrow > hp-color-opacity {
- min-width: min(100%, 210px);
- justify-content: space-between;
- }
- .ripple-colorrow > hp-color-opacity {
- width: 100%;
- justify-content: space-between;
- }
- .ripple-sizerow > .opl {
- min-width: 0;
- }
- .colorrow input[type='range'] { flex: 1; }
- .colorrow .tempin { width: 70px; flex: none; }
.temprange {
display: inline-flex;
align-items: center;
@@ -2968,200 +2599,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
color: var(--hp-muted);
font-size: var(--fs-s);
}
- /* beat the generic hp-dialog .body .namein { width:100% } rule */
- hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
- hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
- .srcrow { flex-wrap: nowrap; }
- /* native HA controls (rendered only when the HA frontend defines them;
- old HA and the smoke env keep the plain inputs). ha-switch is taller
- than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
- .srcrow ha-switch { flex: none; }
- .colorrow ha-slider { flex: 1; min-width: 0; }
- .srcrow > span:first-of-type { white-space: nowrap; }
- .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
- .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
- .markerlightgroup {
- min-width: 0;
- margin: var(--sp-5) 0 0;
- padding: var(--sp-4);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- }
- .markerlightgroup legend {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- padding: 0 var(--sp-2);
- color: var(--hp-txt);
- font-weight: 600;
- }
- .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
- .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
- .markerhelpfield { margin-top: var(--sp-4); }
- .markerhelplabel {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- margin-bottom: var(--sp-1);
- }
- .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
- .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
- .markerlightgroup .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
- .markerglowvalue hp-color-opacity { flex: none; }
- .markerlightdisabled {
- display: flex;
- align-items: center;
- gap: var(--sp-2);
- margin-top: var(--sp-2) !important;
- }
- .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
- .markerbadgetechnical {
- min-width: 0;
- margin: var(--sp-1) 0 var(--sp-2) !important;
- overflow-wrap: anywhere;
- }
- .markerbadgetechnical code { white-space: normal; }
- .planrow {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- /* the "already uploaded" picker: a plan is never deleted for being
- unreferenced, so it has to be findable again */
- .savedplans {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- max-height: 240px;
- overflow: auto;
- margin: var(--sp-3) 0 var(--sp-1);
- padding: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
- /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
- flex item whose automatic minimum size is ZERO (overflow != visible),
- so inside hp-dialog .body — a flex column taller than its 66vh cap —
- it shrank to a 14px sliver: the rows were in the DOM, the owner saw
- a thin rounded stripe under the "Already uploaded" button. Don't
- shrink, and keep a floor even when the box is empty or loading. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .savedplan {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
- .savedplan img {
- width: 56px;
- height: 40px;
- object-fit: contain;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- flex: none;
- }
- .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
- .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .savedmeta .muted { font-size: var(--fs-s); }
- .savedplan .btn.danger ha-icon { color: #f25a4a; }
- .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
- .planprev {
- max-width: 120px;
- max-height: 70px;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- }
- .planname {
- font-size: var(--fs-m);
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .planname.muted {
- color: var(--hp-muted);
- }
- .filebtn {
- cursor: pointer;
- }
- .fileupload { display: inline-flex; min-width: 0; }
- .fileupload > input { display: none; }
- .btn.danger {
- border-color: #b3402a;
- color: #ff7a5c;
- }
- hp-dialog .row .spacer {
- flex: 1;
- }
- hp-dialog .body {
- max-height: 66vh;
- overflow-y: auto;
- }
- .descin {
- width: 100%;
- box-sizing: border-box;
- background: var(--hp-bg);
- border: 1px solid var(--hp-line);
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- font-size: var(--fs-m);
- font-family: inherit;
- resize: vertical;
- /* flex column of the dialog body squeezes textareas — keep a usable height */
- min-height: 92px;
- flex-shrink: 0;
- line-height: 1.35;
- }
- .bindsel {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- padding: var(--sp-4);
- }
- .bindsel .opt {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- background: transparent;
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- font-size: var(--fs-m);
- font-family: inherit;
- }
- .bindsel .opt.on {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- border-color: var(--hp-accent);
- }
- .curbind {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- font-size: var(--fs-m);
- color: var(--hp-txt);
- flex-wrap: wrap;
- }
- .curbind .ref {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
/* live vacuum: a round puck, no badge plate, soft pulse (docs/VACUUM.md) */
.vacpuck {
position: absolute;
@@ -3242,59 +2679,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
stroke: rgba(255, 255, 255, 0.82);
stroke-width: 0.9;
}
- .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
- .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
- .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
- .vacdiag > div > span { color: var(--secondary-text-color); }
- .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
- .vacpicker { margin: var(--sp-3) 0; }
- .vacsource-warning { display: grid; gap: 8px; }
- .vacsource-warning .btn { justify-self: start; }
- .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
- .vacpicker > summary::-webkit-details-marker { display: none; }
- .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
- .vacsource-list details { padding: 6px 0 0; }
- .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
- .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
- width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
- border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
- text-align: left; cursor: pointer; }
- .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
- .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
- .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
- .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
- .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
- .vacfit {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- z-index: 12;
- overflow: visible;
- touch-action: none;
- cursor: grab;
- /* the devlayer is pointer-events: none and every child opts back in —
- without this line real clicks flew straight through the overlay
- (owner: «уголки не кликабельны»; synthetic smoke events bypass
- hit-testing, which is why they lied) */
- pointer-events: auto;
- }
- .vacfit:active { cursor: grabbing; }
- .vacfit polygon {
- fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- stroke-dasharray: 6 4;
- }
- .vacfit text {
- fill: var(--hp-accent);
- font-size: 26px;
- text-anchor: middle;
- dominant-baseline: middle;
- pointer-events: none;
- user-select: none;
- }
/* room resize tool (docs/RESIZE.md) */
/* wall handle: invisible finger-sized hit circle (HP-1550-04 hit priority
kept), the visible glyph lives in the sibling .rszicon */
@@ -3366,290 +2750,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
transform: translate(-50%, -50%);
border: 1px solid var(--hp-accent);
}
- /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
- .furnpalette {
- display: flex;
- flex-direction: column;
- max-height: 38vh;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- background: var(--card-background-color, var(--hp-bg));
- font-size: 0.85em;
- }
- .furnhd {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 8px;
- font-weight: 600;
- opacity: 0.9;
- }
- .furnhd .spacer { flex: 1; }
- .furnbody {
- overflow: auto;
- padding: 0 8px 6px;
- }
- .furngroup {
- margin: 6px 0 3px;
- font-size: 0.85em;
- text-transform: uppercase;
- letter-spacing: 0.04em;
- opacity: 0.6;
- }
- .furnrow {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
- }
- .furnitem {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2px;
- width: 74px;
- padding: 4px 2px;
- border: 1px solid transparent;
- border-radius: 8px;
- background: rgba(127, 127, 127, 0.08);
- color: inherit;
- font: inherit;
- font-size: 0.8em;
- line-height: 1.15;
- text-align: center;
- cursor: pointer;
- }
:host([data-pointer-hover]) .furnitem:hover { background: rgba(127, 127, 127, 0.18); }
- .furnitem.on {
- border-color: var(--hp-accent);
- background: rgba(38, 166, 154, 0.18);
- }
- .furnprev {
- width: 40px;
- height: 40px;
- color: var(--primary-text-color, currentColor);
- }
- .furnsize {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 6px;
- padding: 6px 8px;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- }
- .furnsize label {
- display: flex;
- align-items: baseline;
- gap: 3px;
- opacity: 0.8;
- }
- .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
- .furnsize input {
- width: 5.5em;
- padding: 3px 5px;
- }
- .furnhint { opacity: 0.6; }
- .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
- /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
- .vacfithandle {
- fill: transparent;
- stroke: none;
- cursor: nwse-resize;
- }
- .vacfitknob {
- fill: var(--hp-bg);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- pointer-events: none;
- }
- .vaccalbar {
- position: fixed;
- left: 50%;
- bottom: 24px;
- transform: translateX(-50%);
- display: flex;
- gap: var(--sp-5);
- align-items: center;
- background: var(--hp-bg);
- color: var(--hp-txt);
- border: 1px solid var(--hp-accent);
- border-radius: var(--rad-l);
- padding: 10px var(--sp-5);
- z-index: 60;
- box-shadow: var(--shadow-2);
- }
- .candlist {
- max-height: 160px;
- overflow-y: auto;
- border-top: 1px solid var(--hp-line);
- /* A scrollable box is a flex item that HAPPILY collapses: inside the
- dialog body (a flex column) this list rendered its rows into a 1px
- sliver — the DOM had 26 candidates and the user saw nothing. In the
- binding dropdown it sits inside .droppanel (block context) and never
- showed the bug. Field report, 2026-07-30. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .cand {
- display: flex;
- justify-content: space-between;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- border-radius: var(--rad-s);
- font-size: var(--fs-m);
- }
:host([data-pointer-hover]) .cand:hover {
background: rgba(127, 127, 127, 0.15);
}
- .cand.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .cand .cs {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- white-space: nowrap;
- }
- .opening-entity-candidate {
- width: 100%;
- border: 0;
- background: transparent;
- color: inherit;
- font: inherit;
- text-align: left;
- }
- .opening-entity-candidate.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .opening-entity-empty { cursor: default; }
- .cand.sel .cs {
- color: var(--text-primary-color, #fff);
- opacity: 0.85;
- }
- .cand.muted {
- color: var(--hp-muted);
- cursor: default;
- }
- .pdfedit {
- display: flex;
- flex-wrap: wrap;
- gap: var(--sp-3);
- align-items: center;
- }
- .pdftag {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .pdftag a {
- color: var(--hp-txt);
- text-decoration: none;
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .pdftag .x {
- --mdc-icon-size: 15px;
- cursor: pointer;
- color: var(--hp-muted);
- }
:host([data-pointer-hover]) .pdftag .x:hover {
color: #ff7a5c;
}
- .entlist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- margin-bottom: 10px;
- }
- .entrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- border-radius: var(--rad-m);
- background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
- }
- .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
- .entrow.on ha-icon { color: var(--hp-accent); }
- .entrow .en { flex: 1; font-size: var(--fs-m); }
- .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
- .entbtn {
- min-width: 74px;
- min-height: 32px;
- padding: var(--sp-2) var(--sp-5);
- border: 1px solid var(--hp-muted);
- border-radius: 999px;
- background: transparent;
- color: var(--hp-txt);
- font: inherit;
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .entbtn.on {
- background: var(--hp-accent);
- border-color: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- font-weight: 600;
- }
- .inforow {
- display: flex;
- gap: 10px;
- font-size: var(--fs-m);
- margin: var(--sp-2) 0;
- }
- .inforow .k {
- color: var(--hp-muted);
- min-width: 84px;
- }
- .inforow a {
- color: var(--hp-accent);
- word-break: break-all;
- }
- .infodesc {
- font-size: var(--fs-m);
- white-space: pre-wrap;
- margin-top: var(--sp-3);
- }
- .infodesc.muted {
- color: var(--hp-muted);
- }
- .pdflist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- }
- .pdf {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- color: var(--hp-accent);
- text-decoration: none;
- }
- ha-icon-picker {
- display: block;
- }
- .floorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-2);
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .floorrow .floorlvl {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: 0 var(--sp-3);
- }
.sr-only {
position: absolute !important;
width: 1px !important;
@@ -3661,214 +2768,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
white-space: nowrap !important;
border: 0 !important;
}
- .rtest {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- margin-bottom: var(--sp-4);
- }
- .rtest .namein { flex: 1; }
- .rtest ha-icon { color: var(--hp-accent); }
- .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
- .rrow {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- margin: var(--sp-1) 0;
- }
- .rrow .rpat { flex: 2; }
- .rrow .ricon { flex: 1.4; }
- .rrow .rpat.bad { border-color: #ff7a5c; }
- .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
- .rrow .ract {
- --mdc-icon-size: 16px;
- color: var(--hp-muted);
- cursor: pointer;
- }
:host([data-pointer-hover]) .rrow .ract:hover { color: var(--hp-txt); }
:host([data-pointer-hover]) .rrow .ract.del:hover { color: #ff7a5c; }
- .gsrow .gsl {
- min-width: 150px;
- font-size: var(--fs-m);
- color: var(--hp-muted);
- }
.alignmsg { margin: 0 0 8px; font-size: 13px; line-height: 1.45; }
- .optimize-live {
- display: grid;
- justify-items: start;
- gap: var(--sp-2);
- margin-bottom: var(--sp-3);
- }
- .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
- .optimize-cleanup { min-height: 44px; }
- .optimize-selected { color: var(--hp-txt); }
- .optimize-details {
- margin-top: var(--sp-3);
- color: var(--hp-muted);
- font-size: var(--fs-s);
- overflow-wrap: anywhere;
- }
- .optimize-details > summary {
- width: fit-content;
- color: var(--hp-txt);
- cursor: pointer;
- font-weight: 600;
- }
- .optimize-details > summary:focus-visible {
- outline: 2px solid var(--hp-accent);
- outline-offset: 3px;
- border-radius: var(--rad-s);
- }
- .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
- .optimize-details li + li { margin-top: var(--sp-1); }
- .btn.alignall { width: 100%; justify-content: center; }
- .backupactions {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-3);
- }
- .backupactions .btn { justify-content: center; min-width: 0; }
- .backupupload { display: inline-flex; min-width: 0; }
- .backupupload > .btn { width: 100%; justify-content: center; }
- .backupupload input { display: none; }
- .backupbody { min-width: 0; }
- .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
- .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
- .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
- .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
- .backupfile, .backupsummary, .backupcontent {
- display: flex;
- flex-direction: column;
- min-width: 0;
- gap: var(--sp-1);
- }
- .backupfile b, .backupcontent span {
- overflow-wrap: anywhere;
- }
- .backupfile span, .backupsummary span, .backupcontent span {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
- .backupcounts {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-2) var(--sp-4);
- font-size: var(--fs-s);
- }
- .backupwarn, .backuperror {
- border-radius: var(--rad-s);
- padding: var(--sp-3);
- font-size: var(--fs-s);
- line-height: 1.45;
- overflow-wrap: anywhere;
- }
- .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
- .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
- .backupdetails {
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .backupdetails summary { cursor: pointer; font-weight: 700; }
- .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
- .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
- .backupchoices {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- min-width: 0;
- }
- .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
- .backupconfirm { align-items: flex-start !important; }
- .backupconfirm > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- @media (max-width: 520px) {
- .backupactions, .backupcounts { grid-template-columns: 1fr; }
- }
- .aboutver {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin: var(--sp-2) 0 var(--sp-3);
- }
- .aboutlink {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- width: fit-content;
- color: var(--hp-accent);
- text-decoration: none;
- font-size: var(--fs-m);
- padding: var(--sp-1) 0;
- }
:host([data-pointer-hover]) .aboutlink:hover { text-decoration: underline; }
- .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
- hp-dialog .body {
- padding: var(--sp-5) var(--sp-6);
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- }
- hp-dialog .tapconfirm-body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog .tapconfirm-body p {
- max-width: 100%;
- min-width: 0;
- margin: 0;
- overflow-wrap: anywhere;
- white-space: normal;
- }
- hp-dialog .tapconfirm-line {
- color: var(--hp-muted);
- }
- hp-dialog .body label {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin-top: var(--sp-3);
- }
- hp-dialog .body .namein,
- hp-dialog .body .areasel {
- width: 100%;
- box-sizing: border-box;
- }
- /* Room settings contains long radio labels, two source pickers and live
- previews, so it deliberately uses hp-dialog's medium width. Keep its
- content shrinkable as well: the generic srcrow rule is nowrap because
- compact switch rows need it, but here that created a horizontal scroll
- box even on a wide desktop viewport. */
- hp-dialog.roomdialog .body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog.roomdialog .body > * {
- min-width: 0;
- max-width: 100%;
- box-sizing: border-box;
- }
- hp-dialog.roomdialog .srcrow {
- min-width: 0;
- align-items: flex-start;
- }
- hp-dialog.roomdialog .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- line-height: 1.35;
- }
- hp-dialog.roomdialog .dropbtn > b,
- hp-dialog.roomdialog .dropbtn > .ref {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
.devicepreview-empty {
min-height: 82px;
margin: 4px 0 14px;
@@ -3882,84 +2785,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
color: var(--hp-muted);
text-align: center;
}
- hp-dialog .row {
- display: flex;
- justify-content: flex-end;
- gap: var(--sp-4);
- width: 100%;
- min-width: 0;
- box-sizing: border-box;
- padding: var(--sp-5) var(--sp-6);
- border-top: 1px solid var(--hp-line);
- }
- /* Stable destructive/commit footer contract. A flex spacer cannot react
- when translated labels no longer fit: justify-content then overflows
- the destructive button through the left inset. Two real groups wrap
- as units instead — destructive actions stay left, while Cancel/Save
- move together to a right-aligned second row when necessary. */
- hp-dialog .row.dialog-action-footer {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- row-gap: var(--sp-4);
- }
- hp-dialog .dialog-action-group {
- display: flex;
- flex: 0 1 auto;
- flex-wrap: wrap;
- align-items: center;
- gap: var(--sp-4);
- max-width: 100%;
- min-width: 0;
- }
- hp-dialog .dialog-action-group .btn {
- flex: 0 0 auto;
- min-height: 44px;
- }
- hp-dialog .dialog-action-danger {
- margin-right: auto;
- }
- hp-dialog .dialog-action-commit {
- margin-left: auto;
- justify-content: flex-end;
- }
- hp-dialog .row.markerfooter {
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- }
- hp-dialog .row.roomfooter {
- align-items: center;
- flex-wrap: wrap;
- }
- /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
- wrapping remains as a phone fallback, but without a flex spacer (which
- used to strand Edit alone on the first line). */
- hp-dialog .row.infofooter {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- gap: var(--sp-3);
- }
- hp-dialog .row.infofooter .btn {
- flex-shrink: 0;
- }
- hp-dialog .row.infofooter .infofooter-close {
- margin-left: auto;
- }
- @media (max-width: 480px) {
- hp-dialog .row.infofooter {
- padding: var(--sp-4) var(--sp-5);
- }
- }
- .markeractions,
- .markersaveactions {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .markeractions:empty { display: none; }
- .markersaveactions { margin-left: auto; }
:host([data-pointer-hover]) .menu .it:hover {
background: rgba(127, 127, 127, 0.15);
}
@@ -4356,6 +3181,1182 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
font-size: var(--fs-s);
display: block;
}
+`,o`
+ .head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px var(--sp-5);
+ border-bottom: 1px solid var(--hp-line);
+ flex-wrap: wrap;
+ }
+ .title {
+ font-size: var(--fs-l);
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ white-space: nowrap;
+ }
+ .title ha-icon {
+ color: var(--hp-accent);
+ --mdc-icon-size: 18px;
+ }
+ @media (max-width: 620px) {
+ .head { gap: var(--sp-3); padding: var(--sp-4) 10px; }
+ .head .count { display: none; }
+ .head .title { font-size: var(--fs-m); }
+ }
+ .count {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ }
+ .btn {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ padding: var(--sp-3) 10px;
+ border-radius: var(--rad-m);
+ cursor: pointer;
+ transition: 0.15s;
+ font-family: inherit;
+ font-size: var(--fs-m);
+ }
+ .btn ha-icon {
+ --mdc-icon-size: 17px;
+ }
+ .btn.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .btn.ghost {
+ border: none;
+ }
+ .btn[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ /* the compass dial in the general settings (docs/SUN.md) */
+ .sunrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ margin: var(--sp-3) 0;
+ }
+ .suncol {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ min-width: 0;
+ }
+ .recoveryoverlay {
+ position: absolute;
+ inset: 0;
+ /* Above the contextual editor tray (70): recovery is the one state in
+ which every stage-editing surface must be inert. Dialogs live outside
+ the stage and retain their own higher card-level stacking context. */
+ z-index: 75;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: var(--sp-4);
+ padding: var(--sp-6);
+ box-sizing: border-box;
+ /* The final solid layer guarantees an opaque recovery surface even when
+ a custom HA theme exposes its card colour as rgba(). */
+ background:
+ linear-gradient(var(--ha-card-background, var(--card-background-color, #111)),
+ var(--ha-card-background, var(--card-background-color, #111))),
+ #111;
+ color: var(--primary-text-color, #fff);
+ text-align: center;
+ pointer-events: auto;
+ transition: opacity 0.15s ease;
+ }
+ .recoveryoverlay.phase-entering,
+ .recoveryoverlay.phase-leaving {
+ opacity: 0;
+ }
+ .recoveryoverlay.phase-fading-in,
+ .recoveryoverlay.phase-opaque {
+ opacity: 1;
+ }
+ .recoveryoverlay ha-icon {
+ --mdc-icon-size: 44px;
+ color: var(--hp-accent);
+ }
+ .oplock {
+ --oplock-size: calc(var(--icon-size, 2.5cqw) * 0.62);
+ --oplock-core-size: calc(var(--oplock-size) / 1.26875);
+ --oplock-stroke-ratio: 0.01875;
+ --oplock-stroke-width: max(1px, calc(var(--oplock-core-size) * var(--oplock-stroke-ratio)));
+ --oplock-core-bg: light-dark(#fff, #252525);
+ --oplock-core-fg: light-dark(#252525, #fff);
+ --oplock-shell-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
+ --oplock-shell-shadow:
+ 0 calc(var(--oplock-core-size) * .025) calc(var(--oplock-core-size) * .05) rgb(37 40 45 / 12%),
+ 0 calc(var(--oplock-core-size) * .1) calc(var(--oplock-core-size) * .175)
+ calc(var(--oplock-core-size) * -.025) rgb(37 40 45 / 18%);
+ --oplock-core-shadow: 0 0 0 0 transparent;
+ pointer-events: none; /* inert while editing; clickable in View (rule below) */
+ position: absolute;
+ transform: translate(-50%, -50%);
+ width: var(--oplock-size);
+ height: var(--oplock-size);
+ display: grid;
+ place-items: center;
+ border: 0;
+ background: transparent;
+ z-index: 1;
+ }
+ .oplock.theme-light {
+ --oplock-core-bg: #fff;
+ --oplock-core-fg: #252525;
+ --oplock-shell-stroke: #BCBCBC;
+ }
+ .oplock.theme-dark {
+ --oplock-core-bg: #252525;
+ --oplock-core-fg: #fff;
+ --oplock-shell-stroke: rgb(37 37 37 / 75%);
+ --oplock-core-shadow:
+ inset 0 calc(var(--oplock-core-size) * .0125)
+ calc(var(--oplock-core-size) * .0125) rgb(255 255 255 / 70%);
+ }
+ .oplock-shell {
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ border: var(--oplock-stroke-width) solid var(--oplock-shell-stroke);
+ border-radius: 50%;
+ background: transparent;
+ box-shadow: var(--oplock-shell-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ backdrop-filter: none;
+ }
+ .oplock-core {
+ width: var(--oplock-core-size);
+ height: var(--oplock-core-size);
+ border-radius: 50%;
+ background: var(--oplock-core-bg);
+ color: var(--oplock-core-fg);
+ box-shadow: var(--oplock-core-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ }
+ .oplock ha-icon {
+ --mdc-icon-size: calc(var(--oplock-core-size) * 0.55);
+ display: flex;
+ line-height: 0;
+ }
+ .oplock.locked {
+ --oplock-core-bg: #66D17A;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #66D17A;
+ }
+ .oplock.theme-light.locked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.locked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unlocked {
+ --oplock-core-bg: #F0410C;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #F0410C;
+ }
+ .oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.unlocked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unknown { --oplock-core-fg: var(--hp-muted); }
+ .btn.lockact {
+ width: 100%;
+ justify-content: center;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin-top: var(--sp-4);
+ }
+ .btn.lockact.warn {
+ color: var(--error-color, #d33);
+ border-color: var(--error-color, #d33);
+ }
+ .oprow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) 0;
+ }
+ .oprow b { margin-left: auto; }
+ .oprow.ok b { color: #66d17a; }
+ .oprow.warn b { color: var(--hp-open); }
+ .bindharow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ flex-wrap: wrap;
+ }
+ .bindharow .entcheck { opacity: 0.9; }
+ .ctrlchips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; }
+ .ctrlchip {
+ display: inline-flex; align-items: center; gap: var(--sp-2);
+ background: var(--hp-accent); color: var(--text-primary-color, #fff);
+ border-radius: var(--rad-l); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-s);
+ }
+ .ctrlchip ha-icon { --mdc-icon-size: 14px; cursor: pointer; }
+ .ctrllist { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); }
+ .ctrlopt {
+ display: flex; align-items: center; gap: var(--sp-4); text-align: left;
+ border: 0; background: transparent; color: var(--hp-txt);
+ padding: var(--sp-3) var(--sp-4); border-radius: var(--rad-s); cursor: pointer; font-family: inherit; font-size: var(--fs-m);
+ }
+ .ctrlopt .sub { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
+ .ctrlopt ha-icon { --mdc-icon-size: 16px; }
+ .ctrlstates { display: flex; flex-direction: column; gap: var(--sp-2); }
+ .ctrlstate { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--hp-muted); }
+ .ctrlstate.on { color: var(--hp-txt); }
+ .ctrlstate ha-icon { --mdc-icon-size: 15px; }
+ .cardpreview {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--sp-2);
+ margin: var(--sp-4) 0 var(--sp-1);
+ padding: 10px;
+ border: 1px dashed var(--hp-muted);
+ border-radius: var(--rad-m);
+ }
+ .cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
+ .cardpreview .cpmeta {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.3em;
+ font-weight: 600;
+ opacity: 0.85;
+ }
+ .cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
+ .iconauto {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-s);
+ margin: var(--sp-2) 0 0;
+ }
+ .iconauto ha-icon { --mdc-icon-size: 18px; }
+ .iconauto span { flex: 1; }
+ .iconauto .btn { min-height: 32px; padding: 0 var(--sp-3); }
+ hp-dialog .dfill {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ cursor: pointer;
+ }
+ .wallthick-dlg {
+ position: absolute;
+ z-index: 40;
+ min-width: 200px;
+ transform: translate(-50%, 8px);
+ padding: 10px 12px;
+ border-radius: 10px;
+ background: var(--card-background-color, #fff);
+ box-shadow: 0 8px 28px rgba(0,0,0,.22);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ pointer-events: auto;
+ }
+ .wallthick-dlg .row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .wallthick-dlg input[type="number"] {
+ width: 5.5em;
+ padding: 4px 6px;
+ border: 1px solid var(--divider-color, #ccc);
+ border-radius: 6px;
+ background: var(--input-fill, transparent);
+ color: var(--primary-text-color);
+ }
+ .areasel,
+ .namein {
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .namein {
+ width: 130px;
+ }
+ .habindingbanner {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ padding: var(--sp-3) var(--sp-4);
+ margin-bottom: var(--sp-4);
+ border: 1px solid var(--warning-color, #ff9800);
+ border-radius: var(--rad-m);
+ background: color-mix(in srgb, var(--warning-color, #ff9800) 12%, transparent);
+ color: var(--primary-text-color, #f1f3f6);
+ }
+ .habindingbanner > span { flex: 1; min-width: 0; }
+ .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
+ .habindingbanner.limited {
+ border-color: var(--secondary-text-color, #9aa0aa);
+ background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
+ }
+ .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
+ .srcrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ padding: var(--sp-1) 0;
+ }
+ .dispsection {
+ margin-top: var(--sp-5) !important;
+ padding-top: var(--sp-4);
+ border-top: 1px solid var(--hp-line);
+ font-weight: 600;
+ color: var(--hp-txt) !important;
+ }
+ .colorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .gsrow > hp-color-opacity {
+ min-width: min(100%, 210px);
+ justify-content: space-between;
+ }
+ .ripple-colorrow > hp-color-opacity {
+ width: 100%;
+ justify-content: space-between;
+ }
+ .ripple-sizerow > .opl {
+ min-width: 0;
+ }
+ .colorrow input[type='range'] { flex: 1; }
+ .colorrow .tempin { width: 70px; flex: none; }
+ /* beat the generic hp-dialog .body .namein { width:100% } rule */
+ hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
+ hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
+ .srcrow { flex-wrap: nowrap; }
+ /* native HA controls (rendered only when the HA frontend defines them;
+ old HA and the smoke env keep the plain inputs). ha-switch is taller
+ than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
+ .srcrow ha-switch { flex: none; }
+ .colorrow ha-slider { flex: 1; min-width: 0; }
+ .srcrow > span:first-of-type { white-space: nowrap; }
+ .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
+ .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
+ .markerlightgroup {
+ min-width: 0;
+ margin: var(--sp-5) 0 0;
+ padding: var(--sp-4);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ }
+ .markerlightgroup legend {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ padding: 0 var(--sp-2);
+ color: var(--hp-txt);
+ font-weight: 600;
+ }
+ .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
+ .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
+ .markerhelpfield { margin-top: var(--sp-4); }
+ .markerhelplabel {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ margin-bottom: var(--sp-1);
+ }
+ .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
+ .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
+ .markerlightgroup .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .markerglowvalue hp-color-opacity { flex: none; }
+ .markerlightdisabled {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ margin-top: var(--sp-2) !important;
+ }
+ .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
+ .markerbadgetechnical {
+ min-width: 0;
+ margin: var(--sp-1) 0 var(--sp-2) !important;
+ overflow-wrap: anywhere;
+ }
+ .markerbadgetechnical code { white-space: normal; }
+ .planrow {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ /* the "already uploaded" picker: a plan is never deleted for being
+ unreferenced, so it has to be findable again */
+ .savedplans {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ max-height: 240px;
+ overflow: auto;
+ margin: var(--sp-3) 0 var(--sp-1);
+ padding: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
+ /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
+ flex item whose automatic minimum size is ZERO (overflow != visible),
+ so inside hp-dialog .body — a flex column taller than its 66vh cap —
+ it shrank to a 14px sliver: the rows were in the DOM, the owner saw
+ a thin rounded stripe under the "Already uploaded" button. Don't
+ shrink, and keep a floor even when the box is empty or loading. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .savedplan {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
+ .savedplan img {
+ width: 56px;
+ height: 40px;
+ object-fit: contain;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ flex: none;
+ }
+ .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
+ .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+ .savedmeta .muted { font-size: var(--fs-s); }
+ .savedplan .btn.danger ha-icon { color: #f25a4a; }
+ .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
+ .planprev {
+ max-width: 120px;
+ max-height: 70px;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ }
+ .planname {
+ font-size: var(--fs-m);
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .planname.muted {
+ color: var(--hp-muted);
+ }
+ .filebtn {
+ cursor: pointer;
+ }
+ .fileupload { display: inline-flex; min-width: 0; }
+ .fileupload > input { display: none; }
+ .btn.danger {
+ border-color: #b3402a;
+ color: #ff7a5c;
+ }
+ hp-dialog .row .spacer {
+ flex: 1;
+ }
+ hp-dialog .body {
+ max-height: 66vh;
+ overflow-y: auto;
+ }
+ .descin {
+ width: 100%;
+ box-sizing: border-box;
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ resize: vertical;
+ /* flex column of the dialog body squeezes textareas — keep a usable height */
+ min-height: 92px;
+ flex-shrink: 0;
+ line-height: 1.35;
+ }
+ .bindsel {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ padding: var(--sp-4);
+ }
+ .bindsel .opt {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .bindsel .opt.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .curbind {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-m);
+ color: var(--hp-txt);
+ flex-wrap: wrap;
+ }
+ .curbind .ref {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
+ .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
+ .vacdiag > div > span { color: var(--secondary-text-color); }
+ .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
+ .vacpicker { margin: var(--sp-3) 0; }
+ .vacsource-warning { display: grid; gap: 8px; }
+ .vacsource-warning .btn { justify-self: start; }
+ .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
+ .vacpicker > summary::-webkit-details-marker { display: none; }
+ .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
+ .vacsource-list details { padding: 6px 0 0; }
+ .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
+ .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
+ width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
+ border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
+ text-align: left; cursor: pointer; }
+ .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
+ .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
+ .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
+ .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
+ .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
+ .vacfit {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 12;
+ overflow: visible;
+ touch-action: none;
+ cursor: grab;
+ /* the devlayer is pointer-events: none and every child opts back in —
+ without this line real clicks flew straight through the overlay
+ (owner: «уголки не кликабельны»; synthetic smoke events bypass
+ hit-testing, which is why they lied) */
+ pointer-events: auto;
+ }
+ .vacfit:active { cursor: grabbing; }
+ .vacfit polygon {
+ fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ stroke-dasharray: 6 4;
+ }
+ .vacfit text {
+ fill: var(--hp-accent);
+ font-size: 26px;
+ text-anchor: middle;
+ dominant-baseline: middle;
+ pointer-events: none;
+ user-select: none;
+ }
+ /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
+ .furnpalette {
+ display: flex;
+ flex-direction: column;
+ max-height: 38vh;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ background: var(--card-background-color, var(--hp-bg));
+ font-size: 0.85em;
+ }
+ .furnhd {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 8px;
+ font-weight: 600;
+ opacity: 0.9;
+ }
+ .furnhd .spacer { flex: 1; }
+ .furnbody {
+ overflow: auto;
+ padding: 0 8px 6px;
+ }
+ .furngroup {
+ margin: 6px 0 3px;
+ font-size: 0.85em;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ opacity: 0.6;
+ }
+ .furnrow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
+ .furnitem {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ width: 74px;
+ padding: 4px 2px;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ background: rgba(127, 127, 127, 0.08);
+ color: inherit;
+ font: inherit;
+ font-size: 0.8em;
+ line-height: 1.15;
+ text-align: center;
+ cursor: pointer;
+ }
+ .furnitem.on {
+ border-color: var(--hp-accent);
+ background: rgba(38, 166, 154, 0.18);
+ }
+ .furnprev {
+ width: 40px;
+ height: 40px;
+ color: var(--primary-text-color, currentColor);
+ }
+ .furnsize {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 6px;
+ padding: 6px 8px;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ }
+ .furnsize label {
+ display: flex;
+ align-items: baseline;
+ gap: 3px;
+ opacity: 0.8;
+ }
+ .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
+ .furnsize input {
+ width: 5.5em;
+ padding: 3px 5px;
+ }
+ .furnhint { opacity: 0.6; }
+ .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
+ /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
+ .vacfithandle {
+ fill: transparent;
+ stroke: none;
+ cursor: nwse-resize;
+ }
+ .vacfitknob {
+ fill: var(--hp-bg);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ pointer-events: none;
+ }
+ .vaccalbar {
+ position: fixed;
+ left: 50%;
+ bottom: 24px;
+ transform: translateX(-50%);
+ display: flex;
+ gap: var(--sp-5);
+ align-items: center;
+ background: var(--hp-bg);
+ color: var(--hp-txt);
+ border: 1px solid var(--hp-accent);
+ border-radius: var(--rad-l);
+ padding: 10px var(--sp-5);
+ z-index: 60;
+ box-shadow: var(--shadow-2);
+ }
+ .candlist {
+ max-height: 160px;
+ overflow-y: auto;
+ border-top: 1px solid var(--hp-line);
+ /* A scrollable box is a flex item that HAPPILY collapses: inside the
+ dialog body (a flex column) this list rendered its rows into a 1px
+ sliver — the DOM had 26 candidates and the user saw nothing. In the
+ binding dropdown it sits inside .droppanel (block context) and never
+ showed the bug. Field report, 2026-07-30. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .cand {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ border-radius: var(--rad-s);
+ font-size: var(--fs-m);
+ }
+ .cand.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .cand .cs {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ white-space: nowrap;
+ }
+ .opening-entity-candidate {
+ width: 100%;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ text-align: left;
+ }
+ .opening-entity-candidate.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .opening-entity-empty { cursor: default; }
+ .cand.sel .cs {
+ color: var(--text-primary-color, #fff);
+ opacity: 0.85;
+ }
+ .cand.muted {
+ color: var(--hp-muted);
+ cursor: default;
+ }
+ .pdfedit {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ align-items: center;
+ }
+ .pdftag {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .pdftag a {
+ color: var(--hp-txt);
+ text-decoration: none;
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .pdftag .x {
+ --mdc-icon-size: 15px;
+ cursor: pointer;
+ color: var(--hp-muted);
+ }
+ .entlist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ margin-bottom: 10px;
+ }
+ .entrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ border-radius: var(--rad-m);
+ background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
+ }
+ .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
+ .entrow.on ha-icon { color: var(--hp-accent); }
+ .entrow .en { flex: 1; font-size: var(--fs-m); }
+ .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
+ .entbtn {
+ min-width: 74px;
+ min-height: 32px;
+ padding: var(--sp-2) var(--sp-5);
+ border: 1px solid var(--hp-muted);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--hp-txt);
+ font: inherit;
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .entbtn.on {
+ background: var(--hp-accent);
+ border-color: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ font-weight: 600;
+ }
+ .inforow {
+ display: flex;
+ gap: 10px;
+ font-size: var(--fs-m);
+ margin: var(--sp-2) 0;
+ }
+ .inforow .k {
+ color: var(--hp-muted);
+ min-width: 84px;
+ }
+ .inforow a {
+ color: var(--hp-accent);
+ word-break: break-all;
+ }
+ .infodesc {
+ font-size: var(--fs-m);
+ white-space: pre-wrap;
+ margin-top: var(--sp-3);
+ }
+ .infodesc.muted {
+ color: var(--hp-muted);
+ }
+ .pdflist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ }
+ .pdf {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ color: var(--hp-accent);
+ text-decoration: none;
+ }
+ ha-icon-picker {
+ display: block;
+ }
+ .floorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-2);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .floorrow .floorlvl {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: 0 var(--sp-3);
+ }
+ .rtest {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ margin-bottom: var(--sp-4);
+ }
+ .rtest .namein { flex: 1; }
+ .rtest ha-icon { color: var(--hp-accent); }
+ .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
+ .rrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin: var(--sp-1) 0;
+ }
+ .rrow .rpat { flex: 2; }
+ .rrow .ricon { flex: 1.4; }
+ .rrow .rpat.bad { border-color: #ff7a5c; }
+ .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
+ .rrow .ract {
+ --mdc-icon-size: 16px;
+ color: var(--hp-muted);
+ cursor: pointer;
+ }
+ .gsrow .gsl {
+ min-width: 150px;
+ font-size: var(--fs-m);
+ color: var(--hp-muted);
+ }
+ .optimize-live {
+ display: grid;
+ justify-items: start;
+ gap: var(--sp-2);
+ margin-bottom: var(--sp-3);
+ }
+ .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
+ .optimize-cleanup { min-height: 44px; }
+ .optimize-selected { color: var(--hp-txt); }
+ .optimize-details {
+ margin-top: var(--sp-3);
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ overflow-wrap: anywhere;
+ }
+ .optimize-details > summary {
+ width: fit-content;
+ color: var(--hp-txt);
+ cursor: pointer;
+ font-weight: 600;
+ }
+ .optimize-details > summary:focus-visible {
+ outline: 2px solid var(--hp-accent);
+ outline-offset: 3px;
+ border-radius: var(--rad-s);
+ }
+ .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
+ .optimize-details li + li { margin-top: var(--sp-1); }
+ .btn.alignall { width: 100%; justify-content: center; }
+ .backupactions {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-3);
+ }
+ .backupactions .btn { justify-content: center; min-width: 0; }
+ .backupupload { display: inline-flex; min-width: 0; }
+ .backupupload > .btn { width: 100%; justify-content: center; }
+ .backupupload input { display: none; }
+ .backupbody { min-width: 0; }
+ .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
+ .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
+ .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
+ .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
+ .backupfile, .backupsummary, .backupcontent {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ gap: var(--sp-1);
+ }
+ .backupfile b, .backupcontent span {
+ overflow-wrap: anywhere;
+ }
+ .backupfile span, .backupsummary span, .backupcontent span {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .backupcounts {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-2) var(--sp-4);
+ font-size: var(--fs-s);
+ }
+ .backupwarn, .backuperror {
+ border-radius: var(--rad-s);
+ padding: var(--sp-3);
+ font-size: var(--fs-s);
+ line-height: 1.45;
+ overflow-wrap: anywhere;
+ }
+ .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
+ .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
+ .backupdetails {
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .backupdetails summary { cursor: pointer; font-weight: 700; }
+ .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
+ .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
+ .backupchoices {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ min-width: 0;
+ }
+ .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
+ .backupconfirm { align-items: flex-start !important; }
+ .backupconfirm > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ @media (max-width: 520px) {
+ .backupactions, .backupcounts { grid-template-columns: 1fr; }
+ }
+ .aboutver {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin: var(--sp-2) 0 var(--sp-3);
+ }
+ .aboutlink {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ width: fit-content;
+ color: var(--hp-accent);
+ text-decoration: none;
+ font-size: var(--fs-m);
+ padding: var(--sp-1) 0;
+ }
+ .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
+ hp-dialog .body {
+ padding: var(--sp-5) var(--sp-6);
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ }
+ hp-dialog .tapconfirm-body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog .tapconfirm-body p {
+ max-width: 100%;
+ min-width: 0;
+ margin: 0;
+ overflow-wrap: anywhere;
+ white-space: normal;
+ }
+ hp-dialog .tapconfirm-line {
+ color: var(--hp-muted);
+ }
+ hp-dialog .body label {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin-top: var(--sp-3);
+ }
+ hp-dialog .body .namein,
+ hp-dialog .body .areasel {
+ width: 100%;
+ box-sizing: border-box;
+ }
+ /* Room settings contains long radio labels, two source pickers and live
+ previews, so it deliberately uses hp-dialog's medium width. Keep its
+ content shrinkable as well: the generic srcrow rule is nowrap because
+ compact switch rows need it, but here that created a horizontal scroll
+ box even on a wide desktop viewport. */
+ hp-dialog.roomdialog .body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog.roomdialog .body > * {
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ }
+ hp-dialog.roomdialog .srcrow {
+ min-width: 0;
+ align-items: flex-start;
+ }
+ hp-dialog.roomdialog .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ line-height: 1.35;
+ }
+ hp-dialog.roomdialog .dropbtn > b,
+ hp-dialog.roomdialog .dropbtn > .ref {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ hp-dialog .row {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--sp-4);
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+ padding: var(--sp-5) var(--sp-6);
+ border-top: 1px solid var(--hp-line);
+ }
+ /* Stable destructive/commit footer contract. A flex spacer cannot react
+ when translated labels no longer fit: justify-content then overflows
+ the destructive button through the left inset. Two real groups wrap
+ as units instead — destructive actions stay left, while Cancel/Save
+ move together to a right-aligned second row when necessary. */
+ hp-dialog .row.dialog-action-footer {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ row-gap: var(--sp-4);
+ }
+ hp-dialog .dialog-action-group {
+ display: flex;
+ flex: 0 1 auto;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--sp-4);
+ max-width: 100%;
+ min-width: 0;
+ }
+ hp-dialog .dialog-action-group .btn {
+ flex: 0 0 auto;
+ min-height: 44px;
+ }
+ hp-dialog .dialog-action-danger {
+ margin-right: auto;
+ }
+ hp-dialog .dialog-action-commit {
+ margin-left: auto;
+ justify-content: flex-end;
+ }
+ hp-dialog .row.markerfooter {
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ hp-dialog .row.roomfooter {
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
+ wrapping remains as a phone fallback, but without a flex spacer (which
+ used to strand Edit alone on the first line). */
+ hp-dialog .row.infofooter {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ }
+ hp-dialog .row.infofooter .btn {
+ flex-shrink: 0;
+ }
+ hp-dialog .row.infofooter .infofooter-close {
+ margin-left: auto;
+ }
+ @media (max-width: 480px) {
+ hp-dialog .row.infofooter {
+ padding: var(--sp-4) var(--sp-5);
+ }
+ }
+ .markeractions,
+ .markersaveactions {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .markeractions:empty { display: none; }
+ .markersaveactions { margin-left: auto; }
`],Yt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Jt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|thermometer|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function Xt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const Zt=Xt(Jt),Qt={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},te="mdi:chip";function ee(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??Zt)if(t.test(s))return e;return te}const ie=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var se=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,ne=Math.ceil,oe=Math.floor,ae="[BigNumber Error] ",re=ae+"Number primitive has more than 15 significant digits: ",le=1e14,ce=14,he=9007199254740991,de=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],pe=1e7,ue=1e9;function _e(t){var e=0|t;return t>0||t===e?e:e-1}function me(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function fe(t,e,i,s){if(ti||t!==oe(t))throw Error(ae+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function ve(t){var e=t.c.length-1;return _e(t.e/ce)==e&&t.c[e]%2!=0}function ye(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function be(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else ey?p.c=p.e=null:t.e=10;l/=10,r++);return void(r>y?p.c=p.e=null:(p.e=r,p.c=[t]))}d=String(t)}else{if(!se.test(d=String(t)))return n(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(r=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(r<0&&(r=l),r+=+d.slice(l+1),d=d.substring(0,l)):r<0&&(r=d.length)}else{if(fe(e,2,$.length,"Base"),10==e&&S)return T(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(re+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,e),r=l=0,h=d.length;lr){r=h;continue}}else if(!a&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){a=!0,l=-1,r=0;continue}return n(p,String(t),c,e)}c=!1,(r=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):r=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>he||t!==oe(t)))throw Error(re+p.s*t);if((r=r-l-1)>y)p.c=p.e=null;else if(r=f)?ye(l,a):be(l,a,"0");else if(o=(t=T(new M(t),e,i)).e,r=(l=me(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ra),l=be(l,o,"0"),o+1>r){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-r)>0)for(o+1==r&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*ce-1)>y?t.c=t.e=null:i=10;r/=10,n++);if((o=e-n)<0)o+=ce,a=e,l=d[c=0],h=oe(l/p[n-a-1]%10);else if((c=ne((o+1)/ce))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ce)-ce+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ce)-ce+n)<0?0:oe(l/p[n-a-1]%10)}if(s=s||e<0||null!=d[c+1]||(a<0?l:l%p[n-a-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?a>0?l/p[n-a]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(ce-e%ce)%ce],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=p[ce-o],d[c]=a>0?oe(l/p[n-a]%p[a])*r:0),s)for(;;){if(0==c){for(o=1,a=d[0];a>=10;a/=10,o++);for(a=d[0]+=r,r=1;a>=10;a/=10,r++);o!=r&&(t.e++,d[0]==le&&(d[0]=1));break}if(d[c]+=r,d[c]!=le)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?ye(e,i):be(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(ae+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(fe(i=t[e],0,ue,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(fe(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(fe(i[0],-ue,0,e),fe(i[1],0,ue,e),g=i[0],f=i[1]):(fe(i,-ue,ue,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)fe(i[0],-ue,-1,e),fe(i[1],1,ue,e),v=i[0],y=i[1];else{if(fe(i,-ue,ue,e),!i)throw Error(ae+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(ae+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(ae+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(fe(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(fe(i=t[e],0,ue,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(ae+e+" not an object: "+i);x=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(ae+e+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-ue&&n<=ue&&n===oe(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%ce)<1&&(e+=ce),String(s[0]).length==e){for(e=0;e=le||i!==oe(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(ae+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,a=Math.random()*o&2097151?function(){return oe(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,r=0,l=[],c=new M(u);if(null==t?t=_:fe(t,0,ue),n=ne(t/ce),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));r>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[r]=i[0],e[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(ae+"crypto unavailable");for(e=crypto.randomBytes(n*=7);r=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-1&&(null==a[n+1]&&(a[n+1]=0),a[n+1]+=a[n]/i|0,a[n]%=i)}return a.reverse()}return function(s,n,o,a,r){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(be(me(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,r?(l=$,t):(l=t,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=a,g=(u=i(u,f,y,b,o)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=o/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?be(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=be(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,a,r=0,l=t.length,c=e%pe,h=e/pe|0;for(t=t.slice();l--;)r=((n=c*(o=t[l]%pe)+(s=h*o+(a=t[l]/pe|0)*c)%pe*pe+r)/i|0)+(s/pe|0)+h*a,t[l]=n%i;return r&&(t=[r].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,a,r){var l,c,h,d,p,u,_,m,g,f,v,y,b,w,k,x,$,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=o+(c=s.e-n.e)+1,r||(r=le,c=_e(s.e/ce)-_e(n.e/ce),S=S/ce|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,x=D.length,h=0,S+=2,(p=oe(r/(D[0]+1)))>1&&(D=t(D,p,r),C=t(C,p,r),x=D.length,w=C.length),b=x,v=(f=C.slice(0,x)).length;v=r/2&&k++;do{if(p=0,(l=e(D,f,x,v))<0){if(y=f[0],x!=v&&(y=y*r+(f[1]||0)),(p=oe(y/k))>1)for(p>=r&&(p=r-1),_=(u=t(D,p,r)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,x<_?$:D,_,r),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);T(m,o+(m.e=h+c*ce-1)+1,a,d)}else m.e=c,m.r=+d;return m}}(),r=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(r,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(ae+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return ge(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return fe(t,0,ue),null==e?e=m:fe(e,0,8),T(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-_e(this.e/ce))*ce,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,n,o,a,r,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(ae+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),a=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),a?t.s*(2-ve(t)):+R(t))),e?c.mod(e):c;if(r=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!r&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||a&&h.c[1]>=24e7:h.c[0]<8e13||a&&h.c[0]<=9999975e7)))return o=h.s<0&&ve(t)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=ne(k/ce+2))}for(a?(i=new M(.5),r&&(t.s=1),l=ve(t)):l=(n=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=oe(n/2)))break;l=n%2}else if(T(t=t.times(i),t.e+1,1),t.e>14)l=ve(t);else{if(0===(n=+R(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(r&&(c=u.div(c)),e?c.mod(e):o?T(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:fe(t,0,8),T(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===ge(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return ge(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=ge(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&_e(this.e/ce)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return ge(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=ge(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,n,o,a=this,r=a.s;if(e=(t=new M(t,e)).s,!r||!e)return new M(NaN);if(r!=e)return t.s=-e,a.plus(t);var l=a.e/ce,c=t.e/ce,h=a.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?a:3==m?-0:0)}if(l=_e(l),c=_e(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),e=r;e--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(e=d.length))?r:e,r=e=0;e0)for(;e--;h[i++]=0);for(e=le-1;s>r;){if(h[--s]=0;){for(i=0,p=y[n]%g,u=y[n]/g|0,o=n+(a=l);o>n;)i=((c=p*(c=v[--a]%g)+(r=u*c+(h=v[a]/g|0)*p)%g*g+_[o]+i)/m|0)+(r/g|0)+u*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/ce,a=t.e/ce,r=s.c,l=t.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?t:new M(r[0]?s:0*n)}if(o=_e(o),a=_e(a),r=r.slice(),n=o-a){for(n>0?(a=o,i=l):(n=-n,i=r),i.reverse();n--;i.push(0));i.reverse()}for((n=r.length)-(e=l.length)<0&&(i=l,l=r,r=i,e=n),n=0;e;)n=(r[--e]=r[e]+l[e]+n)/le|0,r[e]=le===r[e]?0:r[e]%le;return n&&(r=[n].concat(r),++a),P(t,r,a)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return fe(t,1,ue),null==e?e=m:fe(e,0,8),T(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*ce+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return fe(t,-9007199254740991,he),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,a=this,r=a.c,l=a.s,c=a.e,h=_+4,d=new M("0.5");if(1!==l||!r||!r[0])return new M(!l||l<0&&(!r||r[0])?NaN:r?a:1/0);if(0==(l=Math.sqrt(+R(a)))||l==1/0?(((e=me(r)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=_e((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(a,o,h,1))),me(o.c).slice(0,l)===(e=me(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%r||r,h=u.substr(0,o);o<_;o+=r)h+=c+u.substr(o,r);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,a,r,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(ae+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=me(f),a=e.e=_.length-g.e-1,e.c[0]=de[(r=a%ce)<0?ce+r:r],t=!t||l.comparedTo(e)>0?a>0?e:h:l,r=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=g.s,p=i(h,n,a*=2,m).minus(g).abs().comparedTo(i(c,s,a,m).minus(g).abs())<1?[h,n]:[c,s],y=r,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&fe(t,1,ue),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=g||o>=f?ye(me(i.c),o):be(me(i.c),o,"0"):10===t&&S?e=be(me((i=T(new M(i),_+o+1,m)).c),i.e,"0"):(fe(t,2,$.length,"Base"),e=s(be(me(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),ke=class{key;left=null;right=null;constructor(t){this.key=t}},xe=class extends ke{constructor(t){super(t)}},$e=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,n=null,o=null,a=e;const r=this.compare;let l;for(;;)if(l=r(a.key,t),l>0){let e=a.left;if(null==e)break;if(l=r(e.key,t),l>0&&(a.left=e.right,e.right=a,a=e,e=a.left,null==e))break;null==i?s=a:i.left=a,i=a,a=e}else{if(!(l<0))break;{let e=a.right;if(null==e)break;if(l=r(e.key,t),l<0&&(a.right=e.left,e.left=a,a=e,e=a.right,null==e))break;null==n?o=a:n.right=a,n=a,a=e}}return null!=n&&(n.right=a.left,a.left=o),null!=i&&(i.left=a.right,a.right=s),this.root!==a&&(this.root=a,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Se=class t extends $e{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new xe(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new xe(s.key);i.left=e,t(s,e)}if(null!=n){const t=new xe(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new De(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ce(this.wrap())}[Symbol.toStringTag]="[object Set]"},Me=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Ce=class extends Me{getValue(t){return t.key}},De=class extends Me{getValue(t){return[t.key,t.key]}},Pe=t=>()=>t,Te=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Pe(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Re(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Pe(!1);return(t,i,s)=>{const n=t.x,o=t.y,a=s.x,r=s.y,l=o.minus(r).times(i.x.minus(a)).minus(n.minus(a).times(i.y.minus(r)));return e(l,n,o,a,r)?0:l.comparedTo(0)}}var Ie=t=>t,Ae=t=>{if(t){const e=new Se(Te(t)),i=new Se(Te(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new we(0),y:new we(0)}),n}return Ie},ze=t=>({set:t=>{Fe=ze(t)},reset:()=>ze(t),compare:Te(t),snap:Ae(t),orient:Re(t)}),Fe=ze(),Ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),Ne=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},Oe=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),He=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),Le=t=>He(t,t).sqrt(),Be=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Oe(n,s).div(Le(n)).div(Le(s))},qe=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return He(n,s).div(Le(n)).div(Le(s))},We=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},je=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},Ue=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:Qe.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:Be(this.point,t.point,s.point),cosine:qe(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:a,cosine:r}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&a.isGreaterThanOrEqualTo(0)?o.isLessThan(r)?1:o.isGreaterThan(r)?-1:0:n.isLessThan(0)&&a.isLessThan(0)?o.isLessThan(r)?-1:o.isGreaterThan(r)?1:0:a.isLessThan(n)?-1:a.isGreaterThan(n)?1:0}}},Ge=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},Ve=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&n){const t=s.getIntersection(n);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(a.isLessThan(r)&&a.isLessThan(c))return-1;if(a.isGreaterThan(r)&&a.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(a.isLessThan(r))return-1;if(a.isGreaterThan(r))return 1;if(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(a),e=n.minus(i),h=c.minus(r),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Ze,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let n,o,a;const r=Ue.comparePoints(e,i);if(r<0)n=e,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,a=-1}const l=new Ue(n,!0),c=new Ue(o,!1);return new t(l,c,[s],[a])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return Fe.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Ne(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=t.leftSE.point,r=t.rightSE.point,l=Ee(e,a)&&0===this.comparePoint(a),c=Ee(i,n)&&0===t.comparePoint(n),h=Ee(e,r)&&0===this.comparePoint(r),d=Ee(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?r:null;if(c)return h&&n.x.eq(r.x)&&n.y.eq(r.y)?null:n;if(l)return d&&o.x.eq(a.x)&&o.y.eq(a.y)?null:a;if(d&&h)return null;if(d)return o;if(h)return r;const p=((t,e,i,s)=>{if(e.x.isZero())return je(i,s,t.x);if(s.x.isZero())return je(t,e,i.x);if(e.y.isZero())return We(i,s,t.y);if(s.y.isZero())return We(t,e,i.y);const n=Oe(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},a=Oe(o,e).div(n),r=Oe(o,s).div(n),l=t.x.plus(r.times(e.x)),c=i.x.plus(a.times(s.x)),h=t.y.plus(r.times(e.y)),d=i.y.plus(a.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),a,t.vector());return null===p?null:Ee(s,p)?Fe.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new Ue(e,!0),o=new Ue(e,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new t(n,a,this.rings.slice(),this.windings.slice());return Ue.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ue.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ti=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=Fe.snap({x:new we(t[0][0]),y:new we(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;eXe.run("union",t,e),ni=(t,...e)=>Xe.run("intersection",t,e),oi=(t,...e)=>Xe.run("difference",t,e);function ai(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ri(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function li(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function ci(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),a=e[1].toFixed(i),r=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function di(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function pi(t){const e=[],i=new Set;for(const s of t||[]){const t=hi(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(s.length||0,0)/2,i/2);let r=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(r=i/2),r=Math.max(o,Math.min(i-o,r));const l=r/i;n={x:e+l*c,y:a+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function _i(t,e,i,s,n,o=1){const a=e*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const e of pi(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(r[0]*(e[1]-t[1])-r[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*r[0]+(i[0][1]-t[1])*r[1],a=(i[1][0]-t[0])*r[0]+(i[1][1]-t[1])*r[1],h=Math.min(n,a),d=Math.max(n,a),p=h>0?h:d<0?-d:0;p[t[0]+r[0]*e,t[1]+r[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function mi(t,e,i=!1){if("passage"===t)return 1;if(null==e||"unavailable"===e||"unknown"===e)return"window"===t?0:1;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function gi(t){return"passage"===t.type?[]:[t.contact,t.lock].filter(t=>"string"==typeof t&&t.length>0)}function fi(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}function yi(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let a=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+a*s),t[1]-(e[1]+a*n))}function bi(t,e,i=1e-6){if(!e||e.length<2)return!1;for(let s=0;sn&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function $i(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(a+r)/2],u=(e,i)=>{const s=((e,i)=>{if(!vi([e,i],t))return-1/0;let s=1/0;for(let n=0;nm&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(r-a)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,a=i+l*n/4,r=u(o,a);r>m&&(m=r,_=[o,a])}}return _||Si(t)||t[0]}function Si(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(wi(s,t,e))return s;for(let s=0;sCi(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ci(e,t,i)))}function Ti(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ii(t,e,i){for(let s=0;s1&&fi(i[0],i[i.length-1],e)&&i.pop(),i}function zi(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(fi(s,n,i))return null;const o=Ii(t,s,i),a=Ii(t,n,i);if(o<0||a<0)return null;const r=e.slice(1,-1);for(const e of r)if(!wi(e,t,i))return null;for(let i=0;i{const a=[e];let r=(s+1)%t.length;for(let e=0;e<=t.length&&(a.push(t[r]),r!==o);e++)r=(r+1)%t.length;return a.push(n),Ai(a,i)};let c,h;if(o===a){const a=Ai([...e],i);if(a.length<3||Ti(a)<=i)return null;const r=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)r.push(t)}c=Ai(r,i),h=a}else c=Ai([...l(s,o,n,a),...[...r].reverse()],i),h=Ai([...l(n,a,s,o),...r],i);return c.length<3||h.length<3||Ti(c)<=i||Ti(h)<=i||Math.abs(Ti(c)+Ti(h)-Ti(t))>Math.max(i,1e-6*Ti(t))?null:[c,h]}function Fi(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function Ei(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Ni(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function Oi(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,a=e.y+s,r=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;s=0)i=e.slice(0,n).trim(),s=e.slice(n+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?n>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Ji(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=Yi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Xi(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),a=t=>t.slice(0,60);if(o){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Zi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${o} ${n}`}function Qi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return Vi;const o=(e?.attr||"").trim(),a=Xi(t,i,o||null);if(null===a)return Vi;return Zi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}function ts(t,e,i,s=()=>!0){const n=t??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=Yi(e);return n?(o=!0,s(n.entity||"")?Qi(i,n):Vi):t});if(o)return a;const r=(e?.entity||"").trim();if(!r)return n;if(!s(r)){const t=n.indexOf(Ki);return t>=0?n.slice(0,t)+Vi+n.slice(t+2):n?`${n} ${Vi}`:Vi}const l=Qi(i,e),c=n.indexOf(Ki);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const es=20;function is(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function ss(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ns="#55606c",os=.55,as={c:"#607d8b",a:.18};function rs(t,e=as){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:Rt(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function ls(t,e){const i=rs(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?rs(s,i):i}function cs(t){const e=t?.settings||{},i=!t?.plan_url,s="glow"===e.fill_mode;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:Rt(e.room_color,ns),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):os,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:rs(e.custom_fill),glow:"boolean"==typeof e.glow_enabled?e.glow_enabled:s,tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:Rt(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function hs(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return Rt(i,"")}const ds={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function ps(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(ds)){const s=ds[t],n=i[t];e[t]={c:Rt(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function us(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),a=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+a.map(t=>t.toString(16).padStart(2,"0")).join("")}function _s(t,e,i,s,n,o,a,r=as){const l=function(t,e,i,s,n,o,a,r=as){if("custom"===t)return rs(r);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:us(a.lqi_low.c,a.lqi_high.c,t),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?a.temp_hot:a.temp_ok}return null}(t,e,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:t}:null}const ms={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},gs=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function fs(t){for(const e of[...Object.values(ms),...gs])if(t===e[0]||t===e[1])return e;return null}function vs(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?fs(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=ms[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=fs(t);return n?"closed"===s?n[0]:n[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function ys(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}const bs=1/2.2;function ws(t){const e=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(t);return e?"#"+e.slice(1).map(t=>Math.min(255,Number(t)).toString(16).padStart(2,"0")).join(""):null}function ks(t){if(!t||"object"!=typeof t||Array.isArray(t))return null;const e=t;if(Object.keys(e).some(t=>"c"!==t&&"bri"!==t))return null;const i=Rt(e.c,null);return i?void 0===e.bri||null===e.bri?{c:i}:"number"!=typeof e.bri||!Number.isFinite(e.bri)||e.bri<.01||e.bri>1?null:{c:i,bri:e.bri}:null}function xs(t,e,i){const s=ks(e),n=t?.attributes||{},o=s?.bri??function(t){const e=t?.attributes?.brightness,i="number"==typeof e?e:"string"==typeof e&&""!==e.trim()?Number(e):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(t);if(s)return{c:s.c,bri:o};const a=zt(n.rgb_color);if(a)return{c:ws(a)||Rt(i,"#ffd9a0"),bri:o};const r=Number(n.color_temp_kelvin)||(Number(n.color_temp)>0?1e6/Number(n.color_temp):NaN);return Number.isFinite(r)&&r>0?{c:(l=ys(r),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Rt(i,"#ffd9a0"),bri:o};var l}function $s(t,e,i){return"on"===t?.state?xs(t,e,i):null}function Ss(t,e=1){const i=Math.max(0,Math.min(1,Number.isFinite(t)?t:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*(.4+.6*Math.pow(i,bs));return Math.max(0,Math.min(1,s))}function Ms(t){return t.startsWith("light.")||t.startsWith("switch.")}function Cs(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(a[0]-o[0])*h+(a[1]-o[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function Ds(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-t[0],r=o[1]-t[1],l=Math.hypot(a,r);if(lo||n>o)continue;const a=(s[0]-t[0])*c+(s[1]-t[1])*h,r=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(a,r)),u=Math.min(l,Math.max(a,r));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}function Ps(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=As(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function As(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function zs(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function Fs(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function Es(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ns(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Os(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Hs(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const Ls=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Bs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&Ls.has(e)}function qs(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}const Ws={availability:"available",status:"alarm",activity:"none"},js=new Set(["motion","vibration","sound"]),Us=new Set(["occupancy","presence"]),Gs=new Set(["door","window","garage_door","opening"]),Vs=new Set(["running","power"]),Ks=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ys=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Js=new Set([...Ys,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),Xs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),Zs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Qs=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,tn=t=>String(t??"").trim().toLowerCase(),en=t=>tn(t).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),sn=new Map([["run_state",0],["job_state",0],["operation_state",0],["activity_state",0],["machine_state",1],["running_state",1],["status",2],["device_status",2],["machine_status",2]]),nn=new Set(["wifi","connection","signal","battery"]);function on(t,e){const i=t?.entities?.[e]||{};if("config"===i.entity_category)return null;const s=String(e||"").split(".").slice(1).join("."),n=[i.translation_key,i.original_name,i.name],o=[...n,s];n.some(t=>tn(t))||o.push(t?.states?.[e]?.attributes?.friendly_name);const a=o.map(en).filter(Boolean);if(a.some(t=>t.split("_").some(t=>nn.has(t))))return null;let r=null;for(const t of a)for(const[e,i]of sn)(t===e||t.endsWith(`_${e}`))&&(r=null==r?i:Math.min(r,i));return r}const an=(t,e)=>null!=on(t,e);function rn(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],n=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(tn).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=tn(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function ln(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=tn(t?.[e]);if(Ys.has(i)||Xs.has(i))return i}return""}function cn(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n=String(e||"").split(".")[0],o=tn(i?.attributes?.device_class),a={eid:e,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&Bs(t,e)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return js.has(o)?{...a,edge:"rising"}:Us.has(o)?{...a,activity:"on"===s?"presence":"none"}:Gs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Vs.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=ln(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(tn):[],n=!Xs.has(s)&&(Zs.has(s)||Ys.has(s)||e.includes(s));return(t?Ys.has(t):n)?{...a,status:"working",activity:"running"}:a}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...a,status:"working",activity:"running"}:a;if("media_player"===n)return"off"===s?{...a,availability:"unavailable"}:a;if("vacuum"===n)return"cleaning"===s?{...a,status:"working",activity:"running"}:"returning"===s?{...a,status:"working",activity:"transition"}:a;if("script"===n)return"on"===s?{...a,status:"working",activity:"running"}:a;if("automation"===n)return a;if("button"===n||"event"===n)return{...a,edge:"change"};const r=ln(i.attributes);return Ys.has(r)||Ys.has(s)&&!Xs.has(s)?{...a,status:"working",activity:"running"}:a}function hn(t,e,i){const s=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category),n=e.find(e=>rn(t,e));if(!(s.length>1&&!!n))return e.map(e=>cn(t,e));const o=e.find(e=>e!==n&&an(t,e)),a=cn(t,n);return"unavailable"===a.availability||"off"===a.state?e.map(e=>({eid:e,state:t?.states?.[e]?tn(t.states[e].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):e.map(e=>{if(e===o)return function(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n={eid:e,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===n.availability)return n;const o=ln(i?.attributes);return Ys.has(o)||Js.has(s)&&!Xs.has(s)?{...n,status:"working",activity:"running"}:n}(t,e);return{...e===n?a:cn(t,e),status:"neutral",activity:"none",edge:"none"}})}function dn(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return Ws;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}function pn(t,e){if(!(t=>!!t&&!Qs(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const un=(t,e=0)=>{const i=Number(t);return Number.isInteger(i)&&i>=0?i:e},_n=(t,e)=>{const i={rev:0,configRev:un(e),off:new Set};if(null==t)return{snapshot:i,valid:!1};const s=t.off;return!Number.isInteger(t.rev)||Number(t.rev)<0||!Number.isInteger(t.config_rev)||Number(t.config_rev)<0||!Array.isArray(s)||s.some(t=>"string"!=typeof t||!t)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(t.rev),configRev:Number(t.config_rev),off:new Set(s)},valid:!0}};function mn(t,e=0){return _n(t,e).snapshot}function gn(t,e,i,s){const n=_n(e,i);return s&&n.valid&&n.snapshot.configRev===i?t.configRev===i&&n.snapshot.rev{try{const[i,s]=await Promise.all([t.callWS({type:"config/device_registry/list"}),t.callWS({type:"config/entity_registry/list"})]),n=Dn(i,"id"),o=Dn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");e.devices=n,e.entities=o,e.authoritative=!0,e.access="full",e.lastSuccess=Date.now(),e.error=void 0}catch(t){e.authoritative=!1,e.access="limited",e.error=function(t){if(t&&"object"==typeof t){const e=t;return String(e.message||e.code||e.error||"registry_unavailable")}return String(t||"registry_unavailable")}(t)}finally{e.revision++,e.loading=void 0,function(t){for(const e of[...t.listeners])try{e()}catch{}}(e)}})()),e.loading}function Tn(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Pn(t,e)},80))}async function Rn(t,e){if(e.subscribing||e.unsubDevice&&e.unsubEntity)return e.subscribing;const i=t?.connection?.subscribeEvents;return"function"==typeof i?(e.subscribing=(async()=>{try{e.unsubDevice||(e.unsubDevice=await i.call(t.connection,()=>Tn(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>Tn(t,e),"entity_registry_updated"))}catch{}finally{0===e.refs&&(e.unsubDevice?.(),e.unsubEntity?.(),e.unsubDevice=void 0,e.unsubEntity=void 0),e.subscribing=void 0}})(),e.subscribing):void 0}function In(t,e){const i=Cn(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Pn(t,i),Rn(t,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function An(t){const e=Cn(t);if(!e)return{revision:0,authoritative:!1,access:"limited",devices:t?.devices||{},entities:t?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=t?.devices||{},s=t?.entities||{};if(e.authoritative){const n=e.liveDevices!==i||e.liveEntities!==s,o=void 0!==e.liveDevices||void 0!==e.liveEntities;if(n&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,o&&Tn(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},n={...e.entities};for(const[e,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(t,e)||(t[e]=s);for(const[t,e]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,t)||(n[t]=e);e.projectedDevices=t,e.projectedEntities=n,e.projectedRevision=e.revision}i=e.projectedDevices,s=e.projectedEntities}return{revision:e.revision,authoritative:e.authoritative,access:e.access,devices:i,entities:s,lastSuccess:e.lastSuccess,error:e.error}}function zn(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=xn.get(t);return e||(e=$n++,xn.set(t,e)),e}function Fn(t,e=An(t)){return[e.revision,e.access,zn(e.devices),zn(e.entities)].join(":")}function En(t){return!!t&&null==t.disabled_by}function Nn(t,e=An(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))En(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!En(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!En(n)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(t&&!En(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!En(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function On(t,e=An(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Hn(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Ln(){if(Mn)return Mn;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Sn)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,n]of Object.entries(t))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||e-n.ts>7776e6||(i[s]=n);return Mn=i,Mn}catch{return{}}}function Bn(t,e,i=An(t)){if(!e||"virtual"===e)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=e.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=e.slice(0,s),o=e.slice(s+1);if("device"!==n&&"entity"!==n||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const a=i.devices||{},r=i.entities||{};if(i.authoritative){if("device"===n){const t=a[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Hn(r,o);if(!En(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>En(r[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=r[o];return e&&!En(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!a[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!En(a[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:e||t?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return Ln()[t]||null}(e);if("device"===n){const e=a[o],i=Hn(r,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=r[t];return null==e?.disabled_by&&(!e.device_id||null==a[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=r[t]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(e||s.some(e=>!!t?.states?.[e]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const e=r[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=a[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(e||t?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function qn(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!En(s))continue;const n=t.devices?.[s.device_id];n&&!En(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function Wn(t,e){const i=new Map;for(const s of t){if(!0===s?.removed)continue;const t=s?.binding||"";if(!t.startsWith("entity:"))continue;const n=t.slice(7);if(!n)continue;const o=e?.entities?.[n]?.device_id;if(!o)continue;const a=i.get(o)||new Set;a.add(n),i.set(o,a)}return{byDevice:i}}function jn(t,e,i,s){const n=s.byDevice.get(e);return n?.size?{partial:!0,entityIds:i.filter(e=>!n.has(e)&&!t?.entities?.[e]?.hidden)}:{partial:!1,entityIds:[...i]}}function Un(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Gn(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Kn=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Yn(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),n=s.length?s:i;for(const t of Vn){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Kn(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=tn(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return js.has(i)||Us.has(i)||Gs.has(i)||Vs.has(i)||"moving"===i||Ks.has(i)}(t,e.eid));if(o.length)return Kn(o).map(t=>t.eid);const a=n.filter(t=>t.eid.startsWith("switch."));if(a.length){const e=Kn(a),s=e.find(e=>rn(t,e.eid));if(a.length>1&&s){const e=i.filter(e=>e.eid!==s.eid&&"config"!==e.reg?.entity_category&&null!=on(t,e.eid)),n=e.filter(t=>!t.reg?.entity_category),o=n.length?n:e;for(const e of[0,1,2]){const i=Kn(o.filter(i=>on(t,i.eid)===e));if(i.length)return[i[0].eid,s.eid]}}return[(s||e[0]).eid]}const r=[];for(const t of ie)r.push(...Kn(n.filter(e=>e.eid.startsWith(t+"."))));return r.push(...Kn(n.filter(t=>!ie.includes(t.eid.split(".")[0])))),r.map(t=>t.eid)}function Jn(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of n){const i=e.find(e=>Gn(t,e.eid));if(i)return i.eid}const o=Yn(t,e);return o.find(Ms)||o[0]}function Xn(t,e,i=[]){return[...new Set(Zn(t,e,i))].filter(t=>Ms(t))}function Zn(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Qn(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function to(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Ms),s=t.primary&&Ms(t.primary)?t.primary:null;return[...new Set([e,s,...i].filter(t=>!!t&&Ms(t)))]}function eo(t){const e=to(t),i=t.marker?.light_entity;return i&&e.includes(i)?i:e[0]||null}function io(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){return[{eid:eo(e),via:"forced"}]}const i=e.primary||Yn(t,e.entities)[0];return i&&!i.startsWith("light.")?[]:e.entities.filter(e=>e.startsWith("light.")&&!!t.states?.[e]).map(t=>({eid:t,via:"light"}))}const so=new WeakMap,no=new WeakMap;function oo(t){return t.map(t=>[t.id||"",!0===t.hidden?1:0,t.primary||"",[...t.entities].join(","),void 0===t.controls?"":[...t.controls].join(","),t.marker?.id||"",t.marker?.binding||"",t.marker?.is_light,t.marker?.tap_action||"",!0===t.marker?.removed?1:0,t.marker?.light_entity||"",null==t.marker?.controls?"":[...t.marker.controls].join(",")].join("")).join("")}function ao(t){const e=oo(t),i=so.get(t);if(i?.fingerprint===e)return i;const s=t.filter(t=>!t.hidden),n=new Map,o=new Map;for(const e of t){o.set(e,Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities));const t=e.hidden||!0!==e.marker?.is_light?null:e.marker?.id||e.id;t&&n.set(String(t),e)}const a=new Map;for(const e of t){const t=o.get(e)||[],i=new Set(void 0===e.controls?t.filter(Ms):e.controls.filter(Ms)),s=eo(e),r=i.size?[...i]:s?[s]:[];for(const i of t){if(!i.startsWith("marker:"))continue;const t=i.slice(7);if(!t||t===String(e.marker?.id||e.id||""))continue;if(!n.has(t))continue;const s=a.get(t)||{markerId:t,controllers:[],driverEids:[]};s.controllers.push({device:e,driverEids:[...r]}),s.driverEids=[...new Set([...s.driverEids,...r])],a.set(t,s)}}const r={fingerprint:e,visible:s,markerById:n,persistedByDevice:o,incomingByMarker:a};return so.set(t,r),r}function ro(t){return ao(t).incomingByMarker}function lo(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities),s=Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities);return(null!=e.marker?.is_light||!i.length&&!s.some(t=>t.startsWith("marker:")))&&io(t,e).length>0}function co(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=lo(t,e)?io(t,e):[];for(const t of s)t.eid&&i.some(e=>e.eid===t.eid)||i.push(t);return i}function ho(t,e,i,s){if(null!=i)return ho(t,e,null,s).filter(t=>Qn(t.device,i));const n=oo(e),o=function(t,e){const i=new Set;for(const t of e){for(const e of t.entities)i.add(e);for(const e of t.controls||t.marker?.controls||[])"string"!=typeof e||e.startsWith("marker:")||i.add(e)}return[...i].sort().map(e=>`${e}:${t?.states?.[e]?.state??""}`).join("|")}(t,e),a=vn(s),r=no.get(e);if(r?.graphFingerprint===n&&r.stateFingerprint===o&&r.virtualLightFingerprint===a&&r.registry===t?.entities)return r.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=ao(e),p=null==i?l:l.filter(t=>Qn(t,i)),u=new Map,_=new Map;for(const e of p){const i=co(t,e).filter(t=>"controls"!==t.via);for(const s of i){const i=!0===e.marker?.is_light?String(e.marker?.id||e.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:e,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===t.states?.[s.eid]?.state};u.set(e,[...u.get(e)||[],a]),s.eid&&_.set(s.eid,a)}}const m=new Map;for(const t of e){const e=h.get(t)||[],i=new Set(void 0===t.controls?e.filter(Ms):t.controls);m.set(t,i)}for(const e of u.values())for(const i of e){if(!i.passive)continue;const e=i.key.slice(7),n=d.get(e);i.on=!n||n.driverEids.some(e=>"on"===t.states?.[e]?.state),!n&&yn(i.device.marker)&&(i.on=bn(i.device.marker,s))}const g=new Map,f=t=>{const e=g.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&g.set(t.key,t)};for(const e of l){const s=h.get(e)||[],n=null==i||Qn(e,i);for(const i of s){if(i.startsWith("marker:")){const t=c.get(i.slice(7)),e=t?u.get(t):null;if(e)for(const t of e)f(t);continue}if(!Ms(i)||!m.get(e)?.has(i))continue;const s=_.get(i);s?f(s):n&&f({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:e,via:"controls",castsGlow:!1,passive:!1,on:"on"===t.states?.[i]?.state})}for(const t of u.get(e)||[])f(t)}const v=[...g.values()];return no.set(e,{graphFingerprint:n,stateFingerprint:o,virtualLightFingerprint:a,registry:t?.entities,sources:v}),v}function po(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function uo(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function _o(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ei(i)}function mo(t,e){for(const i of e){if(!Gn(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function go(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function fo(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function vo(t,e){for(const i of e){if(!fo(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function yo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!En(s))continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if(!En(e))continue;if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function bo(t,e,i,s,n){const o=ee(e,i,n);if(o!==te)return o;const a=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&a.push(i)}return function(t){for(const e of t){const t=Qt[e];if(t)return t}return null}(a)??te}function wo(t){const e=new Set,i=new Set,s=new Set;for(const n of t||[]){const t=String(n.binding||"").indexOf(":");if(t<1)continue;const o=n.binding.slice(0,t),a=n.binding.slice(t+1);a&&(!0===n?.removed?"device"===o?e.add(a):"entity"===o&&i.add(a):"entity"===o&&s.add(a))}return{devices:e,entities:i,liveEntities:s}}function ko(t,e,i){if(i.liveEntities.has(e))return!1;if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function xo(t,e,i,s,n){const o=Xn(e.binding,e.controls,t.entities).filter(t=>!ko(i,t,s)).filter(t=>"active"===Bn(i,"entity:"+t,n).kind);t.marker=e,t.controls=o,t.userHidden=!0===e.hidden,t.hidden=t.userHidden||"ha_disabled"===t.bindingStatus?.kind,e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function $o(t,e,i,s){if("string"==typeof t.room_id&&t.room_id.length>0&&null===t.area)return{area:"",space:t.space||s};const n=t.area||e||"";return{area:n,space:n&&i[n]||t.space||s}}function So(t){const e=t.hass,i=t.registry||An(e),s=Nn(e,i),n=On(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==r.group_lights,_=wo(a),m=yo(s,u).filter(t=>!ko(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=qn(s),v=function(t){const e={};for(const[i,s]of Object.entries(t.entities||{}))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}(n),y=Wn(a,n),b=new Set;for(const t of a){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||b.add(t.binding)}const w=(t,e)=>a.find(i=>i.binding===t+":"+e),k={},x=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(b.has("device:"+t.id))continue;const a=Bn(e,"device:"+t.id,i);if("active"!==a.kind)continue;const h=w("device",t.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=jn(s,t.id,f[t.id]||[],y);if(_.partial&&!_.entityIds.length)continue;const m=_.entityIds,v=_.partial?{kind:"active",enabledEntityIds:m,allEntityIds:m}:a,$=Un(s,t,m),S=!r.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===$&&t.via_device_id)continue}const M=(t.name_by_user||t.name||d("device.unnamed")).trim(),C=M+"|"+n;let D=bo(s,M,t.model,m,p);if(m.some(t=>t.startsWith("lock."))&&(D="mdi:lock"),S&&!c&&u&&"mdi:lightbulb"===D&&g.has(n))continue;k[C]=(k[C]||0)+1;const P=k[C]>1?M+" "+k[C]:M,T={id:t.id,name:P,model:t.model||"",area:n,space:o[n],icon:D,entities:m,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:t.id,pdfs:[]};T.primary=Jn(s,m,D),"mdi:thermometer"!==D&&"mdi:air-filter"!==D||(T.temp=mo(s,m)),T.primary&&fo(s,T.primary)&&(T.hum=vo(s,m)),x.push(T)}for(const t of m)o[t.area]&&(b.has("entity:"+t.eid)||x.push({id:"lg_"+t.eid,name:t.name,model:d("device.light_group"),area:t.area,space:o[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],allEntities:[t.eid],bindingStatus:{kind:"active",enabledEntityIds:[t.eid],allEntityIds:[t.eid]},primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of a){if(t.removed)continue;const[a,l]=t.binding.split(":"),c="device"===a||"entity"===a?Bn(e,t.binding,i):null;if(!t.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const e=c;if("unverified"===e.kind)continue;const a=n.devices[l],{area:r,space:u}=$o(t,a?.area_id,o,h),m="active"===e.kind?e.enabledEntityIds:[];let g=a?bo(s,a.name_by_user||a.name||"",a.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=Jn(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=mo(s,m)),f.primary&&fo(s,f.primary)&&(f.hum=vo(s,m)),xo(f,t,n,_,i),x.push(f)}else if("entity"===a){if(ko(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const a=n.entities[l],r=a?.area_id||a?.device_id&&n.devices[a.device_id]?.area_id||"",{area:d,space:u}=$o(t,r,o,h),m=s.states[l],g=a?.name||m?.attributes?.friendly_name||l;let f=bo(s,g,"",[l],p);l.startsWith("lock.")&&(f="mdi:lock");const v={id:t.id,name:g,model:"",area:d,space:u,icon:f,entities:"active"===e.kind?[l]:[],allEntities:[l],bindingStatus:e,primary:"active"===e.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==f&&"mdi:air-filter"!==f||!v.entities.length||(v.temp=mo(s,v.entities)),v.entities.length&&fo(s,l)&&(v.hum=vo(s,v.entities)),xo(v,t,n,_,i),x.push(v)}else{const e=t.area||"",s=t.space||e&&o[e]||h,a={id:t.id,name:t.name||d("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};xo(a,t,n,_,i),x.push(a)}}return x}function Mo(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const n=e.slice(0,s),o=e.slice(s+1),a=wo(i);return"device"===n?a.devices.has(o):"entity"===n&&ko(t,o,a)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),a=e.slice(n+1);if(!a)return null;if("entity"===o){const e=parseFloat(t.states[a]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===o){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===a).map(([t])=>t);return"temp"===i?mo(t,e):vo(t,e)}return null}const Co=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");function Do(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=_o(t,n.entities);null!=e&&s.push(e)}return Ei(s)}function Po(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=mo(t,n.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}const To={climate:["current_temperature","temperature","current_humidity","humidity"],water_heater:["current_temperature","temperature"],cover:["current_position"],valve:["current_position"],fan:["percentage"],humidifier:["current_humidity","humidity"],light:["brightness"],media_player:["volume_level"],vacuum:["battery_level","fan_speed"],lawn_mower:["battery_level","fan_speed"]};function Ro(t){return t?t.sourceLabel?`${t.sourceLabel}: ${t.fullText}`:t.fullText:""}const Io=new WeakMap;function Ao(t){return t?"entity_state"===t.kind?`state:${t.entity_id}`:"entity_attribute"===t.kind?`attr:${t.entity_id}:${t.attribute}`:"derived_marker_state"===t.kind?`marker:${t.ref}`:"derived:lqi":""}function zo(t){if("derived:lqi"===t)return{kind:"derived_lqi"};if(t.startsWith("state:"))return{kind:"entity_state",entity_id:t.slice(6)};if(t.startsWith("attr:")){const e=t.slice(5),i=e.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:e.slice(0,i),attribute:e.slice(i+1)}}return t.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:t.slice(7)}:null}function Fo(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Eo(t){return t.replaceAll("_"," ")}function No(t,e=1){const i=Number(t);return Number.isFinite(i)?`${Math.round(i*e)} %`:null}function Oo(t){if("derived_lqi"===t.kind)return"lqi";if("entity_attribute"===t.kind){if(t.attribute.includes("temperature"))return"temperature";if(t.attribute.includes("humidity"))return"humidity"}return"default"}function Ho(t,e,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=Fo(t,i.entity_id);const e=t?.states?.[i.entity_id];if(e)if(Bo(t,i.entity_id))if(["unknown","unavailable"].includes(String(e.state).toLowerCase()))a="unavailable";else{const s=Xi(t,i.entity_id);n=s?Zi(s,String(e.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${Eo(i.attribute)} · ${Fo(t,i.entity_id)}`;const e=t?.states?.[i.entity_id];e?Bo(t,i.entity_id)?(n=function(t,e,i){const s=t?.states?.[e];if(!s||!(i in(s.attributes||{})))return null;const n=Xi(t,e,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return No(o,100/255);if("volume_level"===i)return No(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return No(o);if("current_temperature"===i||"temperature"===i){const e=Number(o);if(!Number.isFinite(e))return null;const i=String(t?.config?.unit_system?.temperature||"°C");return`${Math.round(10*e)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(t,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=_o(t,e.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const e=s.find(t=>t.ref===i.ref);if(e){const i=e.on?"on":"off",s=t?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=e.name||o}else a="missing"}const r=t?.localize?.("state.default.unavailable")||"Unavailable";return{source:i,sourceLabel:o,text:n??"—",fullText:n??r,availability:a,isLqi:"derived_lqi"===i.kind,tone:Oo(i)}}function Lo(t,e){if(!0===e.marker?.use_climate_temp){const i=e.entities.find(e=>e.startsWith("climate.")&&Number.isFinite(Number(t?.states?.[e]?.attributes?.current_temperature)));return i?{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}:null}if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=e.entities.find(e=>Gn(t,e)&&Number.isFinite(Number(t?.states?.[e]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return e.primary&&fo(t,e.primary)&&Number.isFinite(Number(t?.states?.[e.primary]?.state))?{kind:"entity_state",entity_id:e.primary}:null}function Bo(t,e){const i=t?.entities?.[e];if(i&&!En(i))return!1;const s=i?.device_id?t?.devices?.[i.device_id]:null;return!s||En(s)}function qo(t,e){return Bo(t,e)&&!!t?.states?.[e]}function Wo(t,e,i=[e]){const s=function(t,e){const i=t=>[t.id,t.primary,t.hidden?1:0,t.userHidden?1:0,t.entities.join(","),(t.controls||[]).join(","),t.marker?.binding||"",!0===t.marker?.is_light?1:0,t.marker?.light_entity||"",(t.marker?.controls||[]).join(",")].join("|");return`${i(t)}\n${e.map(i).join("\n")}`}(e,i),n=Io.get(e);if(n&&n.states===(t?.states||null)&&n.entities===(t?.entities||null)&&n.devices===(t?.devices||null)&&n.signature===s)return n.result;const o=new Set(e.entities);for(const t of e.controls||[])t.startsWith("marker:")||o.add(t);const a=[],r=ho(t,i),l=r.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device?.name||t.key})),c=i=>{const s=Ao(i);if(a.some(t=>t.key===s))return;const n=Ho(t,e,i,l),o="entity_attribute"===i.kind?`${i.entity_id} · ${i.attribute}`:"entity_state"===i.kind?i.entity_id:"derived_marker_state"===i.kind?i.ref:"LQI";a.push({key:s,source:i,label:n.sourceLabel,technical:o,value:n.text,available:"available"===n.availability})};for(const e of[...o].sort()){if(!qo(t,e))continue;const i=e.split(".")[0],s=t?.entities?.[e];if("button"===i||"event"===i||"config"===s?.entity_category)continue;c({kind:"entity_state",entity_id:e});const n=t?.states?.[e];for(const t of To[i]||[])t in(n?.attributes||{})&&c({kind:"entity_attribute",entity_id:e,attribute:t})}const h=new Set((e.marker?.controls||e.controls||[]).filter(t=>t.startsWith("marker:")));!0===e.marker?.is_light&&e.marker.id&&h.add(`marker:${e.marker.id}`);const d=new Set(r.map(t=>t.key));for(const t of[...h].sort())d.has(t)&&c({kind:"derived_marker_state",ref:t});return e.virtual||null==_o(t,e.entities)||c({kind:"derived_lqi"}),Io.set(e,{states:t?.states||null,entities:t?.entities||null,devices:t?.devices||null,signature:s,result:a}),a}function jo(t,e,i){const s=Lo(t,e);if(s&&i.some(t=>t.key===Ao(s)))return s;const n=e.primary&&i.find(t=>t.key===`state:${e.primary}`);if(n)return n.source;const o=[t=>t.technical.includes("temperature"),t=>t.technical.includes("humidity"),t=>t.technical.includes("battery")];for(const t of o){const e=i.find(t);if(e)return e.source}return i.find(t=>"derived_lqi"!==t.source.kind)?.source||i[0]?.source||null}function Uo(t){return"boolean"==typeof t?.themes?.darkMode?t.themes.darkMode?"theme-dark":"theme-light":""}function Go(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"none"!==t.pulse.kind){e.push(`--ripple-scale:${t.pulse.diameterScale}`);const n=(i=t.pulse.color,s=null,"string"==typeof i&&(Tt.test(i)||At.test(i))?i:s);n&&e.push(`--ripple-color:${n}`)}var i,s;return e}function Vo(t){const e=[...String(t)].reduce((t,e)=>/\s/.test(e)?t+.35:t+(e.codePointAt(0)>255?1:.62),0);return e<=8?.45:Math.max(.25,Math.round(3.6/e*1e3)/1e3)}function Ko(t,e){const i=t.pulse,s=i.generation%2==0,n=function(t){const e=t.valueBadge;if(!e||!1!==e.configured)return[];const i=[];return null!=t.tempText&&"temperature"!==e.tone&&i.push({kind:"temperature",text:t.tempText,suffix:"°"}),null!=t.humText&&"humidity"!==e.tone&&i.push({kind:"humidity",text:t.humText,suffix:"%"}),i}(t),o=t.valueBadge,a=o?.position||"right",r=!!o||n.length>0,l=["device-shell",null!=t.valueText?"text-shell":"",r?`with-values pos-${a}`:"",n.length?"with-legacy":""].filter(Boolean).join(" ");return W`
${"none"!==i.kind&&"dot"!==i.reducedMotionIndicator?W``:G}
diff --git a/dist/houseplan-card.js b/dist/houseplan-card.js
index f3721a61..ae0f145f 100755
--- a/dist/houseplan-card.js
+++ b/dist/houseplan-card.js
@@ -1,4 +1,4 @@
-globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825898642dedcccc78443fe6cf5df23";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},a=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=t=>t,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,T=document,R=()=>T.createComment(""),I=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"")),s]};class X{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&I(this._$AH)?this._$AA.nextSibling.data=t:this.T(T.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new X(t)),e}k(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=Z(this,t,e,0),o=!I(t)||t!==this._$AH&&t!==U,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const pt=2,ut=t=>(...e)=>({_$litDirective$:t,values:e});let _t=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const mt={},gt=ut(class extends _t{constructor(){super(...arguments),this.ot=mt}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return U}else if(this.ot===e)return U;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),{I:ft}=at,vt=t=>t,yt=()=>document.createComment(""),bt=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(yt(),n),o=s.insertBefore(yt(),n);i=new ft(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=vt(t).nextSibling;vt(s).insertBefore(t,n),t=e}}}return i},wt=(t,e,i=t)=>(t._$AI(e,i),t),kt={},xt=(t,e=kt)=>t._$AH=e,$t=t=>{t._$AR(),t._$AA.remove()},St=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},Mt=ut(class extends _t{constructor(t){if(super(t),t.type!==pt)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=wt(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=wt(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=wt(n[d],o[_]),bt(t,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=wt(n[p],o[u]),bt(t,n[d],n[p]),p--,u++;else if(void 0===c&&(c=St(a,u,_),h=St(r,d,p)),c.has(r[d]))if(c.has(r[p])){const e=h.get(a[u]),i=void 0!==e?n[e]:null;if(null===i){const e=bt(t,n[d]);wt(e,o[u]),l[u]=e}else l[u]=wt(i,o[u]),bt(t,n[d],i),n[e]=null;u++}else $t(n[p]),p--;else $t(n[d]),d++;for(;u<=_;){const e=bt(t,l[_+1]);wt(e,o[u]),l[u++]=e}for(;d<=p;){const t=n[d++];null!==t&&$t(t)}return this.ut=a,xt(t,l),U}}),Ct=new WeakMap;let Dt=0;class Pt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Dt,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=Ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),Ct.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?Ct.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=Ct.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void Ct.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W`
+globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="70f73686ca2a09aa7ca18ae77f6e6bdddfac7e9cb8cc46a784d01c351e036ef6";const t=globalThis,e=t.ShadowRoot&&(void 0===t.ShadyCSS||t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new WeakMap;let n=class{constructor(t,e,s){if(this._$cssResult$=!0,s!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const i=this.t;if(e&&void 0===t){const e=void 0!==i&&1===i.length;e&&(t=s.get(i)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&s.set(i,t))}return t}toString(){return this.cssText}};const o=(t,...e)=>{const s=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new n(s,t,i)},a=e?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new n("string"==typeof t?t:t+"",void 0,i))(e)})(t):t,{is:r,defineProperty:l,getOwnPropertyDescriptor:c,getOwnPropertyNames:h,getOwnPropertySymbols:d,getPrototypeOf:p}=Object,u=globalThis,_=u.trustedTypes,m=_?_.emptyScript:"",g=u.reactiveElementPolyfillSupport,f=(t,e)=>t,v={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>!r(t,e),b={attribute:!0,type:String,converter:v,reflect:!1,useDefault:!1,hasChanged:y};Symbol.metadata??=Symbol("metadata"),u.litPropertyMetadata??=new WeakMap;let w=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&l(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=c(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const o=s?.call(this);n?.call(this,e),this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??b}static _$Ei(){if(this.hasOwnProperty(f("elementProperties")))return;const t=p(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(f("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(f("properties"))){const t=this.properties,e=[...h(t),...d(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(a(t))}else void 0!==t&&e.push(a(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const i=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return((i,s)=>{if(e)i.adoptedStyleSheets=s.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of s){const s=document.createElement("style"),n=t.litNonce;void 0!==n&&s.setAttribute("nonce",n),s.textContent=e.cssText,i.appendChild(s)}})(i,this.constructor.elementStyles),i}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:v).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:v;this._$Em=s;const o=n.fromAttribute(e,t.type);this[s]=o??this._$Ej?.get(s)??o,this._$Em=null}}requestUpdate(t,e,i,s=!1,n){if(void 0!==t){const o=this.constructor;if(!1===s&&(n=this[t]),i??=o.getPropertyOptions(t),!((i.hasChanged??y)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},o){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,o??e??this[t]),!0!==n||void 0!==o)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};w.elementStyles=[],w.shadowRootOptions={mode:"open"},w[f("elementProperties")]=new Map,w[f("finalized")]=new Map,g?.({ReactiveElement:w}),(u.reactiveElementVersions??=[]).push("2.1.2");const k=globalThis,x=t=>t,$=k.trustedTypes,S=$?$.createPolicy("lit-html",{createHTML:t=>t}):void 0,M="$lit$",C=`lit$${Math.random().toFixed(9).slice(2)}$`,D="?"+C,P=`<${D}>`,T=document,R=()=>T.createComment(""),I=t=>null===t||"object"!=typeof t&&"function"!=typeof t,A=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,N=/>/g,O=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),H=/'/g,L=/"/g,B=/^(?:script|style|textarea|title)$/i,q=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),W=q(1),j=q(2),U=Symbol.for("lit-noChange"),G=Symbol.for("lit-nothing"),V=new WeakMap,K=T.createTreeWalker(T,129);function Y(t,e){if(!A(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==S?S.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,s=[];let n,o=2===e?"":3===e?"":"")),s]};class X{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,o=0;const a=t.length-1,r=this.parts,[l,c]=J(t,e);if(this.el=X.createElement(l,i),K.currentNode=this.el.content,2===e||3===e){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(s=K.nextNode())&&r.length0){s.textContent=$?$.emptyScript:"";for(let i=0;iA(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==G&&I(this._$AH)?this._$AA.nextSibling.data=t:this.T(T.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=X.createElement(Y(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new Q(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new X(t)),e}k(t){A(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new tt(this.O(R()),this.O(R()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=G}_$AI(t,e=this,i,s){const n=this.strings;let o=!1;if(void 0===n)t=Z(this,t,e,0),o=!I(t)||t!==this._$AH&&t!==U,o&&(this._$AH=t);else{const s=t;let a,r;for(t=n[0],a=0;a{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new tt(e.insertBefore(R(),t),t,void 0,i??{})}return n._$AI(t),n},ct=globalThis;let ht=class extends w{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=lt(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return U}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const dt=ct.litElementPolyfillSupport;dt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.2");const pt=2,ut=t=>(...e)=>({_$litDirective$:t,values:e});let _t=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const mt={},gt=ut(class extends _t{constructor(){super(...arguments),this.ot=mt}render(t,e){return e()}update(t,[e,i]){if(Array.isArray(e)){if(Array.isArray(this.ot)&&this.ot.length===e.length&&e.every((t,e)=>t===this.ot[e]))return U}else if(this.ot===e)return U;return this.ot=Array.isArray(e)?Array.from(e):e,this.render(e,i)}}),{I:ft}=at,vt=t=>t,yt=()=>document.createComment(""),bt=(t,e,i)=>{const s=t._$AA.parentNode,n=void 0===e?t._$AB:e._$AA;if(void 0===i){const e=s.insertBefore(yt(),n),o=s.insertBefore(yt(),n);i=new ft(e,o,t,t.options)}else{const e=i._$AB.nextSibling,o=i._$AM,a=o!==t;if(a){let e;i._$AQ?.(t),i._$AM=t,void 0!==i._$AP&&(e=t._$AU)!==o._$AU&&i._$AP(e)}if(e!==n||a){let t=i._$AA;for(;t!==e;){const e=vt(t).nextSibling;vt(s).insertBefore(t,n),t=e}}}return i},wt=(t,e,i=t)=>(t._$AI(e,i),t),kt={},xt=(t,e=kt)=>t._$AH=e,$t=t=>{t._$AR(),t._$AA.remove()},St=(t,e,i)=>{const s=new Map;for(let n=e;n<=i;n++)s.set(t[n],n);return s},Mt=ut(class extends _t{constructor(t){if(super(t),t.type!==pt)throw Error("repeat() can only be used in text expressions")}dt(t,e,i){let s;void 0===i?i=e:void 0!==e&&(s=e);const n=[],o=[];let a=0;for(const e of t)n[a]=s?s(e,a):a,o[a]=i(e,a),a++;return{values:o,keys:n}}render(t,e,i){return this.dt(t,e,i).values}update(t,[e,i,s]){const n=(t=>t._$AH)(t),{values:o,keys:a}=this.dt(e,i,s);if(!Array.isArray(n))return this.ut=a,o;const r=this.ut??=[],l=[];let c,h,d=0,p=n.length-1,u=0,_=o.length-1;for(;d<=p&&u<=_;)if(null===n[d])d++;else if(null===n[p])p--;else if(r[d]===a[u])l[u]=wt(n[d],o[u]),d++,u++;else if(r[p]===a[_])l[_]=wt(n[p],o[_]),p--,_--;else if(r[d]===a[_])l[_]=wt(n[d],o[_]),bt(t,l[_+1],n[d]),d++,_--;else if(r[p]===a[u])l[u]=wt(n[p],o[u]),bt(t,n[d],n[p]),p--,u++;else if(void 0===c&&(c=St(a,u,_),h=St(r,d,p)),c.has(r[d]))if(c.has(r[p])){const e=h.get(a[u]),i=void 0!==e?n[e]:null;if(null===i){const e=bt(t,n[d]);wt(e,o[u]),l[u]=e}else l[u]=wt(i,o[u]),bt(t,n[d],i),n[e]=null;u++}else $t(n[p]),p--;else $t(n[d]),d++;for(;u<=_;){const e=bt(t,l[_+1]);wt(e,o[u]),l[u++]=e}for(;d<=p;){const t=n[d++];null!==t&&$t(t)}return this.ut=a,xt(t,l),U}}),Ct=new WeakMap;let Dt=0;class Pt extends ht{constructor(){super(...arguments),this.title="",this.icon="",this.wide=!1,this.dismissOnScrim=!1,this.hass=null,this._opener=null,this._focusRoot=null,this._useHaDialog=!1,this._closing=!1,this._overlays=[],this._titleId="hp-dialog-title-"+ ++Dt,this._focusInitial=()=>{const t=this._focusableElements(),e=t.find(t=>t.hasAttribute("autofocus"))||t[0]||(this._useHaDialog?null:this.renderRoot.querySelector(".close"))||this.renderRoot.querySelector(".surface")||this.renderRoot.querySelector("ha-dialog");e?.focus({preventScroll:!0})},this._requestClose=()=>{this._closing||(this._closing=!0,this.dispatchEvent(new CustomEvent("hp-close",{bubbles:!0,composed:!0})))},this._onKeyDown=t=>{if("Escape"===t.key){t.preventDefault(),t.stopImmediatePropagation(),this._pruneOverlays();const e=this._overlays[this._overlays.length-1];return e?void this._closeOverlay(e,"escape"):void this._requestClose()}if("Tab"!==t.key||this._useHaDialog)return;const e=this.renderRoot.querySelector(".close"),i=e?[e,...this._focusableElements()]:this._focusableElements();if(!i.length)return t.preventDefault(),void this.renderRoot.querySelector(".surface")?.focus({preventScroll:!0});const s=this._deepActiveElement(),n=i[0],o=i[i.length-1];!t.shiftKey||s!==n&&i.includes(s)?t.shiftKey||s!==o||(t.preventDefault(),n.focus()):(t.preventDefault(),o.focus())},this._onFallbackCancel=t=>{t.preventDefault(),this._requestClose()},this._onFallbackClick=t=>{this.dismissOnScrim&&t.target===t.currentTarget&&this._requestClose()}}connectedCallback(){super.connectedCallback(),this._opener=this._deepActiveElement();const t=this.getRootNode();this._focusRoot=t;const e=Ct.get(t)||{dialogs:new Set,opener:this._opener};e.dialogs.add(this),Ct.set(t,e),this._useHaDialog=!!customElements.get("ha-dialog"),this.addEventListener("keydown",this._onKeyDown,!0)}disconnectedCallback(){this.removeEventListener("keydown",this._onKeyDown,!0);const t=[...this._overlays];this._overlays=[];for(const e of t.reverse())e.close("disconnect");const e=this._focusRoot,i=this._opener;this._opener=null,this._focusRoot=null;const s=e?Ct.get(e):void 0;s?.dialogs.delete(this),super.disconnectedCallback(),e&&s&&requestAnimationFrame(()=>{const t=Ct.get(e);if(!t)return;if(!t.dialogs.size)return t.opener?.isConnected&&t.opener.focus({preventScroll:!0}),void Ct.delete(e);const s=i?.closest("hp-dialog");i?.isConnected&&s&&t.dialogs.has(s)&&i.focus({preventScroll:!0})})}firstUpdated(t){if(super.firstUpdated(t),!this._useHaDialog){const t=this.renderRoot.querySelector("dialog");t&&!t.open&&t.showModal()}queueMicrotask(()=>this._focusInitial())}_deepActiveElement(){let t=document.activeElement;for(;t?.shadowRoot?.activeElement;)t=t.shadowRoot.activeElement;return t&&t!==document.body?t:null}_focusableElements(){const t=["[autofocus]","a[href]","button:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])","textarea:not([disabled])",'[contenteditable="true"]','[tabindex]:not([tabindex="-1"])'].join(","),e=[],i=new Set,s=n=>{if(!i.has(n)){if(i.add(n),n instanceof HTMLElement){if(n.matches(t)&&e.push(n),n instanceof HTMLSlotElement){for(const t of n.assignedNodes({flatten:!0}))s(t);return}if(n.shadowRoot){for(const t of n.shadowRoot.childNodes)s(t);return}}for(const t of n.childNodes)s(t)}};for(const t of this.childNodes)s(t);const n=this.overlayPortal();return n&&s(n),e.filter(t=>{let e=t;for(;e;){const t=getComputedStyle(e);if(e.hidden||e.inert||"true"===e.getAttribute("aria-hidden")||"none"===t.display||"hidden"===t.visibility)return!1;if(e=e.assignedSlot||e.parentElement||(e.getRootNode()instanceof ShadowRoot?e.getRootNode().host:null),e===this)break}return!0})}_pruneOverlays(){this._overlays=this._overlays.filter(t=>t.owner.isConnected)}_closeOverlay(t,e){const i=this._overlays.findIndex(e=>e.token===t.token);i>=0&&this._overlays.splice(i,1),t.close(e)}registerOverlay(t){this._pruneOverlays();const e=this._overlays.find(e=>e.owner===t.owner);e&&this._overlays.splice(this._overlays.indexOf(e),1);const i=t.group||"transient";for(const t of[...this._overlays].reverse())t.group===i&&this._closeOverlay(t,"exclusive");const s={...t,group:i,token:Symbol("hp-overlay")};this._overlays.push(s);let n=!1;return()=>{if(n)return;n=!0;const t=this._overlays.findIndex(t=>t.token===s.token);t>=0&&this._overlays.splice(t,1)}}closeTransientOverlays(t="outside"){this._pruneOverlays();const e=[...this._overlays].filter(t=>"transient"===(t.group||"transient"));for(const i of e.reverse())this._closeOverlay(i,t);return e.length>0}overlayPortal(){return this.renderRoot.querySelector(".overlay-portal")}render(){const t=W`
${this.icon?W``:G}
${this.title}
`;return this._useHaDialog?W` span { flex: 1; min-width: 0; }
- .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
- .habindingbanner.limited {
- border-color: var(--secondary-text-color, #9aa0aa);
- background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
- }
- .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
.dev.sel {
--device-ring-color: #F0A00C;
--device-ring-width: calc(var(--dev-size) * 0.0375);
@@ -2927,39 +2591,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
:host([data-pointer-hover]) .tab:hover .tabedit {
opacity: 0.9;
}
- .srcrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- font-size: var(--fs-m);
- cursor: pointer;
- padding: var(--sp-1) 0;
- }
- .dispsection {
- margin-top: var(--sp-5) !important;
- padding-top: var(--sp-4);
- border-top: 1px solid var(--hp-line);
- font-weight: 600;
- color: var(--hp-txt) !important;
- }
- .colorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .gsrow > hp-color-opacity {
- min-width: min(100%, 210px);
- justify-content: space-between;
- }
- .ripple-colorrow > hp-color-opacity {
- width: 100%;
- justify-content: space-between;
- }
- .ripple-sizerow > .opl {
- min-width: 0;
- }
- .colorrow input[type='range'] { flex: 1; }
- .colorrow .tempin { width: 70px; flex: none; }
.temprange {
display: inline-flex;
align-items: center;
@@ -2968,200 +2599,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
color: var(--hp-muted);
font-size: var(--fs-s);
}
- /* beat the generic hp-dialog .body .namein { width:100% } rule */
- hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
- hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
- .srcrow { flex-wrap: nowrap; }
- /* native HA controls (rendered only when the HA frontend defines them;
- old HA and the smoke env keep the plain inputs). ha-switch is taller
- than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
- .srcrow ha-switch { flex: none; }
- .colorrow ha-slider { flex: 1; min-width: 0; }
- .srcrow > span:first-of-type { white-space: nowrap; }
- .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
- .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
- .markerlightgroup {
- min-width: 0;
- margin: var(--sp-5) 0 0;
- padding: var(--sp-4);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- }
- .markerlightgroup legend {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- padding: 0 var(--sp-2);
- color: var(--hp-txt);
- font-weight: 600;
- }
- .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
- .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
- .markerhelpfield { margin-top: var(--sp-4); }
- .markerhelplabel {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- margin-bottom: var(--sp-1);
- }
- .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
- .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
- .markerlightgroup .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
- .markerglowvalue hp-color-opacity { flex: none; }
- .markerlightdisabled {
- display: flex;
- align-items: center;
- gap: var(--sp-2);
- margin-top: var(--sp-2) !important;
- }
- .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
- .markerbadgetechnical {
- min-width: 0;
- margin: var(--sp-1) 0 var(--sp-2) !important;
- overflow-wrap: anywhere;
- }
- .markerbadgetechnical code { white-space: normal; }
- .planrow {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- /* the "already uploaded" picker: a plan is never deleted for being
- unreferenced, so it has to be findable again */
- .savedplans {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- max-height: 240px;
- overflow: auto;
- margin: var(--sp-3) 0 var(--sp-1);
- padding: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
- /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
- flex item whose automatic minimum size is ZERO (overflow != visible),
- so inside hp-dialog .body — a flex column taller than its 66vh cap —
- it shrank to a 14px sliver: the rows were in the DOM, the owner saw
- a thin rounded stripe under the "Already uploaded" button. Don't
- shrink, and keep a floor even when the box is empty or loading. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .savedplan {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
- .savedplan img {
- width: 56px;
- height: 40px;
- object-fit: contain;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- flex: none;
- }
- .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
- .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .savedmeta .muted { font-size: var(--fs-s); }
- .savedplan .btn.danger ha-icon { color: #f25a4a; }
- .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
- .planprev {
- max-width: 120px;
- max-height: 70px;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- }
- .planname {
- font-size: var(--fs-m);
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .planname.muted {
- color: var(--hp-muted);
- }
- .filebtn {
- cursor: pointer;
- }
- .fileupload { display: inline-flex; min-width: 0; }
- .fileupload > input { display: none; }
- .btn.danger {
- border-color: #b3402a;
- color: #ff7a5c;
- }
- hp-dialog .row .spacer {
- flex: 1;
- }
- hp-dialog .body {
- max-height: 66vh;
- overflow-y: auto;
- }
- .descin {
- width: 100%;
- box-sizing: border-box;
- background: var(--hp-bg);
- border: 1px solid var(--hp-line);
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- font-size: var(--fs-m);
- font-family: inherit;
- resize: vertical;
- /* flex column of the dialog body squeezes textareas — keep a usable height */
- min-height: 92px;
- flex-shrink: 0;
- line-height: 1.35;
- }
- .bindsel {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- padding: var(--sp-4);
- }
- .bindsel .opt {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- background: transparent;
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- font-size: var(--fs-m);
- font-family: inherit;
- }
- .bindsel .opt.on {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- border-color: var(--hp-accent);
- }
- .curbind {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- font-size: var(--fs-m);
- color: var(--hp-txt);
- flex-wrap: wrap;
- }
- .curbind .ref {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
/* live vacuum: a round puck, no badge plate, soft pulse (docs/VACUUM.md) */
.vacpuck {
position: absolute;
@@ -3242,59 +2679,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
stroke: rgba(255, 255, 255, 0.82);
stroke-width: 0.9;
}
- .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
- .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
- .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
- .vacdiag > div > span { color: var(--secondary-text-color); }
- .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
- .vacpicker { margin: var(--sp-3) 0; }
- .vacsource-warning { display: grid; gap: 8px; }
- .vacsource-warning .btn { justify-self: start; }
- .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
- .vacpicker > summary::-webkit-details-marker { display: none; }
- .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
- .vacsource-list details { padding: 6px 0 0; }
- .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
- .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
- width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
- border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
- text-align: left; cursor: pointer; }
- .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
- .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
- .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
- .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
- .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
- .vacfit {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- z-index: 12;
- overflow: visible;
- touch-action: none;
- cursor: grab;
- /* the devlayer is pointer-events: none and every child opts back in —
- without this line real clicks flew straight through the overlay
- (owner: «уголки не кликабельны»; synthetic smoke events bypass
- hit-testing, which is why they lied) */
- pointer-events: auto;
- }
- .vacfit:active { cursor: grabbing; }
- .vacfit polygon {
- fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- stroke-dasharray: 6 4;
- }
- .vacfit text {
- fill: var(--hp-accent);
- font-size: 26px;
- text-anchor: middle;
- dominant-baseline: middle;
- pointer-events: none;
- user-select: none;
- }
/* room resize tool (docs/RESIZE.md) */
/* wall handle: invisible finger-sized hit circle (HP-1550-04 hit priority
kept), the visible glyph lives in the sibling .rszicon */
@@ -3366,290 +2750,13 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
transform: translate(-50%, -50%);
border: 1px solid var(--hp-accent);
}
- /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
- .furnpalette {
- display: flex;
- flex-direction: column;
- max-height: 38vh;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- background: var(--card-background-color, var(--hp-bg));
- font-size: 0.85em;
- }
- .furnhd {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 8px;
- font-weight: 600;
- opacity: 0.9;
- }
- .furnhd .spacer { flex: 1; }
- .furnbody {
- overflow: auto;
- padding: 0 8px 6px;
- }
- .furngroup {
- margin: 6px 0 3px;
- font-size: 0.85em;
- text-transform: uppercase;
- letter-spacing: 0.04em;
- opacity: 0.6;
- }
- .furnrow {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
- }
- .furnitem {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2px;
- width: 74px;
- padding: 4px 2px;
- border: 1px solid transparent;
- border-radius: 8px;
- background: rgba(127, 127, 127, 0.08);
- color: inherit;
- font: inherit;
- font-size: 0.8em;
- line-height: 1.15;
- text-align: center;
- cursor: pointer;
- }
:host([data-pointer-hover]) .furnitem:hover { background: rgba(127, 127, 127, 0.18); }
- .furnitem.on {
- border-color: var(--hp-accent);
- background: rgba(38, 166, 154, 0.18);
- }
- .furnprev {
- width: 40px;
- height: 40px;
- color: var(--primary-text-color, currentColor);
- }
- .furnsize {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 6px;
- padding: 6px 8px;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- }
- .furnsize label {
- display: flex;
- align-items: baseline;
- gap: 3px;
- opacity: 0.8;
- }
- .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
- .furnsize input {
- width: 5.5em;
- padding: 3px 5px;
- }
- .furnhint { opacity: 0.6; }
- .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
- /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
- .vacfithandle {
- fill: transparent;
- stroke: none;
- cursor: nwse-resize;
- }
- .vacfitknob {
- fill: var(--hp-bg);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- pointer-events: none;
- }
- .vaccalbar {
- position: fixed;
- left: 50%;
- bottom: 24px;
- transform: translateX(-50%);
- display: flex;
- gap: var(--sp-5);
- align-items: center;
- background: var(--hp-bg);
- color: var(--hp-txt);
- border: 1px solid var(--hp-accent);
- border-radius: var(--rad-l);
- padding: 10px var(--sp-5);
- z-index: 60;
- box-shadow: var(--shadow-2);
- }
- .candlist {
- max-height: 160px;
- overflow-y: auto;
- border-top: 1px solid var(--hp-line);
- /* A scrollable box is a flex item that HAPPILY collapses: inside the
- dialog body (a flex column) this list rendered its rows into a 1px
- sliver — the DOM had 26 candidates and the user saw nothing. In the
- binding dropdown it sits inside .droppanel (block context) and never
- showed the bug. Field report, 2026-07-30. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .cand {
- display: flex;
- justify-content: space-between;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- border-radius: var(--rad-s);
- font-size: var(--fs-m);
- }
:host([data-pointer-hover]) .cand:hover {
background: rgba(127, 127, 127, 0.15);
}
- .cand.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .cand .cs {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- white-space: nowrap;
- }
- .opening-entity-candidate {
- width: 100%;
- border: 0;
- background: transparent;
- color: inherit;
- font: inherit;
- text-align: left;
- }
- .opening-entity-candidate.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .opening-entity-empty { cursor: default; }
- .cand.sel .cs {
- color: var(--text-primary-color, #fff);
- opacity: 0.85;
- }
- .cand.muted {
- color: var(--hp-muted);
- cursor: default;
- }
- .pdfedit {
- display: flex;
- flex-wrap: wrap;
- gap: var(--sp-3);
- align-items: center;
- }
- .pdftag {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .pdftag a {
- color: var(--hp-txt);
- text-decoration: none;
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .pdftag .x {
- --mdc-icon-size: 15px;
- cursor: pointer;
- color: var(--hp-muted);
- }
:host([data-pointer-hover]) .pdftag .x:hover {
color: #ff7a5c;
}
- .entlist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- margin-bottom: 10px;
- }
- .entrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- border-radius: var(--rad-m);
- background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
- }
- .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
- .entrow.on ha-icon { color: var(--hp-accent); }
- .entrow .en { flex: 1; font-size: var(--fs-m); }
- .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
- .entbtn {
- min-width: 74px;
- min-height: 32px;
- padding: var(--sp-2) var(--sp-5);
- border: 1px solid var(--hp-muted);
- border-radius: 999px;
- background: transparent;
- color: var(--hp-txt);
- font: inherit;
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .entbtn.on {
- background: var(--hp-accent);
- border-color: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- font-weight: 600;
- }
- .inforow {
- display: flex;
- gap: 10px;
- font-size: var(--fs-m);
- margin: var(--sp-2) 0;
- }
- .inforow .k {
- color: var(--hp-muted);
- min-width: 84px;
- }
- .inforow a {
- color: var(--hp-accent);
- word-break: break-all;
- }
- .infodesc {
- font-size: var(--fs-m);
- white-space: pre-wrap;
- margin-top: var(--sp-3);
- }
- .infodesc.muted {
- color: var(--hp-muted);
- }
- .pdflist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- }
- .pdf {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- color: var(--hp-accent);
- text-decoration: none;
- }
- ha-icon-picker {
- display: block;
- }
- .floorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-2);
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .floorrow .floorlvl {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: 0 var(--sp-3);
- }
.sr-only {
position: absolute !important;
width: 1px !important;
@@ -3661,214 +2768,10 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
white-space: nowrap !important;
border: 0 !important;
}
- .rtest {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- margin-bottom: var(--sp-4);
- }
- .rtest .namein { flex: 1; }
- .rtest ha-icon { color: var(--hp-accent); }
- .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
- .rrow {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- margin: var(--sp-1) 0;
- }
- .rrow .rpat { flex: 2; }
- .rrow .ricon { flex: 1.4; }
- .rrow .rpat.bad { border-color: #ff7a5c; }
- .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
- .rrow .ract {
- --mdc-icon-size: 16px;
- color: var(--hp-muted);
- cursor: pointer;
- }
:host([data-pointer-hover]) .rrow .ract:hover { color: var(--hp-txt); }
:host([data-pointer-hover]) .rrow .ract.del:hover { color: #ff7a5c; }
- .gsrow .gsl {
- min-width: 150px;
- font-size: var(--fs-m);
- color: var(--hp-muted);
- }
.alignmsg { margin: 0 0 8px; font-size: 13px; line-height: 1.45; }
- .optimize-live {
- display: grid;
- justify-items: start;
- gap: var(--sp-2);
- margin-bottom: var(--sp-3);
- }
- .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
- .optimize-cleanup { min-height: 44px; }
- .optimize-selected { color: var(--hp-txt); }
- .optimize-details {
- margin-top: var(--sp-3);
- color: var(--hp-muted);
- font-size: var(--fs-s);
- overflow-wrap: anywhere;
- }
- .optimize-details > summary {
- width: fit-content;
- color: var(--hp-txt);
- cursor: pointer;
- font-weight: 600;
- }
- .optimize-details > summary:focus-visible {
- outline: 2px solid var(--hp-accent);
- outline-offset: 3px;
- border-radius: var(--rad-s);
- }
- .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
- .optimize-details li + li { margin-top: var(--sp-1); }
- .btn.alignall { width: 100%; justify-content: center; }
- .backupactions {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-3);
- }
- .backupactions .btn { justify-content: center; min-width: 0; }
- .backupupload { display: inline-flex; min-width: 0; }
- .backupupload > .btn { width: 100%; justify-content: center; }
- .backupupload input { display: none; }
- .backupbody { min-width: 0; }
- .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
- .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
- .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
- .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
- .backupfile, .backupsummary, .backupcontent {
- display: flex;
- flex-direction: column;
- min-width: 0;
- gap: var(--sp-1);
- }
- .backupfile b, .backupcontent span {
- overflow-wrap: anywhere;
- }
- .backupfile span, .backupsummary span, .backupcontent span {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
- .backupcounts {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-2) var(--sp-4);
- font-size: var(--fs-s);
- }
- .backupwarn, .backuperror {
- border-radius: var(--rad-s);
- padding: var(--sp-3);
- font-size: var(--fs-s);
- line-height: 1.45;
- overflow-wrap: anywhere;
- }
- .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
- .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
- .backupdetails {
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .backupdetails summary { cursor: pointer; font-weight: 700; }
- .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
- .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
- .backupchoices {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- min-width: 0;
- }
- .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
- .backupconfirm { align-items: flex-start !important; }
- .backupconfirm > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- @media (max-width: 520px) {
- .backupactions, .backupcounts { grid-template-columns: 1fr; }
- }
- .aboutver {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin: var(--sp-2) 0 var(--sp-3);
- }
- .aboutlink {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- width: fit-content;
- color: var(--hp-accent);
- text-decoration: none;
- font-size: var(--fs-m);
- padding: var(--sp-1) 0;
- }
:host([data-pointer-hover]) .aboutlink:hover { text-decoration: underline; }
- .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
- hp-dialog .body {
- padding: var(--sp-5) var(--sp-6);
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- }
- hp-dialog .tapconfirm-body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog .tapconfirm-body p {
- max-width: 100%;
- min-width: 0;
- margin: 0;
- overflow-wrap: anywhere;
- white-space: normal;
- }
- hp-dialog .tapconfirm-line {
- color: var(--hp-muted);
- }
- hp-dialog .body label {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin-top: var(--sp-3);
- }
- hp-dialog .body .namein,
- hp-dialog .body .areasel {
- width: 100%;
- box-sizing: border-box;
- }
- /* Room settings contains long radio labels, two source pickers and live
- previews, so it deliberately uses hp-dialog's medium width. Keep its
- content shrinkable as well: the generic srcrow rule is nowrap because
- compact switch rows need it, but here that created a horizontal scroll
- box even on a wide desktop viewport. */
- hp-dialog.roomdialog .body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog.roomdialog .body > * {
- min-width: 0;
- max-width: 100%;
- box-sizing: border-box;
- }
- hp-dialog.roomdialog .srcrow {
- min-width: 0;
- align-items: flex-start;
- }
- hp-dialog.roomdialog .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- line-height: 1.35;
- }
- hp-dialog.roomdialog .dropbtn > b,
- hp-dialog.roomdialog .dropbtn > .ref {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
.devicepreview-empty {
min-height: 82px;
margin: 4px 0 14px;
@@ -3882,84 +2785,6 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
color: var(--hp-muted);
text-align: center;
}
- hp-dialog .row {
- display: flex;
- justify-content: flex-end;
- gap: var(--sp-4);
- width: 100%;
- min-width: 0;
- box-sizing: border-box;
- padding: var(--sp-5) var(--sp-6);
- border-top: 1px solid var(--hp-line);
- }
- /* Stable destructive/commit footer contract. A flex spacer cannot react
- when translated labels no longer fit: justify-content then overflows
- the destructive button through the left inset. Two real groups wrap
- as units instead — destructive actions stay left, while Cancel/Save
- move together to a right-aligned second row when necessary. */
- hp-dialog .row.dialog-action-footer {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- row-gap: var(--sp-4);
- }
- hp-dialog .dialog-action-group {
- display: flex;
- flex: 0 1 auto;
- flex-wrap: wrap;
- align-items: center;
- gap: var(--sp-4);
- max-width: 100%;
- min-width: 0;
- }
- hp-dialog .dialog-action-group .btn {
- flex: 0 0 auto;
- min-height: 44px;
- }
- hp-dialog .dialog-action-danger {
- margin-right: auto;
- }
- hp-dialog .dialog-action-commit {
- margin-left: auto;
- justify-content: flex-end;
- }
- hp-dialog .row.markerfooter {
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- }
- hp-dialog .row.roomfooter {
- align-items: center;
- flex-wrap: wrap;
- }
- /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
- wrapping remains as a phone fallback, but without a flex spacer (which
- used to strand Edit alone on the first line). */
- hp-dialog .row.infofooter {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- gap: var(--sp-3);
- }
- hp-dialog .row.infofooter .btn {
- flex-shrink: 0;
- }
- hp-dialog .row.infofooter .infofooter-close {
- margin-left: auto;
- }
- @media (max-width: 480px) {
- hp-dialog .row.infofooter {
- padding: var(--sp-4) var(--sp-5);
- }
- }
- .markeractions,
- .markersaveactions {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .markeractions:empty { display: none; }
- .markersaveactions { margin-left: auto; }
:host([data-pointer-hover]) .menu .it:hover {
background: rgba(127, 127, 127, 0.15);
}
@@ -4356,6 +3181,1182 @@ globalThis.__HOUSEPLAN_BUILD_FINGERPRINT__="d4954a6db7b0484955ad7748a0dd4c5d2825
font-size: var(--fs-s);
display: block;
}
+`,o`
+ .head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px var(--sp-5);
+ border-bottom: 1px solid var(--hp-line);
+ flex-wrap: wrap;
+ }
+ .title {
+ font-size: var(--fs-l);
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ white-space: nowrap;
+ }
+ .title ha-icon {
+ color: var(--hp-accent);
+ --mdc-icon-size: 18px;
+ }
+ @media (max-width: 620px) {
+ .head { gap: var(--sp-3); padding: var(--sp-4) 10px; }
+ .head .count { display: none; }
+ .head .title { font-size: var(--fs-m); }
+ }
+ .count {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ }
+ .btn {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ padding: var(--sp-3) 10px;
+ border-radius: var(--rad-m);
+ cursor: pointer;
+ transition: 0.15s;
+ font-family: inherit;
+ font-size: var(--fs-m);
+ }
+ .btn ha-icon {
+ --mdc-icon-size: 17px;
+ }
+ .btn.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .btn.ghost {
+ border: none;
+ }
+ .btn[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ /* the compass dial in the general settings (docs/SUN.md) */
+ .sunrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ margin: var(--sp-3) 0;
+ }
+ .suncol {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ min-width: 0;
+ }
+ .recoveryoverlay {
+ position: absolute;
+ inset: 0;
+ /* Above the contextual editor tray (70): recovery is the one state in
+ which every stage-editing surface must be inert. Dialogs live outside
+ the stage and retain their own higher card-level stacking context. */
+ z-index: 75;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: var(--sp-4);
+ padding: var(--sp-6);
+ box-sizing: border-box;
+ /* The final solid layer guarantees an opaque recovery surface even when
+ a custom HA theme exposes its card colour as rgba(). */
+ background:
+ linear-gradient(var(--ha-card-background, var(--card-background-color, #111)),
+ var(--ha-card-background, var(--card-background-color, #111))),
+ #111;
+ color: var(--primary-text-color, #fff);
+ text-align: center;
+ pointer-events: auto;
+ transition: opacity 0.15s ease;
+ }
+ .recoveryoverlay.phase-entering,
+ .recoveryoverlay.phase-leaving {
+ opacity: 0;
+ }
+ .recoveryoverlay.phase-fading-in,
+ .recoveryoverlay.phase-opaque {
+ opacity: 1;
+ }
+ .recoveryoverlay ha-icon {
+ --mdc-icon-size: 44px;
+ color: var(--hp-accent);
+ }
+ .oplock {
+ --oplock-size: calc(var(--icon-size, 2.5cqw) * 0.62);
+ --oplock-core-size: calc(var(--oplock-size) / 1.26875);
+ --oplock-stroke-ratio: 0.01875;
+ --oplock-stroke-width: max(1px, calc(var(--oplock-core-size) * var(--oplock-stroke-ratio)));
+ --oplock-core-bg: light-dark(#fff, #252525);
+ --oplock-core-fg: light-dark(#252525, #fff);
+ --oplock-shell-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
+ --oplock-shell-shadow:
+ 0 calc(var(--oplock-core-size) * .025) calc(var(--oplock-core-size) * .05) rgb(37 40 45 / 12%),
+ 0 calc(var(--oplock-core-size) * .1) calc(var(--oplock-core-size) * .175)
+ calc(var(--oplock-core-size) * -.025) rgb(37 40 45 / 18%);
+ --oplock-core-shadow: 0 0 0 0 transparent;
+ pointer-events: none; /* inert while editing; clickable in View (rule below) */
+ position: absolute;
+ transform: translate(-50%, -50%);
+ width: var(--oplock-size);
+ height: var(--oplock-size);
+ display: grid;
+ place-items: center;
+ border: 0;
+ background: transparent;
+ z-index: 1;
+ }
+ .oplock.theme-light {
+ --oplock-core-bg: #fff;
+ --oplock-core-fg: #252525;
+ --oplock-shell-stroke: #BCBCBC;
+ }
+ .oplock.theme-dark {
+ --oplock-core-bg: #252525;
+ --oplock-core-fg: #fff;
+ --oplock-shell-stroke: rgb(37 37 37 / 75%);
+ --oplock-core-shadow:
+ inset 0 calc(var(--oplock-core-size) * .0125)
+ calc(var(--oplock-core-size) * .0125) rgb(255 255 255 / 70%);
+ }
+ .oplock-shell {
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ border: var(--oplock-stroke-width) solid var(--oplock-shell-stroke);
+ border-radius: 50%;
+ background: transparent;
+ box-shadow: var(--oplock-shell-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ backdrop-filter: none;
+ }
+ .oplock-core {
+ width: var(--oplock-core-size);
+ height: var(--oplock-core-size);
+ border-radius: 50%;
+ background: var(--oplock-core-bg);
+ color: var(--oplock-core-fg);
+ box-shadow: var(--oplock-core-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ }
+ .oplock ha-icon {
+ --mdc-icon-size: calc(var(--oplock-core-size) * 0.55);
+ display: flex;
+ line-height: 0;
+ }
+ .oplock.locked {
+ --oplock-core-bg: #66D17A;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #66D17A;
+ }
+ .oplock.theme-light.locked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.locked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unlocked {
+ --oplock-core-bg: #F0410C;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #F0410C;
+ }
+ .oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.unlocked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unknown { --oplock-core-fg: var(--hp-muted); }
+ .btn.lockact {
+ width: 100%;
+ justify-content: center;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin-top: var(--sp-4);
+ }
+ .btn.lockact.warn {
+ color: var(--error-color, #d33);
+ border-color: var(--error-color, #d33);
+ }
+ .oprow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) 0;
+ }
+ .oprow b { margin-left: auto; }
+ .oprow.ok b { color: #66d17a; }
+ .oprow.warn b { color: var(--hp-open); }
+ .bindharow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ flex-wrap: wrap;
+ }
+ .bindharow .entcheck { opacity: 0.9; }
+ .ctrlchips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; }
+ .ctrlchip {
+ display: inline-flex; align-items: center; gap: var(--sp-2);
+ background: var(--hp-accent); color: var(--text-primary-color, #fff);
+ border-radius: var(--rad-l); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-s);
+ }
+ .ctrlchip ha-icon { --mdc-icon-size: 14px; cursor: pointer; }
+ .ctrllist { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); }
+ .ctrlopt {
+ display: flex; align-items: center; gap: var(--sp-4); text-align: left;
+ border: 0; background: transparent; color: var(--hp-txt);
+ padding: var(--sp-3) var(--sp-4); border-radius: var(--rad-s); cursor: pointer; font-family: inherit; font-size: var(--fs-m);
+ }
+ .ctrlopt .sub { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
+ .ctrlopt ha-icon { --mdc-icon-size: 16px; }
+ .ctrlstates { display: flex; flex-direction: column; gap: var(--sp-2); }
+ .ctrlstate { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--hp-muted); }
+ .ctrlstate.on { color: var(--hp-txt); }
+ .ctrlstate ha-icon { --mdc-icon-size: 15px; }
+ .cardpreview {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--sp-2);
+ margin: var(--sp-4) 0 var(--sp-1);
+ padding: 10px;
+ border: 1px dashed var(--hp-muted);
+ border-radius: var(--rad-m);
+ }
+ .cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
+ .cardpreview .cpmeta {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.3em;
+ font-weight: 600;
+ opacity: 0.85;
+ }
+ .cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
+ .iconauto {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-s);
+ margin: var(--sp-2) 0 0;
+ }
+ .iconauto ha-icon { --mdc-icon-size: 18px; }
+ .iconauto span { flex: 1; }
+ .iconauto .btn { min-height: 32px; padding: 0 var(--sp-3); }
+ hp-dialog .dfill {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ cursor: pointer;
+ }
+ .wallthick-dlg {
+ position: absolute;
+ z-index: 40;
+ min-width: 200px;
+ transform: translate(-50%, 8px);
+ padding: 10px 12px;
+ border-radius: 10px;
+ background: var(--card-background-color, #fff);
+ box-shadow: 0 8px 28px rgba(0,0,0,.22);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ pointer-events: auto;
+ }
+ .wallthick-dlg .row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .wallthick-dlg input[type="number"] {
+ width: 5.5em;
+ padding: 4px 6px;
+ border: 1px solid var(--divider-color, #ccc);
+ border-radius: 6px;
+ background: var(--input-fill, transparent);
+ color: var(--primary-text-color);
+ }
+ .areasel,
+ .namein {
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .namein {
+ width: 130px;
+ }
+ .habindingbanner {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ padding: var(--sp-3) var(--sp-4);
+ margin-bottom: var(--sp-4);
+ border: 1px solid var(--warning-color, #ff9800);
+ border-radius: var(--rad-m);
+ background: color-mix(in srgb, var(--warning-color, #ff9800) 12%, transparent);
+ color: var(--primary-text-color, #f1f3f6);
+ }
+ .habindingbanner > span { flex: 1; min-width: 0; }
+ .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
+ .habindingbanner.limited {
+ border-color: var(--secondary-text-color, #9aa0aa);
+ background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
+ }
+ .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
+ .srcrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ padding: var(--sp-1) 0;
+ }
+ .dispsection {
+ margin-top: var(--sp-5) !important;
+ padding-top: var(--sp-4);
+ border-top: 1px solid var(--hp-line);
+ font-weight: 600;
+ color: var(--hp-txt) !important;
+ }
+ .colorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .gsrow > hp-color-opacity {
+ min-width: min(100%, 210px);
+ justify-content: space-between;
+ }
+ .ripple-colorrow > hp-color-opacity {
+ width: 100%;
+ justify-content: space-between;
+ }
+ .ripple-sizerow > .opl {
+ min-width: 0;
+ }
+ .colorrow input[type='range'] { flex: 1; }
+ .colorrow .tempin { width: 70px; flex: none; }
+ /* beat the generic hp-dialog .body .namein { width:100% } rule */
+ hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
+ hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
+ .srcrow { flex-wrap: nowrap; }
+ /* native HA controls (rendered only when the HA frontend defines them;
+ old HA and the smoke env keep the plain inputs). ha-switch is taller
+ than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
+ .srcrow ha-switch { flex: none; }
+ .colorrow ha-slider { flex: 1; min-width: 0; }
+ .srcrow > span:first-of-type { white-space: nowrap; }
+ .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
+ .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
+ .markerlightgroup {
+ min-width: 0;
+ margin: var(--sp-5) 0 0;
+ padding: var(--sp-4);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ }
+ .markerlightgroup legend {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ padding: 0 var(--sp-2);
+ color: var(--hp-txt);
+ font-weight: 600;
+ }
+ .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
+ .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
+ .markerhelpfield { margin-top: var(--sp-4); }
+ .markerhelplabel {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ margin-bottom: var(--sp-1);
+ }
+ .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
+ .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
+ .markerlightgroup .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .markerglowvalue hp-color-opacity { flex: none; }
+ .markerlightdisabled {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ margin-top: var(--sp-2) !important;
+ }
+ .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
+ .markerbadgetechnical {
+ min-width: 0;
+ margin: var(--sp-1) 0 var(--sp-2) !important;
+ overflow-wrap: anywhere;
+ }
+ .markerbadgetechnical code { white-space: normal; }
+ .planrow {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ /* the "already uploaded" picker: a plan is never deleted for being
+ unreferenced, so it has to be findable again */
+ .savedplans {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ max-height: 240px;
+ overflow: auto;
+ margin: var(--sp-3) 0 var(--sp-1);
+ padding: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
+ /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
+ flex item whose automatic minimum size is ZERO (overflow != visible),
+ so inside hp-dialog .body — a flex column taller than its 66vh cap —
+ it shrank to a 14px sliver: the rows were in the DOM, the owner saw
+ a thin rounded stripe under the "Already uploaded" button. Don't
+ shrink, and keep a floor even when the box is empty or loading. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .savedplan {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
+ .savedplan img {
+ width: 56px;
+ height: 40px;
+ object-fit: contain;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ flex: none;
+ }
+ .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
+ .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+ .savedmeta .muted { font-size: var(--fs-s); }
+ .savedplan .btn.danger ha-icon { color: #f25a4a; }
+ .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
+ .planprev {
+ max-width: 120px;
+ max-height: 70px;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ }
+ .planname {
+ font-size: var(--fs-m);
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .planname.muted {
+ color: var(--hp-muted);
+ }
+ .filebtn {
+ cursor: pointer;
+ }
+ .fileupload { display: inline-flex; min-width: 0; }
+ .fileupload > input { display: none; }
+ .btn.danger {
+ border-color: #b3402a;
+ color: #ff7a5c;
+ }
+ hp-dialog .row .spacer {
+ flex: 1;
+ }
+ hp-dialog .body {
+ max-height: 66vh;
+ overflow-y: auto;
+ }
+ .descin {
+ width: 100%;
+ box-sizing: border-box;
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ resize: vertical;
+ /* flex column of the dialog body squeezes textareas — keep a usable height */
+ min-height: 92px;
+ flex-shrink: 0;
+ line-height: 1.35;
+ }
+ .bindsel {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ padding: var(--sp-4);
+ }
+ .bindsel .opt {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .bindsel .opt.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .curbind {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-m);
+ color: var(--hp-txt);
+ flex-wrap: wrap;
+ }
+ .curbind .ref {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
+ .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
+ .vacdiag > div > span { color: var(--secondary-text-color); }
+ .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
+ .vacpicker { margin: var(--sp-3) 0; }
+ .vacsource-warning { display: grid; gap: 8px; }
+ .vacsource-warning .btn { justify-self: start; }
+ .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
+ .vacpicker > summary::-webkit-details-marker { display: none; }
+ .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
+ .vacsource-list details { padding: 6px 0 0; }
+ .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
+ .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
+ width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
+ border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
+ text-align: left; cursor: pointer; }
+ .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
+ .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
+ .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
+ .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
+ .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
+ .vacfit {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 12;
+ overflow: visible;
+ touch-action: none;
+ cursor: grab;
+ /* the devlayer is pointer-events: none and every child opts back in —
+ without this line real clicks flew straight through the overlay
+ (owner: «уголки не кликабельны»; synthetic smoke events bypass
+ hit-testing, which is why they lied) */
+ pointer-events: auto;
+ }
+ .vacfit:active { cursor: grabbing; }
+ .vacfit polygon {
+ fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ stroke-dasharray: 6 4;
+ }
+ .vacfit text {
+ fill: var(--hp-accent);
+ font-size: 26px;
+ text-anchor: middle;
+ dominant-baseline: middle;
+ pointer-events: none;
+ user-select: none;
+ }
+ /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
+ .furnpalette {
+ display: flex;
+ flex-direction: column;
+ max-height: 38vh;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ background: var(--card-background-color, var(--hp-bg));
+ font-size: 0.85em;
+ }
+ .furnhd {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 8px;
+ font-weight: 600;
+ opacity: 0.9;
+ }
+ .furnhd .spacer { flex: 1; }
+ .furnbody {
+ overflow: auto;
+ padding: 0 8px 6px;
+ }
+ .furngroup {
+ margin: 6px 0 3px;
+ font-size: 0.85em;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ opacity: 0.6;
+ }
+ .furnrow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
+ .furnitem {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ width: 74px;
+ padding: 4px 2px;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ background: rgba(127, 127, 127, 0.08);
+ color: inherit;
+ font: inherit;
+ font-size: 0.8em;
+ line-height: 1.15;
+ text-align: center;
+ cursor: pointer;
+ }
+ .furnitem.on {
+ border-color: var(--hp-accent);
+ background: rgba(38, 166, 154, 0.18);
+ }
+ .furnprev {
+ width: 40px;
+ height: 40px;
+ color: var(--primary-text-color, currentColor);
+ }
+ .furnsize {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 6px;
+ padding: 6px 8px;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ }
+ .furnsize label {
+ display: flex;
+ align-items: baseline;
+ gap: 3px;
+ opacity: 0.8;
+ }
+ .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
+ .furnsize input {
+ width: 5.5em;
+ padding: 3px 5px;
+ }
+ .furnhint { opacity: 0.6; }
+ .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
+ /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
+ .vacfithandle {
+ fill: transparent;
+ stroke: none;
+ cursor: nwse-resize;
+ }
+ .vacfitknob {
+ fill: var(--hp-bg);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ pointer-events: none;
+ }
+ .vaccalbar {
+ position: fixed;
+ left: 50%;
+ bottom: 24px;
+ transform: translateX(-50%);
+ display: flex;
+ gap: var(--sp-5);
+ align-items: center;
+ background: var(--hp-bg);
+ color: var(--hp-txt);
+ border: 1px solid var(--hp-accent);
+ border-radius: var(--rad-l);
+ padding: 10px var(--sp-5);
+ z-index: 60;
+ box-shadow: var(--shadow-2);
+ }
+ .candlist {
+ max-height: 160px;
+ overflow-y: auto;
+ border-top: 1px solid var(--hp-line);
+ /* A scrollable box is a flex item that HAPPILY collapses: inside the
+ dialog body (a flex column) this list rendered its rows into a 1px
+ sliver — the DOM had 26 candidates and the user saw nothing. In the
+ binding dropdown it sits inside .droppanel (block context) and never
+ showed the bug. Field report, 2026-07-30. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .cand {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ border-radius: var(--rad-s);
+ font-size: var(--fs-m);
+ }
+ .cand.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .cand .cs {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ white-space: nowrap;
+ }
+ .opening-entity-candidate {
+ width: 100%;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ text-align: left;
+ }
+ .opening-entity-candidate.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .opening-entity-empty { cursor: default; }
+ .cand.sel .cs {
+ color: var(--text-primary-color, #fff);
+ opacity: 0.85;
+ }
+ .cand.muted {
+ color: var(--hp-muted);
+ cursor: default;
+ }
+ .pdfedit {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ align-items: center;
+ }
+ .pdftag {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .pdftag a {
+ color: var(--hp-txt);
+ text-decoration: none;
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .pdftag .x {
+ --mdc-icon-size: 15px;
+ cursor: pointer;
+ color: var(--hp-muted);
+ }
+ .entlist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ margin-bottom: 10px;
+ }
+ .entrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ border-radius: var(--rad-m);
+ background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
+ }
+ .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
+ .entrow.on ha-icon { color: var(--hp-accent); }
+ .entrow .en { flex: 1; font-size: var(--fs-m); }
+ .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
+ .entbtn {
+ min-width: 74px;
+ min-height: 32px;
+ padding: var(--sp-2) var(--sp-5);
+ border: 1px solid var(--hp-muted);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--hp-txt);
+ font: inherit;
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .entbtn.on {
+ background: var(--hp-accent);
+ border-color: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ font-weight: 600;
+ }
+ .inforow {
+ display: flex;
+ gap: 10px;
+ font-size: var(--fs-m);
+ margin: var(--sp-2) 0;
+ }
+ .inforow .k {
+ color: var(--hp-muted);
+ min-width: 84px;
+ }
+ .inforow a {
+ color: var(--hp-accent);
+ word-break: break-all;
+ }
+ .infodesc {
+ font-size: var(--fs-m);
+ white-space: pre-wrap;
+ margin-top: var(--sp-3);
+ }
+ .infodesc.muted {
+ color: var(--hp-muted);
+ }
+ .pdflist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ }
+ .pdf {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ color: var(--hp-accent);
+ text-decoration: none;
+ }
+ ha-icon-picker {
+ display: block;
+ }
+ .floorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-2);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .floorrow .floorlvl {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: 0 var(--sp-3);
+ }
+ .rtest {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ margin-bottom: var(--sp-4);
+ }
+ .rtest .namein { flex: 1; }
+ .rtest ha-icon { color: var(--hp-accent); }
+ .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
+ .rrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin: var(--sp-1) 0;
+ }
+ .rrow .rpat { flex: 2; }
+ .rrow .ricon { flex: 1.4; }
+ .rrow .rpat.bad { border-color: #ff7a5c; }
+ .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
+ .rrow .ract {
+ --mdc-icon-size: 16px;
+ color: var(--hp-muted);
+ cursor: pointer;
+ }
+ .gsrow .gsl {
+ min-width: 150px;
+ font-size: var(--fs-m);
+ color: var(--hp-muted);
+ }
+ .optimize-live {
+ display: grid;
+ justify-items: start;
+ gap: var(--sp-2);
+ margin-bottom: var(--sp-3);
+ }
+ .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
+ .optimize-cleanup { min-height: 44px; }
+ .optimize-selected { color: var(--hp-txt); }
+ .optimize-details {
+ margin-top: var(--sp-3);
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ overflow-wrap: anywhere;
+ }
+ .optimize-details > summary {
+ width: fit-content;
+ color: var(--hp-txt);
+ cursor: pointer;
+ font-weight: 600;
+ }
+ .optimize-details > summary:focus-visible {
+ outline: 2px solid var(--hp-accent);
+ outline-offset: 3px;
+ border-radius: var(--rad-s);
+ }
+ .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
+ .optimize-details li + li { margin-top: var(--sp-1); }
+ .btn.alignall { width: 100%; justify-content: center; }
+ .backupactions {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-3);
+ }
+ .backupactions .btn { justify-content: center; min-width: 0; }
+ .backupupload { display: inline-flex; min-width: 0; }
+ .backupupload > .btn { width: 100%; justify-content: center; }
+ .backupupload input { display: none; }
+ .backupbody { min-width: 0; }
+ .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
+ .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
+ .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
+ .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
+ .backupfile, .backupsummary, .backupcontent {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ gap: var(--sp-1);
+ }
+ .backupfile b, .backupcontent span {
+ overflow-wrap: anywhere;
+ }
+ .backupfile span, .backupsummary span, .backupcontent span {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .backupcounts {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-2) var(--sp-4);
+ font-size: var(--fs-s);
+ }
+ .backupwarn, .backuperror {
+ border-radius: var(--rad-s);
+ padding: var(--sp-3);
+ font-size: var(--fs-s);
+ line-height: 1.45;
+ overflow-wrap: anywhere;
+ }
+ .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
+ .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
+ .backupdetails {
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .backupdetails summary { cursor: pointer; font-weight: 700; }
+ .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
+ .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
+ .backupchoices {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ min-width: 0;
+ }
+ .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
+ .backupconfirm { align-items: flex-start !important; }
+ .backupconfirm > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ @media (max-width: 520px) {
+ .backupactions, .backupcounts { grid-template-columns: 1fr; }
+ }
+ .aboutver {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin: var(--sp-2) 0 var(--sp-3);
+ }
+ .aboutlink {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ width: fit-content;
+ color: var(--hp-accent);
+ text-decoration: none;
+ font-size: var(--fs-m);
+ padding: var(--sp-1) 0;
+ }
+ .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
+ hp-dialog .body {
+ padding: var(--sp-5) var(--sp-6);
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ }
+ hp-dialog .tapconfirm-body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog .tapconfirm-body p {
+ max-width: 100%;
+ min-width: 0;
+ margin: 0;
+ overflow-wrap: anywhere;
+ white-space: normal;
+ }
+ hp-dialog .tapconfirm-line {
+ color: var(--hp-muted);
+ }
+ hp-dialog .body label {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin-top: var(--sp-3);
+ }
+ hp-dialog .body .namein,
+ hp-dialog .body .areasel {
+ width: 100%;
+ box-sizing: border-box;
+ }
+ /* Room settings contains long radio labels, two source pickers and live
+ previews, so it deliberately uses hp-dialog's medium width. Keep its
+ content shrinkable as well: the generic srcrow rule is nowrap because
+ compact switch rows need it, but here that created a horizontal scroll
+ box even on a wide desktop viewport. */
+ hp-dialog.roomdialog .body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog.roomdialog .body > * {
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ }
+ hp-dialog.roomdialog .srcrow {
+ min-width: 0;
+ align-items: flex-start;
+ }
+ hp-dialog.roomdialog .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ line-height: 1.35;
+ }
+ hp-dialog.roomdialog .dropbtn > b,
+ hp-dialog.roomdialog .dropbtn > .ref {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ hp-dialog .row {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--sp-4);
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+ padding: var(--sp-5) var(--sp-6);
+ border-top: 1px solid var(--hp-line);
+ }
+ /* Stable destructive/commit footer contract. A flex spacer cannot react
+ when translated labels no longer fit: justify-content then overflows
+ the destructive button through the left inset. Two real groups wrap
+ as units instead — destructive actions stay left, while Cancel/Save
+ move together to a right-aligned second row when necessary. */
+ hp-dialog .row.dialog-action-footer {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ row-gap: var(--sp-4);
+ }
+ hp-dialog .dialog-action-group {
+ display: flex;
+ flex: 0 1 auto;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--sp-4);
+ max-width: 100%;
+ min-width: 0;
+ }
+ hp-dialog .dialog-action-group .btn {
+ flex: 0 0 auto;
+ min-height: 44px;
+ }
+ hp-dialog .dialog-action-danger {
+ margin-right: auto;
+ }
+ hp-dialog .dialog-action-commit {
+ margin-left: auto;
+ justify-content: flex-end;
+ }
+ hp-dialog .row.markerfooter {
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ hp-dialog .row.roomfooter {
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
+ wrapping remains as a phone fallback, but without a flex spacer (which
+ used to strand Edit alone on the first line). */
+ hp-dialog .row.infofooter {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ }
+ hp-dialog .row.infofooter .btn {
+ flex-shrink: 0;
+ }
+ hp-dialog .row.infofooter .infofooter-close {
+ margin-left: auto;
+ }
+ @media (max-width: 480px) {
+ hp-dialog .row.infofooter {
+ padding: var(--sp-4) var(--sp-5);
+ }
+ }
+ .markeractions,
+ .markersaveactions {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .markeractions:empty { display: none; }
+ .markersaveactions { margin-left: auto; }
`],Yt=new Set(["hacs","sun","backup","hassio","met","telegram_bot","mobile_app","systemmonitor","better_thermostat","adaptive_lighting","yandex_pogoda","upnp_serial_number"]),Jt=[{pattern:"протечк|leak|water sensor",icon:"mdi:water-alert"},{pattern:"клапан|valve",icon:"mdi:pipe-valve"},{pattern:"дым|smoke",icon:"mdi:smoke-detector"},{pattern:"термоголов|trv|radiator",icon:"mdi:radiator"},{pattern:"чайник|kettle|термопот",icon:"mdi:kettle"},{pattern:"сауна|sauna|harvia|парная|парилк",icon:"mdi:hot-tub"},{pattern:"температ|temperature|thermometer|climate sensor",icon:"mdi:thermometer"},{pattern:"qingping|air monitor|молекул|air quality",icon:"mdi:air-filter"},{pattern:"штор|curtain|blind|shade",icon:"mdi:roller-shade"},{pattern:"розетк|plug|socket|outlet",icon:"mdi:power-socket-de"},{pattern:"выключат|switch",icon:"mdi:light-switch"},{pattern:"лампа|лампочк|bulb|gx53|светильник|rgb|lamp|light strip",icon:"mdi:lightbulb"},{pattern:"камер|camera",icon:"mdi:cctv"},{pattern:"замок|ttlock|lock|sn609|sn9161",icon:"mdi:lock"},{pattern:"ворота|garage|gate",icon:"mdi:garage-variant"},{pattern:"калитк|door|открыт|contact",icon:"mdi:door"},{pattern:"счётчик|счетчик|kws|meter",icon:"mdi:meter-electric"},{pattern:"вводный автомат|breaker|wifimcbn",icon:"mdi:electric-switch"},{pattern:"myheat|котёл|котел|boiler|отоплен|heating",icon:"mdi:water-boiler"},{pattern:"холодильник|fridge",icon:"mdi:fridge"},{pattern:"стиральн|washer|washing",icon:"mdi:washing-machine"},{pattern:"сушилк|dryer",icon:"mdi:tumble-dryer"},{pattern:"пылесос|vacuum|dreame|roborock",icon:"mdi:robot-vacuum"},{pattern:"soundbar",icon:"mdi:soundbar"},{pattern:"колонк|станц|speaker|яндекс|yandex|алиса|alice",icon:"mdi:speaker"},{pattern:"tv|телевизор|hyundaitv|mitv|television",icon:"mdi:television"},{pattern:"keenetic|роутер|router|mesh|access point",icon:"mdi:router-wireless"},{pattern:"ибп|ups|kirpich",icon:"mdi:battery-charging-high"},{pattern:"slzb|координат|zigbee|coordinator",icon:"mdi:zigbee"},{pattern:"motion|движен|presence|присутств",icon:"mdi:motion-sensor"},{pattern:"humidity|влажн",icon:"mdi:water-percent"}];function Xt(t){const e=[];for(const i of t)if(i&&"string"==typeof i.pattern&&i.icon)try{e.push({re:new RegExp(i.pattern,"i"),icon:i.icon})}catch{}return e}const Zt=Xt(Jt),Qt={temperature:"mdi:thermometer",humidity:"mdi:water-percent",motion:"mdi:motion-sensor",occupancy:"mdi:motion-sensor",presence:"mdi:motion-sensor",door:"mdi:door",window:"mdi:window-closed",garage_door:"mdi:garage-variant",smoke:"mdi:smoke-detector",moisture:"mdi:water-alert",gas:"mdi:gas-cylinder",power:"mdi:meter-electric",energy:"mdi:meter-electric",illuminance:"mdi:brightness-5",co2:"mdi:molecule-co2",pm25:"mdi:air-filter",battery:"mdi:battery"},te="mdi:chip";function ee(t,e,i){const s=((t||"")+" "+(e||"")).toLowerCase();for(const{re:t,icon:e}of i??Zt)if(t.test(s))return e;return te}const ie=["light","switch","cover","valve","lock","climate","fan","media_player","camera","vacuum","humidifier","water_heater","alarm_control_panel","sensor","binary_sensor","event","button","number","select","update"];var se=/^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i,ne=Math.ceil,oe=Math.floor,ae="[BigNumber Error] ",re=ae+"Number primitive has more than 15 significant digits: ",le=1e14,ce=14,he=9007199254740991,de=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],pe=1e7,ue=1e9;function _e(t){var e=0|t;return t>0||t===e?e:e-1}function me(t){for(var e,i,s=1,n=t.length,o=t[0]+"";sc^i?1:-1;for(r=(l=n.length)<(c=o.length)?l:c,a=0;ao[a]^i?1:-1;return l==c?0:l>c^i?1:-1}function fe(t,e,i,s){if(ti||t!==oe(t))throw Error(ae+(s||"Argument")+("number"==typeof t?ti?" out of range: ":" not an integer: ":" not a primitive number: ")+String(t))}function ve(t){var e=t.c.length-1;return _e(t.e/ce)==e&&t.c[e]%2!=0}function ye(t,e){return(t.length>1?t.charAt(0)+"."+t.slice(1):t)+(e<0?"e":"e+")+e}function be(t,e,i){var s,n;if(e<0){for(n=i+".";++e;n+=i);t=n+t}else if(++e>(s=t.length)){for(n=i,e-=s;--e;n+=i);t+=n}else ey?p.c=p.e=null:t.e=10;l/=10,r++);return void(r>y?p.c=p.e=null:(p.e=r,p.c=[t]))}d=String(t)}else{if(!se.test(d=String(t)))return n(p,d,c);p.s=45==d.charCodeAt(0)?(d=d.slice(1),-1):1}(r=d.indexOf("."))>-1&&(d=d.replace(".","")),(l=d.search(/e/i))>0?(r<0&&(r=l),r+=+d.slice(l+1),d=d.substring(0,l)):r<0&&(r=d.length)}else{if(fe(e,2,$.length,"Base"),10==e&&S)return T(p=new M(t),_+p.e+1,m);if(d=String(t),c="number"==typeof t){if(0*t!=0)return n(p,d,c,e);if(p.s=1/t<0?(d=d.slice(1),-1):1,M.DEBUG&&d.replace(/^0\.0*|\./,"").length>15)throw Error(re+t)}else p.s=45===d.charCodeAt(0)?(d=d.slice(1),-1):1;for(i=$.slice(0,e),r=l=0,h=d.length;lr){r=h;continue}}else if(!a&&(d==d.toUpperCase()&&(d=d.toLowerCase())||d==d.toLowerCase()&&(d=d.toUpperCase()))){a=!0,l=-1,r=0;continue}return n(p,String(t),c,e)}c=!1,(r=(d=s(d,e,10,p.s)).indexOf("."))>-1?d=d.replace(".",""):r=d.length}for(l=0;48===d.charCodeAt(l);l++);for(h=d.length;48===d.charCodeAt(--h););if(d=d.slice(l,++h)){if(h-=l,c&&M.DEBUG&&h>15&&(t>he||t!==oe(t)))throw Error(re+p.s*t);if((r=r-l-1)>y)p.c=p.e=null;else if(r=f)?ye(l,a):be(l,a,"0");else if(o=(t=T(new M(t),e,i)).e,r=(l=me(t.c)).length,1==s||2==s&&(e<=o||o<=g)){for(;ra),l=be(l,o,"0"),o+1>r){if(--e>0)for(l+=".";e--;l+="0");}else if((e+=o-r)>0)for(o+1==r&&(l+=".");e--;l+="0");return t.s<0&&n?"-"+l:l}function D(t,e){for(var i,s,n=1,o=new M(t[0]);n=10;n/=10,s++);return(i=s+i*ce-1)>y?t.c=t.e=null:i=10;r/=10,n++);if((o=e-n)<0)o+=ce,a=e,l=d[c=0],h=oe(l/p[n-a-1]%10);else if((c=ne((o+1)/ce))>=d.length){if(!s)break t;for(;d.length<=c;d.push(0));l=h=0,n=1,a=(o%=ce)-ce+1}else{for(l=r=d[c],n=1;r>=10;r/=10,n++);h=(a=(o%=ce)-ce+n)<0?0:oe(l/p[n-a-1]%10)}if(s=s||e<0||null!=d[c+1]||(a<0?l:l%p[n-a-1]),s=i<4?(h||s)&&(0==i||i==(t.s<0?3:2)):h>5||5==h&&(4==i||s||6==i&&(o>0?a>0?l/p[n-a]:0:d[c-1])%10&1||i==(t.s<0?8:7)),e<1||!d[0])return d.length=0,s?(e-=t.e+1,d[0]=p[(ce-e%ce)%ce],t.e=-e||0):d[0]=t.e=0,t;if(0==o?(d.length=c,r=1,c--):(d.length=c+1,r=p[ce-o],d[c]=a>0?oe(l/p[n-a]%p[a])*r:0),s)for(;;){if(0==c){for(o=1,a=d[0];a>=10;a/=10,o++);for(a=d[0]+=r,r=1;a>=10;a/=10,r++);o!=r&&(t.e++,d[0]==le&&(d[0]=1));break}if(d[c]+=r,d[c]!=le)break;d[c--]=0,r=1}for(o=d.length;0===d[--o];d.pop());}t.e>y?t.c=t.e=null:t.e=f?ye(e,i):be(e,i,"0"),t.s<0?"-"+e:e)}return M.clone=t,M.ROUND_UP=0,M.ROUND_DOWN=1,M.ROUND_CEIL=2,M.ROUND_FLOOR=3,M.ROUND_HALF_UP=4,M.ROUND_HALF_DOWN=5,M.ROUND_HALF_EVEN=6,M.ROUND_HALF_CEIL=7,M.ROUND_HALF_FLOOR=8,M.EUCLID=9,M.config=M.set=function(t){var e,i;if(null!=t){if("object"!=typeof t)throw Error(ae+"Object expected: "+t);if(t.hasOwnProperty(e="DECIMAL_PLACES")&&(fe(i=t[e],0,ue,e),_=i),t.hasOwnProperty(e="ROUNDING_MODE")&&(fe(i=t[e],0,8,e),m=i),t.hasOwnProperty(e="EXPONENTIAL_AT")&&((i=t[e])&&i.pop?(fe(i[0],-ue,0,e),fe(i[1],0,ue,e),g=i[0],f=i[1]):(fe(i,-ue,ue,e),g=-(f=i<0?-i:i))),t.hasOwnProperty(e="RANGE"))if((i=t[e])&&i.pop)fe(i[0],-ue,-1,e),fe(i[1],1,ue,e),v=i[0],y=i[1];else{if(fe(i,-ue,ue,e),!i)throw Error(ae+e+" cannot be zero: "+i);v=-(y=i<0?-i:i)}if(t.hasOwnProperty(e="CRYPTO")){if((i=t[e])!==!!i)throw Error(ae+e+" not true or false: "+i);if(i){if("undefined"==typeof crypto||!crypto||!crypto.getRandomValues&&!crypto.randomBytes)throw b=!i,Error(ae+"crypto unavailable");b=i}else b=i}if(t.hasOwnProperty(e="MODULO_MODE")&&(fe(i=t[e],0,9,e),w=i),t.hasOwnProperty(e="POW_PRECISION")&&(fe(i=t[e],0,ue,e),k=i),t.hasOwnProperty(e="FORMAT")){if("object"!=typeof(i=t[e]))throw Error(ae+e+" not an object: "+i);x=i}if(t.hasOwnProperty(e="ALPHABET")){if("string"!=typeof(i=t[e])||/^.?$|[+\-.\s]|(.).*\1/.test(i))throw Error(ae+e+" invalid: "+i);S="0123456789"==i.slice(0,10),$=i}}return{DECIMAL_PLACES:_,ROUNDING_MODE:m,EXPONENTIAL_AT:[g,f],RANGE:[v,y],CRYPTO:b,MODULO_MODE:w,POW_PRECISION:k,FORMAT:x,ALPHABET:$}},M.isBigNumber=function(t){if(!t||!0!==t._isBigNumber)return!1;if(!M.DEBUG)return!0;var e,i,s=t.c,n=t.e,o=t.s;t:if("[object Array]"=={}.toString.call(s)){if((1===o||-1===o)&&n>=-ue&&n<=ue&&n===oe(n)){if(0===s[0]){if(0===n&&1===s.length)return!0;break t}if((e=(n+1)%ce)<1&&(e+=ce),String(s[0]).length==e){for(e=0;e=le||i!==oe(i))break t;if(0!==i)return!0}}}else if(null===s&&null===n&&(null===o||1===o||-1===o))return!0;throw Error(ae+"Invalid BigNumber: "+t)},M.maximum=M.max=function(){return D(arguments,-1)},M.minimum=M.min=function(){return D(arguments,1)},M.random=(o=9007199254740992,a=Math.random()*o&2097151?function(){return oe(Math.random()*o)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)},function(t){var e,i,s,n,o,r=0,l=[],c=new M(u);if(null==t?t=_:fe(t,0,ue),n=ne(t/ce),b)if(crypto.getRandomValues){for(e=crypto.getRandomValues(new Uint32Array(n*=2));r>>11))>=9e15?(i=crypto.getRandomValues(new Uint32Array(2)),e[r]=i[0],e[r+1]=i[1]):(l.push(o%1e14),r+=2);r=n/2}else{if(!crypto.randomBytes)throw b=!1,Error(ae+"crypto unavailable");for(e=crypto.randomBytes(n*=7);r=9e15?crypto.randomBytes(7).copy(e,r):(l.push(o%1e14),r+=7);r=n/7}if(!b)for(;r=10;o/=10,r++);ri-1&&(null==a[n+1]&&(a[n+1]=0),a[n+1]+=a[n]/i|0,a[n]%=i)}return a.reverse()}return function(s,n,o,a,r){var l,c,h,d,p,u,g,f,v=s.indexOf("."),y=_,b=m;for(v>=0&&(d=k,k=0,s=s.replace(".",""),u=(f=new M(n)).pow(s.length-v),k=d,f.c=e(be(me(u.c),u.e,"0"),10,o,t),f.e=f.c.length),h=d=(g=e(s,n,o,r?(l=$,t):(l=t,$))).length;0==g[--d];g.pop());if(!g[0])return l.charAt(0);if(v<0?--h:(u.c=g,u.e=h,u.s=a,g=(u=i(u,f,y,b,o)).c,p=u.r,h=u.e),v=g[c=h+y+1],d=o/2,p=p||c<0||null!=g[c+1],p=b<4?(null!=v||p)&&(0==b||b==(u.s<0?3:2)):v>d||v==d&&(4==b||p||6==b&&1&g[c-1]||b==(u.s<0?8:7)),c<1||!g[0])s=p?be(l.charAt(1),-y,l.charAt(0)):l.charAt(0);else{if(g.length=c,p)for(--o;++g[--c]>o;)g[c]=0,c||(++h,g=[1].concat(g));for(d=g.length;!g[--d];);for(v=0,s="";v<=d;s+=l.charAt(g[v++]));s=be(s,h,l.charAt(0))}return s}}(),i=function(){function t(t,e,i){var s,n,o,a,r=0,l=t.length,c=e%pe,h=e/pe|0;for(t=t.slice();l--;)r=((n=c*(o=t[l]%pe)+(s=h*o+(a=t[l]/pe|0)*c)%pe*pe+r)/i|0)+(s/pe|0)+h*a,t[l]=n%i;return r&&(t=[r].concat(t)),t}function e(t,e,i,s){var n,o;if(i!=s)o=i>s?1:-1;else for(n=o=0;ne[n]?1:-1;break}return o}function i(t,e,i,s){for(var n=0;i--;)t[i]-=n,n=t[i]1;t.splice(0,1));}return function(s,n,o,a,r){var l,c,h,d,p,u,_,m,g,f,v,y,b,w,k,x,$,S=s.s==n.s?1:-1,C=s.c,D=n.c;if(!(C&&C[0]&&D&&D[0]))return new M(s.s&&n.s&&(C?!D||C[0]!=D[0]:D)?C&&0==C[0]||!D?0*S:S/0:NaN);for(g=(m=new M(S)).c=[],S=o+(c=s.e-n.e)+1,r||(r=le,c=_e(s.e/ce)-_e(n.e/ce),S=S/ce|0),h=0;D[h]==(C[h]||0);h++);if(D[h]>(C[h]||0)&&c--,S<0)g.push(1),d=!0;else{for(w=C.length,x=D.length,h=0,S+=2,(p=oe(r/(D[0]+1)))>1&&(D=t(D,p,r),C=t(C,p,r),x=D.length,w=C.length),b=x,v=(f=C.slice(0,x)).length;v=r/2&&k++;do{if(p=0,(l=e(D,f,x,v))<0){if(y=f[0],x!=v&&(y=y*r+(f[1]||0)),(p=oe(y/k))>1)for(p>=r&&(p=r-1),_=(u=t(D,p,r)).length,v=f.length;1==e(u,f,_,v);)p--,i(u,x<_?$:D,_,r),_=u.length,l=1;else 0==p&&(l=p=1),_=(u=D.slice()).length;if(_=10;S/=10,h++);T(m,o+(m.e=h+c*ce-1)+1,a,d)}else m.e=c,m.r=+d;return m}}(),r=/^(-?)0([xbo])(?=\w[\w.]*$)/i,l=/^([^.]+)\.$/,c=/^\.([^.]+)$/,h=/^-?(Infinity|NaN)$/,d=/^\s*\+(?=[\w.])|^\s+|\s+$/g,n=function(t,e,i,s){var n,o=i?e:e.replace(d,"");if(h.test(o))t.s=isNaN(o)?null:o<0?-1:1;else{if(!i&&(o=o.replace(r,function(t,e,i){return n="x"==(i=i.toLowerCase())?16:"b"==i?2:8,s&&s!=n?t:e}),s&&(n=s,o=o.replace(l,"$1").replace(c,"0.$1")),e!=o))return new M(o,n);if(M.DEBUG)throw Error(ae+"Not a"+(s?" base "+s:"")+" number: "+e);t.s=null}t.c=t.e=null},p.absoluteValue=p.abs=function(){var t=new M(this);return t.s<0&&(t.s=1),t},p.comparedTo=function(t,e){return ge(this,new M(t,e))},p.decimalPlaces=p.dp=function(t,e){var i,s,n,o=this;if(null!=t)return fe(t,0,ue),null==e?e=m:fe(e,0,8),T(new M(o),t+o.e+1,e);if(!(i=o.c))return null;if(s=((n=i.length-1)-_e(this.e/ce))*ce,n=i[n])for(;n%10==0;n/=10,s--);return s<0&&(s=0),s},p.dividedBy=p.div=function(t,e){return i(this,new M(t,e),_,m)},p.dividedToIntegerBy=p.idiv=function(t,e){return i(this,new M(t,e),0,1)},p.exponentiatedBy=p.pow=function(t,e){var i,s,n,o,a,r,l,c,h=this;if((t=new M(t)).c&&!t.isInteger())throw Error(ae+"Exponent not an integer: "+R(t));if(null!=e&&(e=new M(e)),a=t.e>14,!h.c||!h.c[0]||1==h.c[0]&&!h.e&&1==h.c.length||!t.c||!t.c[0])return c=new M(Math.pow(+R(h),a?t.s*(2-ve(t)):+R(t))),e?c.mod(e):c;if(r=t.s<0,e){if(e.c?!e.c[0]:!e.s)return new M(NaN);(s=!r&&h.isInteger()&&e.isInteger())&&(h=h.mod(e))}else{if(t.e>9&&(h.e>0||h.e<-1||(0==h.e?h.c[0]>1||a&&h.c[1]>=24e7:h.c[0]<8e13||a&&h.c[0]<=9999975e7)))return o=h.s<0&&ve(t)?-0:0,h.e>-1&&(o=1/o),new M(r?1/o:o);k&&(o=ne(k/ce+2))}for(a?(i=new M(.5),r&&(t.s=1),l=ve(t)):l=(n=Math.abs(+R(t)))%2,c=new M(u);;){if(l){if(!(c=c.times(h)).c)break;o?c.c.length>o&&(c.c.length=o):s&&(c=c.mod(e))}if(n){if(0===(n=oe(n/2)))break;l=n%2}else if(T(t=t.times(i),t.e+1,1),t.e>14)l=ve(t);else{if(0===(n=+R(t)))break;l=n%2}h=h.times(h),o?h.c&&h.c.length>o&&(h.c.length=o):s&&(h=h.mod(e))}return s?c:(r&&(c=u.div(c)),e?c.mod(e):o?T(c,k,m,void 0):c)},p.integerValue=function(t){var e=new M(this);return null==t?t=m:fe(t,0,8),T(e,e.e+1,t)},p.isEqualTo=p.eq=function(t,e){return 0===ge(this,new M(t,e))},p.isFinite=function(){return!!this.c},p.isGreaterThan=p.gt=function(t,e){return ge(this,new M(t,e))>0},p.isGreaterThanOrEqualTo=p.gte=function(t,e){return 1===(e=ge(this,new M(t,e)))||0===e},p.isInteger=function(){return!!this.c&&_e(this.e/ce)>this.c.length-2},p.isLessThan=p.lt=function(t,e){return ge(this,new M(t,e))<0},p.isLessThanOrEqualTo=p.lte=function(t,e){return-1===(e=ge(this,new M(t,e)))||0===e},p.isNaN=function(){return!this.s},p.isNegative=function(){return this.s<0},p.isPositive=function(){return this.s>0},p.isZero=function(){return!!this.c&&0==this.c[0]},p.minus=function(t,e){var i,s,n,o,a=this,r=a.s;if(e=(t=new M(t,e)).s,!r||!e)return new M(NaN);if(r!=e)return t.s=-e,a.plus(t);var l=a.e/ce,c=t.e/ce,h=a.c,d=t.c;if(!l||!c){if(!h||!d)return h?(t.s=-e,t):new M(d?a:NaN);if(!h[0]||!d[0])return d[0]?(t.s=-e,t):new M(h[0]?a:3==m?-0:0)}if(l=_e(l),c=_e(c),h=h.slice(),r=l-c){for((o=r<0)?(r=-r,n=h):(c=l,n=d),n.reverse(),e=r;e--;n.push(0));n.reverse()}else for(s=(o=(r=h.length)<(e=d.length))?r:e,r=e=0;e0)for(;e--;h[i++]=0);for(e=le-1;s>r;){if(h[--s]=0;){for(i=0,p=y[n]%g,u=y[n]/g|0,o=n+(a=l);o>n;)i=((c=p*(c=v[--a]%g)+(r=u*c+(h=v[a]/g|0)*p)%g*g+_[o]+i)/m|0)+(r/g|0)+u*h,_[o--]=c%m;_[o]=i}return i?++s:_.splice(0,1),P(t,_,s)},p.negated=function(){var t=new M(this);return t.s=-t.s||null,t},p.plus=function(t,e){var i,s=this,n=s.s;if(e=(t=new M(t,e)).s,!n||!e)return new M(NaN);if(n!=e)return t.s=-e,s.minus(t);var o=s.e/ce,a=t.e/ce,r=s.c,l=t.c;if(!o||!a){if(!r||!l)return new M(n/0);if(!r[0]||!l[0])return l[0]?t:new M(r[0]?s:0*n)}if(o=_e(o),a=_e(a),r=r.slice(),n=o-a){for(n>0?(a=o,i=l):(n=-n,i=r),i.reverse();n--;i.push(0));i.reverse()}for((n=r.length)-(e=l.length)<0&&(i=l,l=r,r=i,e=n),n=0;e;)n=(r[--e]=r[e]+l[e]+n)/le|0,r[e]=le===r[e]?0:r[e]%le;return n&&(r=[n].concat(r),++a),P(t,r,a)},p.precision=p.sd=function(t,e){var i,s,n,o=this;if(null!=t&&t!==!!t)return fe(t,1,ue),null==e?e=m:fe(e,0,8),T(new M(o),t,e);if(!(i=o.c))return null;if(s=(n=i.length-1)*ce+1,n=i[n]){for(;n%10==0;n/=10,s--);for(n=i[0];n>=10;n/=10,s++);}return t&&o.e+1>s&&(s=o.e+1),s},p.shiftedBy=function(t){return fe(t,-9007199254740991,he),this.times("1e"+t)},p.squareRoot=p.sqrt=function(){var t,e,s,n,o,a=this,r=a.c,l=a.s,c=a.e,h=_+4,d=new M("0.5");if(1!==l||!r||!r[0])return new M(!l||l<0&&(!r||r[0])?NaN:r?a:1/0);if(0==(l=Math.sqrt(+R(a)))||l==1/0?(((e=me(r)).length+c)%2==0&&(e+="0"),l=Math.sqrt(+e),c=_e((c+1)/2)-(c<0||c%2),s=new M(e=l==1/0?"5e"+c:(e=l.toExponential()).slice(0,e.indexOf("e")+1)+c)):s=new M(l+""),s.c[0])for((l=(c=s.e)+h)<3&&(l=0);;)if(o=s,s=d.times(o.plus(i(a,o,h,1))),me(o.c).slice(0,l)===(e=me(s.c)).slice(0,l)){if(s.e0&&_>0){for(o=_%r||r,h=u.substr(0,o);o<_;o+=r)h+=c+u.substr(o,r);l>0&&(h+=c+u.slice(o)),p&&(h="-"+h)}s=d?h+(i.decimalSeparator||"")+((l=+i.fractionGroupSize)?d.replace(new RegExp("\\d{"+l+"}\\B","g"),"$&"+(i.fractionGroupSeparator||"")):d):h}return(i.prefix||"")+s+(i.suffix||"")},p.toFraction=function(t){var e,s,n,o,a,r,l,c,h,d,p,_,g=this,f=g.c;if(null!=t&&(!(l=new M(t)).isInteger()&&(l.c||1!==l.s)||l.lt(u)))throw Error(ae+"Argument "+(l.isInteger()?"out of range: ":"not an integer: ")+R(l));if(!f)return new M(g);for(e=new M(u),h=s=new M(u),n=c=new M(u),_=me(f),a=e.e=_.length-g.e-1,e.c[0]=de[(r=a%ce)<0?ce+r:r],t=!t||l.comparedTo(e)>0?a>0?e:h:l,r=y,y=1/0,l=new M(_),c.c[0]=0;d=i(l,e,0,1),1!=(o=s.plus(d.times(n))).comparedTo(t);)s=n,n=o,h=c.plus(d.times(o=h)),c=o,e=l.minus(d.times(o=e)),l=o;return o=i(t.minus(s),n,0,1),c=c.plus(o.times(h)),s=s.plus(o.times(n)),c.s=h.s=g.s,p=i(h,n,a*=2,m).minus(g).abs().comparedTo(i(c,s,a,m).minus(g).abs())<1?[h,n]:[c,s],y=r,p},p.toNumber=function(){return+R(this)},p.toPrecision=function(t,e){return null!=t&&fe(t,1,ue),C(this,t,e,2)},p.toString=function(t){var e,i=this,n=i.s,o=i.e;return null===o?n?(e="Infinity",n<0&&(e="-"+e)):e="NaN":(null==t?e=o<=g||o>=f?ye(me(i.c),o):be(me(i.c),o,"0"):10===t&&S?e=be(me((i=T(new M(i),_+o+1,m)).c),i.e,"0"):(fe(t,2,$.length,"Base"),e=s(be(me(i.c),o,"0"),10,t,n,!0)),n<0&&i.c[0]&&(e="-"+e)),e},p.valueOf=p.toJSON=function(){return R(this)},p._isBigNumber=!0,p[Symbol.toStringTag]="BigNumber",p[Symbol.for("nodejs.util.inspect.custom")]=p.valueOf,null!=e&&M.set(e),M}(),ke=class{key;left=null;right=null;constructor(t){this.key=t}},xe=class extends ke{constructor(t){super(t)}},$e=class{size=0;modificationCount=0;splayCount=0;splay(t){const e=this.root;if(null==e)return this.compare(t,t),-1;let i=null,s=null,n=null,o=null,a=e;const r=this.compare;let l;for(;;)if(l=r(a.key,t),l>0){let e=a.left;if(null==e)break;if(l=r(e.key,t),l>0&&(a.left=e.right,e.right=a,a=e,e=a.left,null==e))break;null==i?s=a:i.left=a,i=a,a=e}else{if(!(l<0))break;{let e=a.right;if(null==e)break;if(l=r(e.key,t),l<0&&(a.right=e.left,e.left=a,a=e,e=a.right,null==e))break;null==n?o=a:n.right=a,n=a,a=e}}return null!=n&&(n.right=a.left,a.left=o),null!=i&&(i.left=a.right,a.right=s),this.root!==a&&(this.root=a,this.splayCount++),l}splayMin(t){let e=t,i=e.left;for(;null!=i;){const t=i;e.left=t.right,t.right=e,e=t,i=e.left}return e}splayMax(t){let e=t,i=e.right;for(;null!=i;){const t=i;e.right=t.left,t.left=e,e=t,i=e.right}return e}_delete(t){if(null==this.root)return null;if(0!=this.splay(t))return null;let e=this.root;const i=e,s=e.left;if(this.size--,null==s)this.root=e.right;else{const t=e.right;e=this.splayMax(s),e.right=t,this.root=e}return this.modificationCount++,i}addNewRoot(t,e){this.size++,this.modificationCount++;const i=this.root;null!=i?(e<0?(t.left=i,t.right=i.right,i.right=null):(t.right=i,t.left=i.left,i.left=null),this.root=t):this.root=t}_first(){const t=this.root;return null==t?null:(this.root=this.splayMin(t),this.root)}_last(){const t=this.root;return null==t?null:(this.root=this.splayMax(t),this.root)}clear(){this.root=null,this.size=0,this.modificationCount++}has(t){return this.validKey(t)&&0==this.splay(t)}defaultCompare(){return(t,e)=>te?1:0}wrap(){return{getRoot:()=>this.root,setRoot:t=>{this.root=t},getSize:()=>this.size,getModificationCount:()=>this.modificationCount,getSplayCount:()=>this.splayCount,setSplayCount:t=>{this.splayCount=t},splay:t=>this.splay(t),has:t=>this.has(t)}}},Se=class t extends $e{root=null;compare;validKey;constructor(t,e){super(),this.compare=t??this.defaultCompare(),this.validKey=e??(t=>null!=t&&null!=t)}delete(t){return!!this.validKey(t)&&null!=this._delete(t)}deleteAll(t){for(const e of t)this.delete(e)}forEach(t){const e=this[Symbol.iterator]();let i;for(;i=e.next(),!i.done;)t(i.value,i.value,this)}add(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this}addAndReturn(t){const e=this.splay(t);return 0!=e&&this.addNewRoot(new xe(t),e),this.root.key}addAll(t){for(const e of t)this.add(e)}isEmpty(){return null==this.root}isNotEmpty(){return null!=this.root}single(){if(0==this.size)throw"Bad state: No element";if(this.size>1)throw"Bad state: Too many element";return this.root.key}first(){if(0==this.size)throw"Bad state: No element";return this._first().key}last(){if(0==this.size)throw"Bad state: No element";return this._last().key}lastBefore(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)<0)return this.root.key;let e=this.root.left;if(null==e)return null;let i=e.right;for(;null!=i;)e=i,i=e.right;return e.key}firstAfter(t){if(null==t)throw"Invalid arguments(s)";if(null==this.root)return null;if(this.splay(t)>0)return this.root.key;let e=this.root.right;if(null==e)return null;let i=e.left;for(;null!=i;)e=i,i=e.left;return e.key}retainAll(e){const i=new t(this.compare,this.validKey),s=this.modificationCount;for(const t of e){if(s!=this.modificationCount)throw"Concurrent modification during iteration.";this.validKey(t)&&0==this.splay(t)&&i.add(this.root.key)}i.size!=this.size&&(this.root=i.root,this.size=i.size,this.modificationCount++)}lookup(t){if(!this.validKey(t))return null;return 0!=this.splay(t)?null:this.root.key}intersection(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)&&i.add(t);return i}difference(e){const i=new t(this.compare,this.validKey);for(const t of this)e.has(t)||i.add(t);return i}union(t){const e=this.clone();return e.addAll(t),e}clone(){const e=new t(this.compare,this.validKey);return e.size=this.size,e.root=this.copyNode(this.root),e}copyNode(t){if(null==t)return null;const e=new xe(t.key);return function t(e,i){let s,n;do{if(s=e.left,n=e.right,null!=s){const e=new xe(s.key);i.left=e,t(s,e)}if(null!=n){const t=new xe(n.key);i.right=t,e=n,i=t}}while(null!=n)}(t,e),e}toSet(){return this.clone()}entries(){return new De(this.wrap())}keys(){return this[Symbol.iterator]()}values(){return this[Symbol.iterator]()}[Symbol.iterator](){return new Ce(this.wrap())}[Symbol.toStringTag]="[object Set]"},Me=class{tree;path=new Array;modificationCount=null;splayCount;constructor(t){this.tree=t,this.splayCount=t.getSplayCount()}[Symbol.iterator](){return this}next(){return this.moveNext()?{done:!1,value:this.current()}:{done:!0,value:null}}current(){if(!this.path.length)return null;const t=this.path[this.path.length-1];return this.getValue(t)}rebuildPath(t){this.path.splice(0,this.path.length),this.tree.splay(t),this.path.push(this.tree.getRoot()),this.splayCount=this.tree.getSplayCount()}findLeftMostDescendent(t){for(;null!=t;)this.path.push(t),t=t.left}moveNext(){if(this.modificationCount!=this.tree.getModificationCount()){if(null==this.modificationCount){this.modificationCount=this.tree.getModificationCount();let t=this.tree.getRoot();for(;null!=t;)this.path.push(t),t=t.left;return this.path.length>0}throw"Concurrent modification during iteration."}if(!this.path.length)return!1;this.splayCount!=this.tree.getSplayCount()&&this.rebuildPath(this.path[this.path.length-1].key);let t=this.path[this.path.length-1],e=t.right;if(null!=e){for(;null!=e;)this.path.push(e),e=e.left;return!0}for(this.path.pop();this.path.length&&this.path[this.path.length-1].right===t;)t=this.path.pop();return this.path.length>0}},Ce=class extends Me{getValue(t){return t.key}},De=class extends Me{getValue(t){return[t.key,t.key]}},Pe=t=>()=>t,Te=t=>{const e=t?(e,i)=>i.minus(e).abs().isLessThanOrEqualTo(t):Pe(!1);return(t,i)=>e(t,i)?0:t.comparedTo(i)};function Re(t){const e=t?(e,i,s,n,o)=>e.exponentiatedBy(2).isLessThanOrEqualTo(n.minus(i).exponentiatedBy(2).plus(o.minus(s).exponentiatedBy(2)).times(t)):Pe(!1);return(t,i,s)=>{const n=t.x,o=t.y,a=s.x,r=s.y,l=o.minus(r).times(i.x.minus(a)).minus(n.minus(a).times(i.y.minus(r)));return e(l,n,o,a,r)?0:l.comparedTo(0)}}var Ie=t=>t,Ae=t=>{if(t){const e=new Se(Te(t)),i=new Se(Te(t)),s=(t,e)=>e.addAndReturn(t),n=t=>({x:s(t.x,e),y:s(t.y,i)});return n({x:new we(0),y:new we(0)}),n}return Ie},ze=t=>({set:t=>{Fe=ze(t)},reset:()=>ze(t),compare:Te(t),snap:Ae(t),orient:Re(t)}),Fe=ze(),Ee=(t,e)=>t.ll.x.isLessThanOrEqualTo(e.x)&&e.x.isLessThanOrEqualTo(t.ur.x)&&t.ll.y.isLessThanOrEqualTo(e.y)&&e.y.isLessThanOrEqualTo(t.ur.y),Ne=(t,e)=>{if(e.ur.x.isLessThan(t.ll.x)||t.ur.x.isLessThan(e.ll.x)||e.ur.y.isLessThan(t.ll.y)||t.ur.y.isLessThan(e.ll.y))return null;const i=t.ll.x.isLessThan(e.ll.x)?e.ll.x:t.ll.x,s=t.ur.x.isLessThan(e.ur.x)?t.ur.x:e.ur.x;return{ll:{x:i,y:t.ll.y.isLessThan(e.ll.y)?e.ll.y:t.ll.y},ur:{x:s,y:t.ur.y.isLessThan(e.ur.y)?t.ur.y:e.ur.y}}},Oe=(t,e)=>t.x.times(e.y).minus(t.y.times(e.x)),He=(t,e)=>t.x.times(e.x).plus(t.y.times(e.y)),Le=t=>He(t,t).sqrt(),Be=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return Oe(n,s).div(Le(n)).div(Le(s))},qe=(t,e,i)=>{const s={x:e.x.minus(t.x),y:e.y.minus(t.y)},n={x:i.x.minus(t.x),y:i.y.minus(t.y)};return He(n,s).div(Le(n)).div(Le(s))},We=(t,e,i)=>e.y.isZero()?null:{x:t.x.plus(e.x.div(e.y).times(i.minus(t.y))),y:i},je=(t,e,i)=>e.x.isZero()?null:{x:i,y:t.y.plus(e.y.div(e.x).times(i.minus(t.x)))},Ue=class t{point;isLeft;segment;otherSE;consumedBy;static compare(e,i){const s=t.comparePoints(e.point,i.point);return 0!==s?s:(e.point!==i.point&&e.link(i),e.isLeft!==i.isLeft?e.isLeft?1:-1:Qe.compare(e.segment,i.segment))}static comparePoints(t,e){return t.x.isLessThan(e.x)?-1:t.x.isGreaterThan(e.x)?1:t.y.isLessThan(e.y)?-1:t.y.isGreaterThan(e.y)?1:0}constructor(t,e){void 0===t.events?t.events=[this]:t.events.push(this),this.point=t,this.isLeft=e}link(t){if(t.point===this.point)throw new Error("Tried to link already linked events");const e=t.point.events;for(let t=0,i=e.length;t{const s=i.otherSE;e.set(i,{sine:Be(this.point,t.point,s.point),cosine:qe(this.point,t.point,s.point)})};return(t,s)=>{e.has(t)||i(t),e.has(s)||i(s);const{sine:n,cosine:o}=e.get(t),{sine:a,cosine:r}=e.get(s);return n.isGreaterThanOrEqualTo(0)&&a.isGreaterThanOrEqualTo(0)?o.isLessThan(r)?1:o.isGreaterThan(r)?-1:0:n.isLessThan(0)&&a.isLessThan(0)?o.isLessThan(r)?-1:o.isGreaterThan(r)?1:0:a.isLessThan(n)?-1:a.isGreaterThan(n)?1:0}}},Ge=class t{events;poly;_isExteriorRing;_enclosingRing;static factory(e){const i=[];for(let s=0,n=e.length;s0&&(t=i)}let e=t.segment.prevInResult(),i=e?e.prevInResult():null;for(;;){if(!e)return null;if(!i)return e.ringOut;if(i.ringOut!==e.ringOut)return i.ringOut?.enclosingRing()!==e.ringOut?e.ringOut:e.ringOut?.enclosingRing();e=i.prevInResult(),i=e?e.prevInResult():null}}},Ve=class{exteriorRing;interiorRings;constructor(t){this.exteriorRing=t,t.poly=this,this.interiorRings=[]}addInterior(t){this.interiorRings.push(t),t.poly=this}getGeom(){const t=this.exteriorRing.getGeom();if(null===t)return null;const e=[t];for(let t=0,i=this.interiorRings.length;t0?(this.tree.delete(e),i.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&n){const t=s.getIntersection(n);if(null!==t){if(!s.isAnEndpoint(t)){const e=this._splitSafely(s,t);for(let t=0,s=e.length;t0)return-1;const s=e.comparePoint(t.rightSE.point);return 0!==s?s:-1}if(i.isGreaterThan(s)){if(a.isLessThan(r)&&a.isLessThan(c))return-1;if(a.isGreaterThan(r)&&a.isGreaterThan(c))return 1;const i=e.comparePoint(t.leftSE.point);if(0!==i)return i;const s=t.comparePoint(e.rightSE.point);return s<0?1:s>0?-1:1}if(a.isLessThan(r))return-1;if(a.isGreaterThan(r))return 1;if(n.isLessThan(o)){const i=e.comparePoint(t.rightSE.point);if(0!==i)return i}if(n.isGreaterThan(o)){const i=t.comparePoint(e.rightSE.point);if(i<0)return 1;if(i>0)return-1}if(!n.eq(o)){const t=l.minus(a),e=n.minus(i),h=c.minus(r),d=o.minus(s);if(t.isGreaterThan(e)&&h.isLessThan(d))return 1;if(t.isLessThan(e)&&h.isGreaterThan(d))return-1}return n.isGreaterThan(o)?1:n.isLessThan(o)||l.isLessThan(c)?-1:l.isGreaterThan(c)?1:t.ide.id?1:0}constructor(t,e,i,s){this.id=++Ze,this.leftSE=t,t.segment=this,t.otherSE=e,this.rightSE=e,e.segment=this,e.otherSE=t,this.rings=i,this.windings=s}static fromRing(e,i,s){let n,o,a;const r=Ue.comparePoints(e,i);if(r<0)n=e,o=i,a=1;else{if(!(r>0))throw new Error(`Tried to create degenerate segment at [${e.x}, ${e.y}]`);n=i,o=e,a=-1}const l=new Ue(n,!0),c=new Ue(o,!1);return new t(l,c,[s],[a])}replaceRightSE(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}bbox(){const t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:t.isLessThan(e)?t:e},ur:{x:this.rightSE.point.x,y:t.isGreaterThan(e)?t:e}}}vector(){return{x:this.rightSE.point.x.minus(this.leftSE.point.x),y:this.rightSE.point.y.minus(this.leftSE.point.y)}}isAnEndpoint(t){return t.x.eq(this.leftSE.point.x)&&t.y.eq(this.leftSE.point.y)||t.x.eq(this.rightSE.point.x)&&t.y.eq(this.rightSE.point.y)}comparePoint(t){return Fe.orient(this.leftSE.point,t,this.rightSE.point)}getIntersection(t){const e=this.bbox(),i=t.bbox(),s=Ne(e,i);if(null===s)return null;const n=this.leftSE.point,o=this.rightSE.point,a=t.leftSE.point,r=t.rightSE.point,l=Ee(e,a)&&0===this.comparePoint(a),c=Ee(i,n)&&0===t.comparePoint(n),h=Ee(e,r)&&0===this.comparePoint(r),d=Ee(i,o)&&0===t.comparePoint(o);if(c&&l)return d&&!h?o:!d&&h?r:null;if(c)return h&&n.x.eq(r.x)&&n.y.eq(r.y)?null:n;if(l)return d&&o.x.eq(a.x)&&o.y.eq(a.y)?null:a;if(d&&h)return null;if(d)return o;if(h)return r;const p=((t,e,i,s)=>{if(e.x.isZero())return je(i,s,t.x);if(s.x.isZero())return je(t,e,i.x);if(e.y.isZero())return We(i,s,t.y);if(s.y.isZero())return We(t,e,i.y);const n=Oe(e,s);if(n.isZero())return null;const o={x:i.x.minus(t.x),y:i.y.minus(t.y)},a=Oe(o,e).div(n),r=Oe(o,s).div(n),l=t.x.plus(r.times(e.x)),c=i.x.plus(a.times(s.x)),h=t.y.plus(r.times(e.y)),d=i.y.plus(a.times(s.y));return{x:l.plus(c).div(2),y:h.plus(d).div(2)}})(n,this.vector(),a,t.vector());return null===p?null:Ee(s,p)?Fe.snap(p):null}split(e){const i=[],s=void 0!==e.events,n=new Ue(e,!0),o=new Ue(e,!1),a=this.rightSE;this.replaceRightSE(o),i.push(o),i.push(n);const r=new t(n,a,this.rings.slice(),this.windings.slice());return Ue.comparePoints(r.leftSE.point,r.rightSE.point)>0&&r.swapEvents(),Ue.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),s&&(n.checkForConsuming(),o.checkForConsuming()),i}swapEvents(){const t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(let t=0,e=this.windings.length;t0){const t=i;i=s,s=t}if(i.prev===s){const t=i;i=s,s=t}for(let t=0,e=s.rings.length;t1===t.length&&t[0].isSubject;this._isInResult=i(t)!==i(e);break}}return this._isInResult}},ti=class{poly;isExterior;segments;bbox;constructor(t,e,i){if(!Array.isArray(t)||0===t.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=e,this.isExterior=i,this.segments=[],"number"!=typeof t[0][0]||"number"!=typeof t[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");const s=Fe.snap({x:new we(t[0][0]),y:new we(t[0][1])});this.bbox={ll:{x:s.x,y:s.y},ur:{x:s.x,y:s.y}};let n=s;for(let e=1,i=t.length;eXe.run("union",t,e),ni=(t,...e)=>Xe.run("intersection",t,e),oi=(t,...e)=>Xe.run("difference",t,e);function ai(t){const e=Math.max(0,Math.min(120,(t-40)/140*120));return`hsl(${Math.round(e)}, 85%, 55%)`}function ri(t,e){if(!(Number.isFinite(t)&&e>0))return t;const i=Math.round(t/e)*e;return Math.abs(i-t)<=1e-9*e?t:i}function li(t,e){if(e){const e=t/2.54;let i=Math.floor(e/12),s=Math.round(e-12*i);return 12===s&&(i+=1,s=0),`${i}′ ${s}″`}return`${(t/100).toFixed(2)} m`}function ci(t,e,i=1){const s=t[0].toFixed(i),n=t[1].toFixed(i),o=e[0].toFixed(i),a=e[1].toFixed(i),r=s=3?t.poly:t&&null!=t.x&&null!=t.y&&null!=t.w&&null!=t.h?[[t.x,t.y],[t.x+t.w,t.y],[t.x+t.w,t.y+t.h],[t.x,t.y+t.h]]:null}function di(t){const e=[];for(const i of t||[])i?.poly?.length>=3?e.push({poly:i.poly.map(t=>t.join(",")).join(" ")}):i&&null!=i.x&&null!=i.y&&null!=i.w&&null!=i.h&&e.push({rect:{x:i.x,y:i.y,w:i.w,h:i.h,rx:.03*Math.min(i.w,i.h)}});return e}function pi(t){const e=[],i=new Set;for(const s of t||[]){const t=hi(s);if(t)for(let s=0;s=90?t-=180:t<-90&&(t+=180),s.step&&s.step>0){const i=Math.sqrt(d),o=Math.min(Math.max(s.length||0,0)/2,i/2);let r=Math.round(p*i/s.step)*s.step;Math.abs(p*i-i/2)<=s.step/2&&(r=i/2),r=Math.max(o,Math.min(i-o,r));const l=r/i;n={x:e+l*c,y:a+l*h,angle:t}}else n={x:u[0],y:u[1],angle:t}}}return n}function _i(t,e,i,s,n,o=1){const a=e*Math.PI/180,r=[Math.cos(a),Math.sin(a)];let l=null,c=o;for(const e of pi(s)){const i=[[e[0],e[1]],[e[2],e[3]]],s=e=>Math.abs(r[0]*(e[1]-t[1])-r[1]*(e[0]-t[0]));if(s(i[0])>o||s(i[1])>o)continue;const n=(i[0][0]-t[0])*r[0]+(i[0][1]-t[1])*r[1],a=(i[1][0]-t[0])*r[0]+(i[1][1]-t[1])*r[1],h=Math.min(n,a),d=Math.max(n,a),p=h>0?h:d<0?-d:0;p[t[0]+r[0]*e,t[1]+r[1]*e],g=(h+d)/2;return{wallA:m(h),wallB:m(d),sideA:u,sideB:_,midA:m((h-p)/2),midB:m((p+d)/2),wallCenter:m(g),centered:Math.abs(g)<=n}}function mi(t,e,i=!1){if("passage"===t)return 1;if(null==e||"unavailable"===e||"unknown"===e)return"window"===t?0:1;const s=function(t){return["on","open","home","detected","playing","cleaning"].includes(String(t))}(e)!==!!i;return s?1:0}function gi(t){return"passage"===t.type?[]:[t.contact,t.lock].filter(t=>"string"==typeof t&&t.length>0)}function fi(t,e,i=.001){return Math.abs(t[0]-e[0])t[1]!=l>t[1]&&t[0]<(r-o)*(t[1]-a)/(l-a)+o&&(i=!i)}return i}function yi(t,e,i){const s=i[0]-e[0],n=i[1]-e[1],o=s*s+n*n;let a=o?((t[0]-e[0])*s+(t[1]-e[1])*n)/o:0;return a=Math.max(0,Math.min(1,a)),Math.hypot(t[0]-(e[0]+a*s),t[1]-(e[1]+a*n))}function bi(t,e,i=1e-6){if(!e||e.length<2)return!1;for(let s=0;sn&&a<-n||o<-n&&a>n)&&(r>n&&l<-n||r<-n&&l>n)}function $i(t,e=24){const i=t.map(t=>t[0]),s=t.map(t=>t[1]),n=Math.min(...i),o=Math.max(...i),a=Math.min(...s),r=Math.max(...s),l=Math.max(o-n,r-a)||1;let c=0,h=0,d=0;for(let e=0;e1e-9?[h/(3*c),d/(3*c)]:[(n+o)/2,(a+r)/2],u=(e,i)=>{const s=((e,i)=>{if(!vi([e,i],t))return-1/0;let s=1/0;for(let n=0;nm&&(m=c,_=[s,l])}if(_){const[t,i]=_,s=(o-n)/e,l=(r-a)/e;for(let e=-4;e<=4;e++)for(let n=-4;n<=4;n++){const o=t+s*e/4,a=i+l*n/4,r=u(o,a);r>m&&(m=r,_=[o,a])}}return _||Si(t)||t[0]}function Si(t,e=1e-6){if(!t||t.length<3)return null;const i=t.length,s=[t.reduce((t,e)=>t+e[0],0)/i,t.reduce((t,e)=>t+e[1],0)/i];if(wi(s,t,e))return s;for(let s=0;sCi(t,e,i));return s.filter(t=>!s.some(e=>e!==t&&Ci(e,t,i)))}function Ti(t){if(!t||t.length<3)return 0;let e=0;for(let i=0;i[t[0],t[1]]),[t[0][0],t[0][1]]]]}function Ii(t,e,i){for(let s=0;s1&&fi(i[0],i[i.length-1],e)&&i.pop(),i}function zi(t,e,i=1e-6){if(!t||t.length<3||!e||e.length<2)return null;const s=e[0],n=e[e.length-1];if(fi(s,n,i))return null;const o=Ii(t,s,i),a=Ii(t,n,i);if(o<0||a<0)return null;const r=e.slice(1,-1);for(const e of r)if(!wi(e,t,i))return null;for(let i=0;i{const a=[e];let r=(s+1)%t.length;for(let e=0;e<=t.length&&(a.push(t[r]),r!==o);e++)r=(r+1)%t.length;return a.push(n),Ai(a,i)};let c,h;if(o===a){const a=Ai([...e],i);if(a.length<3||Ti(a)<=i)return null;const r=[];for(let i=0;i=0?e:[...e].reverse();for(const t of i)r.push(t)}c=Ai(r,i),h=a}else c=Ai([...l(s,o,n,a),...[...r].reverse()],i),h=Ai([...l(n,a,s,o),...r],i);return c.length<3||h.length<3||Ti(c)<=i||Ti(h)<=i||Math.abs(Ti(c)+Ti(h)-Ti(t))>Math.max(i,1e-6*Ti(t))?null:[c,h]}function Fi(t,e,i){const[s,n]=t.split(":");return"device"===s?n:"entity"===s?"lg_"+n:e&&e.startsWith("v_")?e:i()}function Ei(t){return t.length?Math.round(t.reduce((t,e)=>t+e,0)/t.length):null}function Ni(t,e){if(e>t[2]/t[3]){const i=t[3],s=t[3]*e;return{x:t[0]-(s-t[2])/2,y:t[1],w:s,h:i}}const i=t[2],s=t[2]/e;return{x:t[0],y:t[1]-(s-t[3])/2,w:i,h:s}}function Oi(t,e,i,s){if(t.length<2)return;const n=e.x+s,o=e.x+e.w-s,a=e.y+s,r=e.y+e.h-s;for(let e=0;e<60;e++){let e=!1;for(let s=0;s=0)i=e.slice(0,n).trim(),s=e.slice(n+1).trim();else{const t=e.split(".");t.length>2&&(i=t.slice(0,2).join("."),s=t.slice(2).join("."))}return/^[a-z0-9_]+\.[a-z0-9_]+$/.test(i)?n>=0&&!s||s&&!/^[a-zA-Z0-9_.-]+$/.test(s)?null:s?{entity:i,attr:s}:{entity:i}:null}function Ji(t,e){const i=String(t??"").trim(),s=String(e??"").trim(),n=Yi(s?`${i}:${s}`:i);return n?`{${n.entity}${n.attr?`:${n.attr}`:""}}`:""}function Xi(t,e,i){const s=String(e??"").trim();if(!s)return null;const n=t?.states?.[s];if(!n)return null;const o=String(i??"").trim(),a=t=>t.slice(0,60);if(o){const e=function(t){if(null==t)return null;if(Array.isArray(t)){const e=t.map(t=>null==t?"":String(t)).join(", ");return e?e.slice(0,60):null}if("object"==typeof t)return null;const e=String(t);return""===e?null:e.slice(0,60)}(n.attributes?.[o]);if(null===e)return null;const i=t?.formatEntityAttributeValue;if("function"==typeof i)try{const e=i.call(t,n,o);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:e,formatted:!1}}const r=n.state;if(null==r||""===r)return null;const l=t?.formatEntityState;if("function"==typeof l)try{const e=l.call(t,n);if("string"==typeof e&&""!==e)return{text:a(e),formatted:!0}}catch{}return{text:a(String(r)),formatted:!1}}function Zi(t,e,i){const s=String(e??"").trim(),n=String(i??"").trim()||s;if(!n)return t.text;const o=t.formatted&&s?function(t,e){if(!e)return t;const i=t.replace(/\s+$/,"");return i.endsWith(e)?i.slice(0,i.length-e.length).replace(/\s+$/,""):t}(t.text,s):t.text;return`${o} ${n}`}function Qi(t,e){const i=(e?.entity||"").trim();if(!i)return"";const s=t?.states?.[i],n=s?.state;if(!s||null==n||""===n||"unavailable"===n||"unknown"===n)return Vi;const o=(e?.attr||"").trim(),a=Xi(t,i,o||null);if(null===a)return Vi;return Zi(a,o?"":String(s.attributes?.unit_of_measurement??"").trim(),e?.unit)}function ts(t,e,i,s=()=>!0){const n=t??"";let o=!1;const a=n.replace(/\{([^{}\r\n]+)\}/g,(t,e)=>{const n=Yi(e);return n?(o=!0,s(n.entity||"")?Qi(i,n):Vi):t});if(o)return a;const r=(e?.entity||"").trim();if(!r)return n;if(!s(r)){const t=n.indexOf(Ki);return t>=0?n.slice(0,t)+Vi+n.slice(t+2):n?`${n} ${Vi}`:Vi}const l=Qi(i,e),c=n.indexOf(Ki);return c>=0?n.slice(0,c)+l+n.slice(c+2):n?`${n} ${l}`:l}const es=20;function is(t){const e=Number(t?.scale);if(Number.isFinite(e)&&e>0)return Math.min(20,Math.max(.15,e));return{s:.7,m:1,l:1.5}[String(t?.size??"")]??1}function ss(t,e){if(!e)return t;let i=t;for(const[t,s]of Object.entries(e))i=i.split("{"+t+"}").join(String(s));return i}const ns="#55606c",os=.55,as={c:"#607d8b",a:.18};function rs(t,e=as){const i=t&&"object"==typeof t?t:null,s=i?.a;return{c:Rt(i?.c,e.c),a:"number"==typeof s&&Number.isFinite(s)?Math.min(1,Math.max(0,s)):e.a}}function ls(t,e){const i=rs(t),s=e?.settings?.custom_fill;return s&&"object"==typeof s?rs(s,i):i}function cs(t){const e=t?.settings||{},i=!t?.plan_url,s="glow"===e.fill_mode;return{showBorders:e.show_borders??i,showNames:e.show_names??i,color:Rt(e.room_color,ns),opacity:"number"==typeof e.room_opacity?Math.min(1,Math.max(0,e.room_opacity)):os,fill:["lqi","light","temp","custom"].includes(e.fill_mode)?e.fill_mode:"none",customFill:rs(e.custom_fill),glow:"boolean"==typeof e.glow_enabled?e.glow_enabled:s,tempMin:"number"==typeof e.temp_min?e.temp_min:20,tempMax:"number"==typeof e.temp_max?e.temp_max:25,showLqi:"boolean"==typeof e.show_lqi?e.show_lqi:null,cardFontScale:"number"==typeof e.card_font_scale&&e.card_font_scale>0?Math.min(3,Math.max(.5,e.card_font_scale)):1,labelTemp:!0===e.label_temp,labelHum:!0===e.label_hum,labelLqi:!0===e.label_lqi,labelLight:!0===e.label_light,bgColor:Rt(e.bg_color,null),hideDecor:!0===e.hide_decor,hideOpenings:!0===e.hide_openings}}function hs(t,e){if(e.bgColor)return e.bgColor;const i=t?.bg_color;return Rt(i,"")}const ds={light_on:{c:"#ffd45c",a:.18},light_off:{c:"#9aa0a6",a:.14},light_none:{c:"#6b7480",a:0},temp_cold:{c:"#4fc3f7",a:.18},temp_ok:{c:"#66d17a",a:.18},temp_hot:{c:"#ffd45c",a:.18},lqi_low:{c:"#f25a4a",a:.18},lqi_high:{c:"#4bd28f",a:.18},glow_base:{c:"#0d1b2a",a:.5},glow_light:{c:"#ffd9a0",a:.85},wall_fill:{c:"#ffffff",a:1}};function ps(t){const e={},i=t?.fill_colors||{};for(const t of Object.keys(ds)){const s=ds[t],n=i[t];e[t]={c:Rt(n?.c,s.c),a:n&&"number"==typeof n.a?Math.min(1,Math.max(0,n.a)):s.a}}return e}function us(t,e,i){const s=Math.min(1,Math.max(0,i)),n=[1,3,5].map(e=>parseInt(t.slice(e,e+2),16)),o=[1,3,5].map(t=>parseInt(e.slice(t,t+2),16)),a=n.map((t,e)=>Math.round(t+(o[e]-t)*s));return"#"+a.map(t=>t.toString(16).padStart(2,"0")).join("")}function _s(t,e,i,s,n,o,a,r=as){const l=function(t,e,i,s,n,o,a,r=as){if("custom"===t)return rs(r);if("lqi"===t){if(null==e)return null;const t=(e-40)/140;return{c:us(a.lqi_low.c,a.lqi_high.c,t),a:a.lqi_low.a+(a.lqi_high.a-a.lqi_low.a)*Math.min(1,Math.max(0,t))}}if("light"===t)return"none"===i?a.light_none.a>0?a.light_none:null:"on"===i?a.light_on:a.light_off;if("temp"===t){if(null==s)return null;const t=Math.min(n,o),e=Math.max(n,o);return se?a.temp_hot:a.temp_ok}return null}(t,e,i,s,n,o,a,r);return l?{color:l.c,opacity:l.a,mode:t}:null}const ms={blind:["mdi:blinds","mdi:blinds-open"],shade:["mdi:blinds","mdi:blinds-open"],shutter:["mdi:window-shutter","mdi:window-shutter-open"],curtain:["mdi:curtains-closed","mdi:curtains"],window:["mdi:window-closed","mdi:window-open"],awning:["mdi:awning-outline","mdi:awning"],door:["mdi:door-closed","mdi:door-open"],garage:["mdi:garage","mdi:garage-open"],gate:["mdi:gate","mdi:gate-open"],damper:["mdi:circle-slice-8","mdi:circle-outline"]},gs=[["mdi:roller-shade-closed","mdi:roller-shade"],["mdi:blinds-horizontal-closed","mdi:blinds-horizontal"],["mdi:garage-variant","mdi:garage-open-variant"],["mdi:door","mdi:door-open"]];function fs(t){for(const e of[...Object.values(ms),...gs])if(t===e[0]||t===e[1])return e;return null}function vs(t,e,i,s,n){if(!s||"unavailable"===s||"unknown"===s)return t;if(n){const i="cover"===e?fs(t):null;return i?"closed"===s?i[0]:i[1]:t}if("binary_sensor"===e){if("door"===i)return"on"===s?"mdi:door-open":"mdi:door-closed";if("window"===i)return"on"===s?"mdi:window-open":"mdi:window-closed";if("garage_door"===i)return"on"===s?"mdi:garage-open-variant":"mdi:garage-variant"}if("cover"===e){const e=ms[String(i||"")];if(e)return"closed"===s?e[0]:e[1];const n=fs(t);return n?"closed"===s?n[0]:n[1]:t}return"lock"===e?"locked"===s?"mdi:lock":"mdi:lock-open-variant":"light"===e&&"mdi:lightbulb"===t&&"on"===s?"mdi:lightbulb-on":t}function ys(t){const e=Math.min(4e4,Math.max(1e3,t))/100,i=e<=66?255:329.698727446*Math.pow(e-60,-.1332047592),s=e<=66?99.4708025861*Math.log(e)-161.1195681661:288.1221695283*Math.pow(e-60,-.0755148492),n=e>=66?255:e<=19?0:138.5177312231*Math.log(e-10)-305.0447927307,o=t=>Math.round(Math.min(255,Math.max(0,t)));return[o(i),o(s),o(n)]}const bs=1/2.2;function ws(t){const e=/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(t);return e?"#"+e.slice(1).map(t=>Math.min(255,Number(t)).toString(16).padStart(2,"0")).join(""):null}function ks(t){if(!t||"object"!=typeof t||Array.isArray(t))return null;const e=t;if(Object.keys(e).some(t=>"c"!==t&&"bri"!==t))return null;const i=Rt(e.c,null);return i?void 0===e.bri||null===e.bri?{c:i}:"number"!=typeof e.bri||!Number.isFinite(e.bri)||e.bri<.01||e.bri>1?null:{c:i,bri:e.bri}:null}function xs(t,e,i){const s=ks(e),n=t?.attributes||{},o=s?.bri??function(t){const e=t?.attributes?.brightness,i="number"==typeof e?e:"string"==typeof e&&""!==e.trim()?Number(e):Number.NaN;return Number.isFinite(i)?Math.max(0,Math.min(1,i/255)):1}(t);if(s)return{c:s.c,bri:o};const a=zt(n.rgb_color);if(a)return{c:ws(a)||Rt(i,"#ffd9a0"),bri:o};const r=Number(n.color_temp_kelvin)||(Number(n.color_temp)>0?1e6/Number(n.color_temp):NaN);return Number.isFinite(r)&&r>0?{c:(l=ys(r),"#"+l.slice(0,3).map(t=>Math.min(255,Math.max(0,Math.round(Number(t)||0))).toString(16).padStart(2,"0")).join("")),bri:o}:{c:Rt(i,"#ffd9a0"),bri:o};var l}function $s(t,e,i){return"on"===t?.state?xs(t,e,i):null}function Ss(t,e=1){const i=Math.max(0,Math.min(1,Number.isFinite(t)?t:1)),s=.7*Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*(.4+.6*Math.pow(i,bs));return Math.max(0,Math.min(1,s))}function Ms(t){return t.startsWith("light.")||t.startsWith("switch.")}function Cs(t,e,i=1e-6){const s=[];if(!t||!e||t.length<3||e.length<3)return s;for(let n=0;np||l>p)continue;const u=(n[0]-o[0])*h+(n[1]-o[1])*d,_=(a[0]-o[0])*h+(a[1]-o[1])*d,m=Math.max(0,Math.min(u,_)),g=Math.min(c,Math.max(u,_));g-m>i&&s.push([o[0]+h*m,o[1]+d*m,o[0]+h*g,o[1]+d*g])}}return s}function Ds(t,e,i=1e-6){const s=[];for(const n of t){const t=[n[0],n[1]],o=[n[2],n[3]],a=o[0]-t[0],r=o[1]-t[1],l=Math.hypot(a,r);if(lo||n>o)continue;const a=(s[0]-t[0])*c+(s[1]-t[1])*h,r=(s[2]-t[0])*c+(s[3]-t[1])*h,p=Math.max(0,Math.min(a,r)),u=Math.min(l,Math.max(a,r));u-p>i&&d.push([p,u])}if(!d.length){s.push([t[0],t[1],o[0],o[1]]);continue}d.sort((t,e)=>t[0]-e[0]);let p=0;for(const[e,n]of d)e-p>i&&s.push([t[0]+c*p,t[1]+h*p,t[0]+c*e,t[1]+h*e]),p=Math.max(p,n);l-p>i&&s.push([t[0]+c*p,t[1]+h*p,o[0],o[1]])}return s}function Ps(t,e,i=1e-6){const s=[];for(let e=0;e{if("string"!=typeof t||!t)return;const i=As(t);i.startsWith("/api/houseplan/content/")&&e.add(i)};for(const e of t?.spaces||[]){i(e?.plan_url);for(const t of e?.markers||[])for(const e of t?.pdfs||[])i(e?.url)}for(const e of t?.markers||[])for(const t of e?.pdfs||[])i(t?.url);return e}function As(t){return t?t.startsWith("/houseplan_files/plans/")?"/api/houseplan/content/plans/_/"+t.slice(23):t.startsWith("/houseplan_files/files/")?"/api/houseplan/content/files/"+t.slice(23):t:""}function zs(t,e){const i=e?.settings?.fill_mode;return"none"===i||"lqi"===i||"light"===i||"temp"===i||"custom"===i?i:t}function Fs(t,e){const i=e?.settings;return"boolean"==typeof i?.glow?i.glow:"glow"===i?.fill_mode||t}function Es(t,e=1){const i=Number(t);return Number.isFinite(i)&&i>0?Math.min(3,Math.max(.5,i)):e}function Ns(t,e){let i=180*Math.atan2(e[1]-t[1],e[0]-t[0])/Math.PI;return i<0&&(i+=360),i}function Os(t,e=.5){const i=(t%45+45)%45;return i<=e||45-i<=e}function Hs(t,e){const i=e[2]-e[0],s=e[3]-e[1],n=i*i+s*s;if(!n)return Math.hypot(t[0]-e[0],t[1]-e[1]);let o=((t[0]-e[0])*i+(t[1]-e[1])*s)/n;return o=Math.max(0,Math.min(1,o)),Math.hypot(t[0]-(e[0]+o*i),t[1]-(e[1]+o*s))}const Ls=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);function Bs(t,e){return"alarm_control_panel"===t||"siren"===t||"binary_sensor"===t&&!!e&&Ls.has(e)}function qs(t){if(!t)return null;const e=t.indexOf("#");if(e<=0)return null;const i=t.slice(0,e),s=t.slice(e+1);if(!s)return null;if(s.startsWith("@")){const t=s.slice(1);return t?{space:i,area:null,roomId:t}:null}return{space:i,area:s,roomId:null}}const Ws={availability:"available",status:"alarm",activity:"none"},js=new Set(["motion","vibration","sound"]),Us=new Set(["occupancy","presence"]),Gs=new Set(["door","window","garage_door","opening"]),Vs=new Set(["running","power"]),Ks=new Set(["smoke","gas","carbon_monoxide","moisture","safety","tamper","problem"]);const Ys=new Set(["running","working","washing","rinsing","spinning","drying","heating","cooling","cleaning","cooking","playing","recording","pumping","irrigating","humidifying","dehumidifying","fan","preheating","defrosting"]),Js=new Set([...Ys,"start","started","run","active","in_progress","wash","rinse","spin","dry"]),Xs=new Set(["off","idle","paused","standby","docked","finished","complete","completed","stopped","ready","sleeping","stop","end","done","inactive"]),Zs=new Set(["heat","cool","heat_cool","auto","dry","fan_only"]),Qs=t=>""===t||"unknown"===t||"unavailable"===t||"__missing__"===t,tn=t=>String(t??"").trim().toLowerCase(),en=t=>tn(t).replace(/[\s-]+/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,""),sn=new Map([["run_state",0],["job_state",0],["operation_state",0],["activity_state",0],["machine_state",1],["running_state",1],["status",2],["device_status",2],["machine_status",2]]),nn=new Set(["wifi","connection","signal","battery"]);function on(t,e){const i=t?.entities?.[e]||{};if("config"===i.entity_category)return null;const s=String(e||"").split(".").slice(1).join("."),n=[i.translation_key,i.original_name,i.name],o=[...n,s];n.some(t=>tn(t))||o.push(t?.states?.[e]?.attributes?.friendly_name);const a=o.map(en).filter(Boolean);if(a.some(t=>t.split("_").some(t=>nn.has(t))))return null;let r=null;for(const t of a)for(const[e,i]of sn)(t===e||t.endsWith(`_${e}`))&&(r=null==r?i:Math.min(r,i));return r}const an=(t,e)=>null!=on(t,e);function rn(t,e){if(!e.startsWith("switch."))return!1;const i=t?.entities?.[e]||{},s=t?.states?.[e],n=e.slice(7).toLowerCase();if(/(?:^|_)(?:main_)?power$/.test(n))return!0;if([i.translation_key,i.original_name,i.name].map(tn).some(t=>["power","main power","power switch","питание"].includes(t)))return!0;const o=tn(s?.attributes?.friendly_name);return/(?:^|[\s._-])(?:main[\s._-]+)?power$/.test(o)||/(?:^|[\s._-])питание$/.test(o)}function ln(t){for(const e of["hvac_action","action","current_operation","run_state","job_state","operation","activity"]){const i=tn(t?.[e]);if(Ys.has(i)||Xs.has(i))return i}return""}function cn(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n=String(e||"").split(".")[0],o=tn(i?.attributes?.device_class),a={eid:e,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===a.availability)return a;if(function(t,e,i){return"alarm_control_panel"===t?"triggered"===i:"on"===i&&Bs(t,e)}(n,o,s))return{...a,status:"alarm"};if("binary_sensor"===n)return js.has(o)?{...a,edge:"rising"}:Us.has(o)?{...a,activity:"on"===s?"presence":"none"}:Gs.has(o)?{...a,status:"on"===s?"open":"neutral",edge:"rising"}:"moving"===o?{...a,activity:"on"===s?"transition":"none"}:Vs.has(o)&&"on"===s?{...a,status:"working",activity:"running"}:a;if("cover"===n)return{...a,activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("lock"===n)return{...a,status:"unlocked"===s||"open"===s?"open":"neutral",activity:"locking"===s||"unlocking"===s?"transition":"none",edge:"terminal_transition"};if("valve"===n)return{...a,status:["open","opening","closing"].includes(s)?"open":"neutral",activity:"opening"===s||"closing"===s?"transition":"none",edge:"terminal_transition"};if("climate"===n){const t=ln(i.attributes),e=Array.isArray(i.attributes?.hvac_modes)?i.attributes.hvac_modes.map(tn):[],n=!Xs.has(s)&&(Zs.has(s)||Ys.has(s)||e.includes(s));return(t?Ys.has(t):n)?{...a,status:"working",activity:"running"}:a}if(["light","switch","fan","humidifier"].includes(n))return"on"===s?{...a,status:"working",activity:"running"}:a;if("media_player"===n)return"off"===s?{...a,availability:"unavailable"}:a;if("vacuum"===n)return"cleaning"===s?{...a,status:"working",activity:"running"}:"returning"===s?{...a,status:"working",activity:"transition"}:a;if("script"===n)return"on"===s?{...a,status:"working",activity:"running"}:a;if("automation"===n)return a;if("button"===n||"event"===n)return{...a,edge:"change"};const r=ln(i.attributes);return Ys.has(r)||Ys.has(s)&&!Xs.has(s)?{...a,status:"working",activity:"running"}:a}function hn(t,e,i){const s=i.filter(e=>e.startsWith("switch.")&&!t?.entities?.[e]?.entity_category),n=e.find(e=>rn(t,e));if(!(s.length>1&&!!n))return e.map(e=>cn(t,e));const o=e.find(e=>e!==n&&an(t,e)),a=cn(t,n);return"unavailable"===a.availability||"off"===a.state?e.map(e=>({eid:e,state:t?.states?.[e]?tn(t.states[e].state):"__missing__",availability:"unavailable",status:"neutral",activity:"none",edge:"none"})):e.map(e=>{if(e===o)return function(t,e){const i=t?.states?.[e],s=i?tn(i.state):"__missing__",n={eid:e,state:s,availability:Qs(s)?"unavailable":"available",status:"neutral",activity:"none",edge:"none"};if("unavailable"===n.availability)return n;const o=ln(i?.attributes);return Ys.has(o)||Js.has(s)&&!Xs.has(s)?{...n,status:"working",activity:"running"}:n}(t,e);return{...e===n?a:cn(t,e),status:"neutral",activity:"none",edge:"none"}})}function dn(t){if(!t.length)return{availability:"available",status:"neutral",activity:"none"};const e=t.filter(t=>"available"===t.availability);if(!e.length)return{availability:"unavailable",status:"neutral",activity:"none"};if(e.some(t=>"alarm"===t.status))return Ws;const i=e.some(t=>"working"===t.status)?"working":e.some(t=>"open"===t.status)?"open":"neutral",s=e.some(t=>"transition"===t.activity)?"transition":e.some(t=>"presence"===t.activity)?"presence":e.some(t=>"running"===t.activity)?"running":"none";return{availability:"available",status:i,activity:s}}function pn(t,e){if(!(t=>!!t&&!Qs(t))(t)||"unavailable"===e.availability||t===e.state)return null;if("rising"===e.edge)return"off"===t&&"on"===e.state?"event":null;if("change"===e.edge)return"event";if("terminal_transition"===e.edge){const i=new Set([t,e.state]);if(i.has("closed")&&i.has("open")||i.has("locked")&&i.has("unlocked"))return"transition"}return null}const un=(t,e=0)=>{const i=Number(t);return Number.isInteger(i)&&i>=0?i:e},_n=(t,e)=>{const i={rev:0,configRev:un(e),off:new Set};if(null==t)return{snapshot:i,valid:!1};const s=t.off;return!Number.isInteger(t.rev)||Number(t.rev)<0||!Number.isInteger(t.config_rev)||Number(t.config_rev)<0||!Array.isArray(s)||s.some(t=>"string"!=typeof t||!t)?{snapshot:i,valid:!1}:{snapshot:{rev:Number(t.rev),configRev:Number(t.config_rev),off:new Set(s)},valid:!0}};function mn(t,e=0){return _n(t,e).snapshot}function gn(t,e,i,s){const n=_n(e,i);return s&&n.valid&&n.snapshot.configRev===i?t.configRev===i&&n.snapshot.rev{try{const[i,s]=await Promise.all([t.callWS({type:"config/device_registry/list"}),t.callWS({type:"config/entity_registry/list"})]),n=Dn(i,"id"),o=Dn(s,"entity_id");if(!n||!o)throw new Error("invalid_registry_response");e.devices=n,e.entities=o,e.authoritative=!0,e.access="full",e.lastSuccess=Date.now(),e.error=void 0}catch(t){e.authoritative=!1,e.access="limited",e.error=function(t){if(t&&"object"==typeof t){const e=t;return String(e.message||e.code||e.error||"registry_unavailable")}return String(t||"registry_unavailable")}(t)}finally{e.revision++,e.loading=void 0,function(t){for(const e of[...t.listeners])try{e()}catch{}}(e)}})()),e.loading}function Tn(t,e){void 0===e.reloadTimer&&(e.reloadTimer=globalThis.setTimeout(()=>{e.reloadTimer=void 0,Pn(t,e)},80))}async function Rn(t,e){if(e.subscribing||e.unsubDevice&&e.unsubEntity)return e.subscribing;const i=t?.connection?.subscribeEvents;return"function"==typeof i?(e.subscribing=(async()=>{try{e.unsubDevice||(e.unsubDevice=await i.call(t.connection,()=>Tn(t,e),"device_registry_updated")),e.unsubEntity||(e.unsubEntity=await i.call(t.connection,()=>Tn(t,e),"entity_registry_updated"))}catch{}finally{0===e.refs&&(e.unsubDevice?.(),e.unsubEntity?.(),e.unsubDevice=void 0,e.unsubEntity=void 0),e.subscribing=void 0}})(),e.subscribing):void 0}function In(t,e){const i=Cn(t);if(!i)return()=>{};const s=0===i.refs;i.refs++,i.listeners.add(e),("pending"===i.access||s)&&Pn(t,i),Rn(t,i);let n=!1;return()=>{n||(n=!0,i.listeners.delete(e),i.refs=Math.max(0,i.refs-1),i.refs>0||(i.unsubDevice?.(),i.unsubEntity?.(),i.unsubDevice=void 0,i.unsubEntity=void 0,void 0!==i.reloadTimer&&globalThis.clearTimeout(i.reloadTimer),i.reloadTimer=void 0))}}function An(t){const e=Cn(t);if(!e)return{revision:0,authoritative:!1,access:"limited",devices:t?.devices||{},entities:t?.entities||{},lastSuccess:0,error:"registry_unavailable"};let i=t?.devices||{},s=t?.entities||{};if(e.authoritative){const n=e.liveDevices!==i||e.liveEntities!==s,o=void 0!==e.liveDevices||void 0!==e.liveEntities;if(n&&(e.liveDevices=i,e.liveEntities=s,e.projectedRevision=void 0,o&&Tn(t,e)),e.projectedRevision!==e.revision||!e.projectedDevices||!e.projectedEntities){const t={...e.devices},n={...e.entities};for(const[e,s]of Object.entries(i))Object.prototype.hasOwnProperty.call(t,e)||(t[e]=s);for(const[t,e]of Object.entries(s))Object.prototype.hasOwnProperty.call(n,t)||(n[t]=e);e.projectedDevices=t,e.projectedEntities=n,e.projectedRevision=e.revision}i=e.projectedDevices,s=e.projectedEntities}return{revision:e.revision,authoritative:e.authoritative,access:e.access,devices:i,entities:s,lastSuccess:e.lastSuccess,error:e.error}}function zn(t){if(!t||"object"!=typeof t&&"function"!=typeof t)return 0;let e=xn.get(t);return e||(e=$n++,xn.set(t,e)),e}function Fn(t,e=An(t)){return[e.revision,e.access,zn(e.devices),zn(e.entities)].join(":")}function En(t){return!!t&&null==t.disabled_by}function Nn(t,e=An(t)){const i={},s={},n={};for(const[t,s]of Object.entries(e.devices||{}))En(s)&&(i[t]=s);for(const[t,i]of Object.entries(e.entities||{})){if(!En(i))continue;const n=i.device_id?e.devices?.[i.device_id]:null;e.authoritative&&i.device_id&&!n||(n&&!En(n)||(s[t]=i))}for(const[i,s]of Object.entries(t?.states||{})){const t=e.entities?.[i];if(t&&!En(t))continue;const o=t?.device_id?e.devices?.[t.device_id]:null;e.authoritative&&t?.device_id&&!o||(o&&!En(o)||(n[i]=s))}return{...t,devices:i,entities:s,states:n}}function On(t,e=An(t)){return{...t,devices:e.devices||{},entities:e.entities||{}}}function Hn(t,e){const i=[];for(const[s,n]of Object.entries(t||{}))n?.device_id===e&&i.push(s);return i}function Ln(){if(Mn)return Mn;if("undefined"==typeof localStorage)return{};try{const t=JSON.parse(localStorage.getItem(Sn)||"{}");if(!t||"object"!=typeof t||Array.isArray(t))return{};const e=Date.now(),i={};for(const[s,n]of Object.entries(t))!n||"active"!==n.kind&&"ha_disabled"!==n.kind||!Number.isFinite(n.ts)||e-n.ts>7776e6||(i[s]=n);return Mn=i,Mn}catch{return{}}}function Bn(t,e,i=An(t)){if(!e||"virtual"===e)return{kind:"active",enabledEntityIds:[],allEntityIds:[]};const s=e.indexOf(":");if(s<1)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const n=e.slice(0,s),o=e.slice(s+1);if("device"!==n&&"entity"!==n||!o)return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]};const a=i.devices||{},r=i.entities||{};if(i.authoritative){if("device"===n){const t=a[o];if(!t)return{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[]};const e=Hn(r,o);if(!En(t))return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:e};const i=e.filter(t=>En(r[t]));return e.length&&!i.length?{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:e}:{kind:"active",enabledEntityIds:i,allEntityIds:e}}const e=r[o];return e&&!En(e)?{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!a[e.device_id]?{kind:"orphaned",reason:"device_missing",enabledEntityIds:[],allEntityIds:[o]}:e?.device_id&&!En(a[e.device_id])?{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]}:e||t?.states?.[o]?{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}:{kind:"orphaned",reason:"entity_missing",enabledEntityIds:[],allEntityIds:[]}}const l=function(t){return Ln()[t]||null}(e);if("device"===n){const e=a[o],i=Hn(r,o);if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:i};const s=i.filter(t=>{const e=r[t];return null==e?.disabled_by&&(!e.device_id||null==a[e.device_id]?.disabled_by)});if(i.length&&!s.length&&i.every(t=>null!=r[t]?.disabled_by))return{kind:"ha_disabled",reason:"all_entities",enabledEntityIds:[],allEntityIds:i};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"device",enabledEntityIds:[],allEntityIds:i};if(e||s.some(e=>!!t?.states?.[e]))return{kind:"active",enabledEntityIds:s,allEntityIds:i}}else{const e=r[o];if(null!=e?.disabled_by)return{kind:"ha_disabled",reason:"entity",enabledEntityIds:[],allEntityIds:[o]};if(e?.device_id&&null!=a[e.device_id]?.disabled_by)return{kind:"ha_disabled",reason:"device",enabledEntityIds:[],allEntityIds:[o]};if("ha_disabled"===l?.kind)return{kind:"ha_disabled",reason:l.reason||"entity",enabledEntityIds:[],allEntityIds:[o]};if(e||t?.states?.[o])return{kind:"active",enabledEntityIds:[o],allEntityIds:[o]}}return{kind:"unverified",reason:"registry_unavailable",enabledEntityIds:[],allEntityIds:[]}}function qn(t){const e={};for(const[i,s]of Object.entries(t.entities)){if(!s?.device_id||!En(s))continue;const n=t.devices?.[s.device_id];n&&!En(n)||(e[s.device_id]=e[s.device_id]||[]).push(i)}return e}function Wn(t,e){const i=new Map;for(const s of t){if(!0===s?.removed)continue;const t=s?.binding||"";if(!t.startsWith("entity:"))continue;const n=t.slice(7);if(!n)continue;const o=e?.entities?.[n]?.device_id;if(!o)continue;const a=i.get(o)||new Set;a.add(n),i.set(o,a)}return{byDevice:i}}function jn(t,e,i,s){const n=s.byDevice.get(e);return n?.size?{partial:!0,entityIds:i.filter(e=>!n.has(e)&&!t?.entities?.[e]?.hidden)}:{partial:!1,entityIds:[...i]}}function Un(t,e,i){if(e.identifiers?.[0]?.[0])return e.identifiers[0][0];for(const e of i){const i=t.entities[e]?.platform;if(i)return i}return""}function Gn(t,e){if(/_device_temperature$/.test(e))return!1;if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_temperature$/.test(e);const s=i.attributes||{};return"temperature"===s.device_class||/°C|°F/.test(s.unit_of_measurement||"")||/_temperature$/.test(e)}const Vn=["vacuum","lawn_mower","climate","media_player","light","cover","lock","valve","alarm_control_panel","water_heater","fan","humidifier","siren","camera","remote"],Kn=t=>[...t.filter(t=>!t.reg?.hidden),...t.filter(t=>!!t.reg?.hidden)];function Yn(t,e){const i=e.map(e=>({eid:e,reg:t?.entities?.[e]})).filter(t=>!!t.reg);if(!i.length)return[];const s=i.filter(t=>!t.reg.entity_category),n=s.length?s:i;for(const t of Vn){const e=n.filter(e=>e.eid.startsWith(t+"."));if(e.length)return Kn(e).map(t=>t.eid)}const o=n.filter(e=>function(t,e){if(!e.startsWith("binary_sensor."))return!1;const i=tn(t?.states?.[e]?.attributes?.device_class||t?.entities?.[e]?.device_class||t?.entities?.[e]?.original_device_class);return js.has(i)||Us.has(i)||Gs.has(i)||Vs.has(i)||"moving"===i||Ks.has(i)}(t,e.eid));if(o.length)return Kn(o).map(t=>t.eid);const a=n.filter(t=>t.eid.startsWith("switch."));if(a.length){const e=Kn(a),s=e.find(e=>rn(t,e.eid));if(a.length>1&&s){const e=i.filter(e=>e.eid!==s.eid&&"config"!==e.reg?.entity_category&&null!=on(t,e.eid)),n=e.filter(t=>!t.reg?.entity_category),o=n.length?n:e;for(const e of[0,1,2]){const i=Kn(o.filter(i=>on(t,i.eid)===e));if(i.length)return[i[0].eid,s.eid]}}return[(s||e[0]).eid]}const r=[];for(const t of ie)r.push(...Kn(n.filter(e=>e.eid.startsWith(t+"."))));return r.push(...Kn(n.filter(t=>!ie.includes(t.eid.split(".")[0])))),r.map(t=>t.eid)}function Jn(t,e,i){const s=e.map(e=>({eid:e,reg:t.entities[e],st:t.states[e]})).filter(t=>t.reg),n=[s.filter(t=>!t.reg.hidden&&!t.reg.entity_category),s.filter(t=>!t.reg.entity_category),s.filter(t=>!t.reg.hidden),s];if("mdi:thermometer"===i||"mdi:air-filter"===i)for(const e of n){const i=e.find(e=>Gn(t,e.eid));if(i)return i.eid}const o=Yn(t,e);return o.find(Ms)||o[0]}function Xn(t,e,i=[]){return[...new Set(Zn(t,e,i))].filter(t=>Ms(t))}function Zn(t,e,i=[]){const s=t?.startsWith("entity:")?new Set([t.slice(7)]):new Set(i);return(e||[]).filter(t=>"string"==typeof t&&!s.has(t))}function Qn(t,e){if("string"==typeof e)return t.area===e;const i=t.marker?.room_id;return i?!!e.id&&i===e.id:!!e.area&&t.area===e.area}function to(t){const e=t.marker?.binding?.startsWith("entity:")?t.marker.binding.slice(7):null,i=t.entities.filter(Ms),s=t.primary&&Ms(t.primary)?t.primary:null;return[...new Set([e,s,...i].filter(t=>!!t&&Ms(t)))]}function eo(t){const e=to(t),i=t.marker?.light_entity;return i&&e.includes(i)?i:e[0]||null}function io(t,e){if(!1===e.marker?.is_light)return[];if(!0===e.marker?.is_light){return[{eid:eo(e),via:"forced"}]}const i=e.primary||Yn(t,e.entities)[0];return i&&!i.startsWith("light.")?[]:e.entities.filter(e=>e.startsWith("light.")&&!!t.states?.[e]).map(t=>({eid:t,via:"light"}))}const so=new WeakMap,no=new WeakMap;function oo(t){return t.map(t=>[t.id||"",!0===t.hidden?1:0,t.primary||"",[...t.entities].join(","),void 0===t.controls?"":[...t.controls].join(","),t.marker?.id||"",t.marker?.binding||"",t.marker?.is_light,t.marker?.tap_action||"",!0===t.marker?.removed?1:0,t.marker?.light_entity||"",null==t.marker?.controls?"":[...t.marker.controls].join(",")].join("")).join("")}function ao(t){const e=oo(t),i=so.get(t);if(i?.fingerprint===e)return i;const s=t.filter(t=>!t.hidden),n=new Map,o=new Map;for(const e of t){o.set(e,Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities));const t=e.hidden||!0!==e.marker?.is_light?null:e.marker?.id||e.id;t&&n.set(String(t),e)}const a=new Map;for(const e of t){const t=o.get(e)||[],i=new Set(void 0===e.controls?t.filter(Ms):e.controls.filter(Ms)),s=eo(e),r=i.size?[...i]:s?[s]:[];for(const i of t){if(!i.startsWith("marker:"))continue;const t=i.slice(7);if(!t||t===String(e.marker?.id||e.id||""))continue;if(!n.has(t))continue;const s=a.get(t)||{markerId:t,controllers:[],driverEids:[]};s.controllers.push({device:e,driverEids:[...r]}),s.driverEids=[...new Set([...s.driverEids,...r])],a.set(t,s)}}const r={fingerprint:e,visible:s,markerById:n,persistedByDevice:o,incomingByMarker:a};return so.set(t,r),r}function ro(t){return ao(t).incomingByMarker}function lo(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities),s=Zn(e.marker?.binding,e.marker?.controls??e.controls,e.entities);return(null!=e.marker?.is_light||!i.length&&!s.some(t=>t.startsWith("marker:")))&&io(t,e).length>0}function co(t,e){const i=Xn(e.marker?.binding,e.controls??e.marker?.controls,e.entities).map(t=>({eid:t,via:"controls"})),s=lo(t,e)?io(t,e):[];for(const t of s)t.eid&&i.some(e=>e.eid===t.eid)||i.push(t);return i}function ho(t,e,i,s){if(null!=i)return ho(t,e,null,s).filter(t=>Qn(t.device,i));const n=oo(e),o=function(t,e){const i=new Set;for(const t of e){for(const e of t.entities)i.add(e);for(const e of t.controls||t.marker?.controls||[])"string"!=typeof e||e.startsWith("marker:")||i.add(e)}return[...i].sort().map(e=>`${e}:${t?.states?.[e]?.state??""}`).join("|")}(t,e),a=vn(s),r=no.get(e);if(r?.graphFingerprint===n&&r.stateFingerprint===o&&r.virtualLightFingerprint===a&&r.registry===t?.entities)return r.sources;const{visible:l,markerById:c,persistedByDevice:h,incomingByMarker:d}=ao(e),p=null==i?l:l.filter(t=>Qn(t,i)),u=new Map,_=new Map;for(const e of p){const i=co(t,e).filter(t=>"controls"!==t.via);for(const s of i){const i=!0===e.marker?.is_light?String(e.marker?.id||e.id||""):"",n=i?`marker:${i}`:`entity:${s.eid}`,o=s.eid?[s.eid]:[],a={key:n,eid:s.eid||"",stateEids:o,serviceEids:o,device:e,via:s.via,castsGlow:!0,passive:!s.eid,on:!s.eid||"on"===t.states?.[s.eid]?.state};u.set(e,[...u.get(e)||[],a]),s.eid&&_.set(s.eid,a)}}const m=new Map;for(const t of e){const e=h.get(t)||[],i=new Set(void 0===t.controls?e.filter(Ms):t.controls);m.set(t,i)}for(const e of u.values())for(const i of e){if(!i.passive)continue;const e=i.key.slice(7),n=d.get(e);i.on=!n||n.driverEids.some(e=>"on"===t.states?.[e]?.state),!n&&yn(i.device.marker)&&(i.on=bn(i.device.marker,s))}const g=new Map,f=t=>{const e=g.get(t.key);(!e||t.castsGlow&&!e.castsGlow)&&g.set(t.key,t)};for(const e of l){const s=h.get(e)||[],n=null==i||Qn(e,i);for(const i of s){if(i.startsWith("marker:")){const t=c.get(i.slice(7)),e=t?u.get(t):null;if(e)for(const t of e)f(t);continue}if(!Ms(i)||!m.get(e)?.has(i))continue;const s=_.get(i);s?f(s):n&&f({key:`entity:${i}`,eid:i,stateEids:[i],serviceEids:[i],device:e,via:"controls",castsGlow:!1,passive:!1,on:"on"===t.states?.[i]?.state})}for(const t of u.get(e)||[])f(t)}const v=[...g.values()];return no.set(e,{graphFingerprint:n,stateFingerprint:o,virtualLightFingerprint:a,registry:t?.entities,sources:v}),v}function po(t){const e=t.filter(t=>t.castsGlow);return e.find(t=>t.on)||e[0]||null}function uo(t){return t.length?t.some(t=>t.on)?"on":"off":"none"}function _o(t,e){const i=[];for(const s of e){const e=t.states[s];if(!e)continue;const n=(e.attributes?.unit_of_measurement||"").toLowerCase();if(/_(linkquality|lqi)$/.test(s)||"lqi"===n){const t=parseFloat(e.state);isNaN(t)||i.push(t);continue}const o=e.attributes?.linkquality??e.attributes?.lqi;if(null!=o){const t=parseFloat(o);isNaN(t)||i.push(t)}}return Ei(i)}function mo(t,e){for(const i of e){if(!Gn(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(10*s)/10}return null}function go(t,e){for(const i of e){if(!i.startsWith("climate."))continue;const e=t.states[i];if(!e||"unavailable"===e.state||"unknown"===e.state)continue;const s=parseFloat(e.attributes?.current_temperature);if(Number.isFinite(s))return Math.round(10*s)/10}return null}function fo(t,e){if(t.entities?.[e]?.entity_category)return!1;const i=t.states[e];if(!i)return/_humidity$/.test(e);const s=i.attributes||{};return"humidity"===s.device_class||"%"===s.unit_of_measurement&&/_humidity$/.test(e)||/_humidity$/.test(e)}function vo(t,e){for(const i of e){if(!fo(t,i))continue;const e=t.states[i];if(!e)continue;const s=parseFloat(e.state);if(!isNaN(s))return Math.round(s)}return null}function yo(t,e){if(!e)return[];const i=[];for(const[e,s]of Object.entries(t.entities)){if(!e.startsWith("light.")||s.hidden||!En(s))continue;let n=null;if("group"===s.platform)n=s.area_id||null;else{if(!s.device_id)continue;{const e=t.devices[s.device_id];if(!En(e))continue;if("Group"!==e?.model)continue;n=e.area_id||s.area_id||null}}if(!n)continue;const o=t.states[e];i.push({eid:e,name:s.name||o?.attributes?.friendly_name||e,area:n})}return i}function bo(t,e,i,s,n){const o=ee(e,i,n);if(o!==te)return o;const a=[];for(const e of s){const i=t.states[e]?.attributes?.device_class;i&&a.push(i)}return function(t){for(const e of t){const t=Qt[e];if(t)return t}return null}(a)??te}function wo(t){const e=new Set,i=new Set,s=new Set;for(const n of t||[]){const t=String(n.binding||"").indexOf(":");if(t<1)continue;const o=n.binding.slice(0,t),a=n.binding.slice(t+1);a&&(!0===n?.removed?"device"===o?e.add(a):"entity"===o&&i.add(a):"entity"===o&&s.add(a))}return{devices:e,entities:i,liveEntities:s}}function ko(t,e,i){if(i.liveEntities.has(e))return!1;if(i.entities.has(e))return!0;const s=t?.entities?.[e]?.device_id;return!!s&&i.devices.has(s)}function xo(t,e,i,s,n){const o=Xn(e.binding,e.controls,t.entities).filter(t=>!ko(i,t,s)).filter(t=>"active"===Bn(i,"entity:"+t,n).kind);t.marker=e,t.controls=o,t.userHidden=!0===e.hidden,t.hidden=t.userHidden||"ha_disabled"===t.bindingStatus?.kind,e.name&&(t.name=e.name),e.icon&&(t.icon=e.icon),null!=e.model&&(t.model=e.model),t.link=e.link??null,t.description=e.description??null,t.pdfs=e.pdfs||[],t.tapAction=e.tap_action??null}function $o(t,e,i,s){if("string"==typeof t.room_id&&t.room_id.length>0&&null===t.area)return{area:"",space:t.space||s};const n=t.area||e||"";return{area:n,space:n&&i[n]||t.space||s}}function So(t){const e=t.hass,i=t.registry||An(e),s=Nn(e,i),n=On(e,i),{areaToSpace:o,markers:a,settings:r,excluded:l,showAll:c,firstSpaceId:h,loc:d,iconRules:p}=t,u=!1!==r.group_lights,_=wo(a),m=yo(s,u).filter(t=>!ko(s,t.eid,_)),g=new Set(m.map(t=>t.area)),f=qn(s),v=function(t){const e={};for(const[i,s]of Object.entries(t.entities||{}))s?.device_id&&(e[s.device_id]=e[s.device_id]||[]).push(i);return e}(n),y=Wn(a,n),b=new Set;for(const t of a){const[e,i]=t.binding.split(":");"device"!==e&&"entity"!==e||!i||b.add(t.binding)}const w=(t,e)=>a.find(i=>i.binding===t+":"+e),k={},x=[];for(const t of Object.values(s.devices)){const n=t.area_id;if(!n||!o[n])continue;if("service"===t.entry_type)continue;if(b.has("device:"+t.id))continue;const a=Bn(e,"device:"+t.id,i);if("active"!==a.kind)continue;const h=w("device",t.id);if(h&&h.hidden&&!r.filter_seeded)continue;const _=jn(s,t.id,f[t.id]||[],y);if(_.partial&&!_.entityIds.length)continue;const m=_.entityIds,v=_.partial?{kind:"active",enabledEntityIds:m,allEntityIds:m}:a,$=Un(s,t,m),S=!r.filter_seeded;if(S&&!c){if(l.has($))continue;if("Group"===t.model)continue;if(/scene/i.test(t.model||""))continue;if(/bridge/i.test((t.model||"")+(t.name||"")))continue;if("myheat"===$&&t.via_device_id)continue}const M=(t.name_by_user||t.name||d("device.unnamed")).trim(),C=M+"|"+n;let D=bo(s,M,t.model,m,p);if(m.some(t=>t.startsWith("lock."))&&(D="mdi:lock"),S&&!c&&u&&"mdi:lightbulb"===D&&g.has(n))continue;k[C]=(k[C]||0)+1;const P=k[C]>1?M+" "+k[C]:M,T={id:t.id,name:P,model:t.model||"",area:n,space:o[n],icon:D,entities:m,allEntities:v.allEntityIds,bindingStatus:v,bindingKind:"device",bindingRef:t.id,pdfs:[]};T.primary=Jn(s,m,D),"mdi:thermometer"!==D&&"mdi:air-filter"!==D||(T.temp=mo(s,m)),T.primary&&fo(s,T.primary)&&(T.hum=vo(s,m)),x.push(T)}for(const t of m)o[t.area]&&(b.has("entity:"+t.eid)||x.push({id:"lg_"+t.eid,name:t.name,model:d("device.light_group"),area:t.area,space:o[t.area],icon:"mdi:lightbulb-group",entities:[t.eid],allEntities:[t.eid],bindingStatus:{kind:"active",enabledEntityIds:[t.eid],allEntityIds:[t.eid]},primary:t.eid,bindingKind:"entity",bindingRef:t.eid,pdfs:[]}));for(const t of a){if(t.removed)continue;const[a,l]=t.binding.split(":"),c="device"===a||"entity"===a?Bn(e,t.binding,i):null;if(!t.hidden||r.filter_seeded||"ha_disabled"===c?.kind)if("device"===a){const e=c;if("unverified"===e.kind)continue;const a=n.devices[l],{area:r,space:u}=$o(t,a?.area_id,o,h),m="active"===e.kind?e.enabledEntityIds:[];let g=a?bo(s,a.name_by_user||a.name||"",a.model,m,p):"mdi:help-circle";m.some(t=>t.startsWith("lock."))&&(g="mdi:lock");const f={id:t.id,name:a?.name_by_user||a?.name||d("device.fallback"),model:a?.model||"",area:r,space:u,icon:g,entities:m,allEntities:e.allEntityIds.length?e.allEntityIds:a&&v[a.id]||[],bindingStatus:e,bindingKind:"device",bindingRef:l};f.primary=Jn(s,m,g),"mdi:thermometer"!==g&&"mdi:air-filter"!==g||(f.temp=mo(s,m)),f.primary&&fo(s,f.primary)&&(f.hum=vo(s,m)),xo(f,t,n,_,i),x.push(f)}else if("entity"===a){if(ko(n,l,_))continue;const e=c;if("unverified"===e.kind)continue;const a=n.entities[l],r=a?.area_id||a?.device_id&&n.devices[a.device_id]?.area_id||"",{area:d,space:u}=$o(t,r,o,h),m=s.states[l],g=a?.name||m?.attributes?.friendly_name||l;let f=bo(s,g,"",[l],p);l.startsWith("lock.")&&(f="mdi:lock");const v={id:t.id,name:g,model:"",area:d,space:u,icon:f,entities:"active"===e.kind?[l]:[],allEntities:[l],bindingStatus:e,primary:"active"===e.kind?l:void 0,bindingKind:"entity",bindingRef:l};"mdi:thermometer"!==f&&"mdi:air-filter"!==f||!v.entities.length||(v.temp=mo(s,v.entities)),v.entities.length&&fo(s,l)&&(v.hum=vo(s,v.entities)),xo(v,t,n,_,i),x.push(v)}else{const e=t.area||"",s=t.space||e&&o[e]||h,a={id:t.id,name:t.name||d("device.virtual"),model:t.model||"",area:e,space:s,icon:t.icon||"mdi:map-marker",entities:[],allEntities:[],bindingStatus:{kind:"active",enabledEntityIds:[],allEntityIds:[]},bindingKind:"virtual",virtual:!0};xo(a,t,n,_,i),x.push(a)}}return x}function Mo(t,e,i,s){if(!e)return null;if(function(t,e,i){if(!e)return!1;const s=e.indexOf(":");if(s<1)return!1;const n=e.slice(0,s),o=e.slice(s+1),a=wo(i);return"device"===n?a.devices.has(o):"entity"===n&&ko(t,o,a)}(t,e,s))return null;const n=e.indexOf(":");if(n<0)return null;const o=e.slice(0,n),a=e.slice(n+1);if(!a)return null;if("entity"===o){const e=parseFloat(t.states[a]?.state);return Number.isFinite(e)?"temp"===i?Math.round(10*e)/10:Math.round(e):null}if("device"===o){const e=Object.entries(t.entities).filter(([,t])=>t.device_id===a).map(([t])=>t);return"temp"===i?mo(t,e):vo(t,e)}return null}const Co=new RegExp(["water","voda","coolant","flow_?temp","return_?temp","target","setpoint","chip","cpu","processor","board","core_temp","device_temp","batter","akkum","freezer","fridge","oven","kettle","boiler"].join("|"),"i");function Do(t,e,i){const s=[];for(const n of e){if(n.area!==i||n.virtual)continue;const e=_o(t,n.entities);null!=e&&s.push(e)}return Ei(s)}function Po(t,e,i){const s=[];for(const n of e){if(n.area!==i)continue;if("mdi:thermometer"!==n.icon&&"mdi:air-filter"!==n.icon)continue;const e=mo(t,n.entities);null!=e&&s.push(e)}return s.length?Math.round(s.reduce((t,e)=>t+e,0)/s.length*10)/10:null}const To={climate:["current_temperature","temperature","current_humidity","humidity"],water_heater:["current_temperature","temperature"],cover:["current_position"],valve:["current_position"],fan:["percentage"],humidifier:["current_humidity","humidity"],light:["brightness"],media_player:["volume_level"],vacuum:["battery_level","fan_speed"],lawn_mower:["battery_level","fan_speed"]};function Ro(t){return t?t.sourceLabel?`${t.sourceLabel}: ${t.fullText}`:t.fullText:""}const Io=new WeakMap;function Ao(t){return t?"entity_state"===t.kind?`state:${t.entity_id}`:"entity_attribute"===t.kind?`attr:${t.entity_id}:${t.attribute}`:"derived_marker_state"===t.kind?`marker:${t.ref}`:"derived:lqi":""}function zo(t){if("derived:lqi"===t)return{kind:"derived_lqi"};if(t.startsWith("state:"))return{kind:"entity_state",entity_id:t.slice(6)};if(t.startsWith("attr:")){const e=t.slice(5),i=e.lastIndexOf(":");if(i>0)return{kind:"entity_attribute",entity_id:e.slice(0,i),attribute:e.slice(i+1)}}return t.startsWith("marker:marker:")?{kind:"derived_marker_state",ref:t.slice(7)}:null}function Fo(t,e){const i=t?.entities?.[e],s=t?.states?.[e];return String(i?.name||i?.original_name||s?.attributes?.friendly_name||e)}function Eo(t){return t.replaceAll("_"," ")}function No(t,e=1){const i=Number(t);return Number.isFinite(i)?`${Math.round(i*e)} %`:null}function Oo(t){if("derived_lqi"===t.kind)return"lqi";if("entity_attribute"===t.kind){if(t.attribute.includes("temperature"))return"temperature";if(t.attribute.includes("humidity"))return"humidity"}return"default"}function Ho(t,e,i,s=[]){let n=null,o="",a="available";if("entity_state"===i.kind){o=Fo(t,i.entity_id);const e=t?.states?.[i.entity_id];if(e)if(Bo(t,i.entity_id))if(["unknown","unavailable"].includes(String(e.state).toLowerCase()))a="unavailable";else{const s=Xi(t,i.entity_id);n=s?Zi(s,String(e.attributes?.unit_of_measurement||"")):null,n||(a="unavailable")}else a="unavailable";else a="missing"}else if("entity_attribute"===i.kind){o=`${Eo(i.attribute)} · ${Fo(t,i.entity_id)}`;const e=t?.states?.[i.entity_id];e?Bo(t,i.entity_id)?(n=function(t,e,i){const s=t?.states?.[e];if(!s||!(i in(s.attributes||{})))return null;const n=Xi(t,e,i);if(n?.formatted)return n.text;const o=s.attributes?.[i];if("brightness"===i)return No(o,100/255);if("volume_level"===i)return No(o,100);if(["current_position","percentage","current_humidity","humidity","battery_level"].includes(i))return No(o);if("current_temperature"===i||"temperature"===i){const e=Number(o);if(!Number.isFinite(e))return null;const i=String(t?.config?.unit_system?.temperature||"°C");return`${Math.round(10*e)/10} ${i}`}return n?.text||(["string","number","boolean"].includes(typeof o)?String(o):null)}(t,i.entity_id,i.attribute),null==n&&(a="unavailable")):a="unavailable":a="missing"}else if("derived_lqi"===i.kind){o="LQI";const i=_o(t,e.entities);null==i?a="unavailable":n=String(i)}else{o="Light state";const e=s.find(t=>t.ref===i.ref);if(e){const i=e.on?"on":"off",s=t?.localize?.(`state.default.${i}`);n="string"==typeof s&&s?s:i,o=e.name||o}else a="missing"}const r=t?.localize?.("state.default.unavailable")||"Unavailable";return{source:i,sourceLabel:o,text:n??"—",fullText:n??r,availability:a,isLqi:"derived_lqi"===i.kind,tone:Oo(i)}}function Lo(t,e){if(!0===e.marker?.use_climate_temp){const i=e.entities.find(e=>e.startsWith("climate.")&&Number.isFinite(Number(t?.states?.[e]?.attributes?.current_temperature)));return i?{kind:"entity_attribute",entity_id:i,attribute:"current_temperature"}:null}if("mdi:thermometer"===e.icon||"mdi:air-filter"===e.icon){const i=e.entities.find(e=>Gn(t,e)&&Number.isFinite(Number(t?.states?.[e]?.state)));if(i)return{kind:"entity_state",entity_id:i}}return e.primary&&fo(t,e.primary)&&Number.isFinite(Number(t?.states?.[e.primary]?.state))?{kind:"entity_state",entity_id:e.primary}:null}function Bo(t,e){const i=t?.entities?.[e];if(i&&!En(i))return!1;const s=i?.device_id?t?.devices?.[i.device_id]:null;return!s||En(s)}function qo(t,e){return Bo(t,e)&&!!t?.states?.[e]}function Wo(t,e,i=[e]){const s=function(t,e){const i=t=>[t.id,t.primary,t.hidden?1:0,t.userHidden?1:0,t.entities.join(","),(t.controls||[]).join(","),t.marker?.binding||"",!0===t.marker?.is_light?1:0,t.marker?.light_entity||"",(t.marker?.controls||[]).join(",")].join("|");return`${i(t)}\n${e.map(i).join("\n")}`}(e,i),n=Io.get(e);if(n&&n.states===(t?.states||null)&&n.entities===(t?.entities||null)&&n.devices===(t?.devices||null)&&n.signature===s)return n.result;const o=new Set(e.entities);for(const t of e.controls||[])t.startsWith("marker:")||o.add(t);const a=[],r=ho(t,i),l=r.filter(t=>t.key.startsWith("marker:")).map(t=>({ref:t.key,on:t.on,name:t.device?.name||t.key})),c=i=>{const s=Ao(i);if(a.some(t=>t.key===s))return;const n=Ho(t,e,i,l),o="entity_attribute"===i.kind?`${i.entity_id} · ${i.attribute}`:"entity_state"===i.kind?i.entity_id:"derived_marker_state"===i.kind?i.ref:"LQI";a.push({key:s,source:i,label:n.sourceLabel,technical:o,value:n.text,available:"available"===n.availability})};for(const e of[...o].sort()){if(!qo(t,e))continue;const i=e.split(".")[0],s=t?.entities?.[e];if("button"===i||"event"===i||"config"===s?.entity_category)continue;c({kind:"entity_state",entity_id:e});const n=t?.states?.[e];for(const t of To[i]||[])t in(n?.attributes||{})&&c({kind:"entity_attribute",entity_id:e,attribute:t})}const h=new Set((e.marker?.controls||e.controls||[]).filter(t=>t.startsWith("marker:")));!0===e.marker?.is_light&&e.marker.id&&h.add(`marker:${e.marker.id}`);const d=new Set(r.map(t=>t.key));for(const t of[...h].sort())d.has(t)&&c({kind:"derived_marker_state",ref:t});return e.virtual||null==_o(t,e.entities)||c({kind:"derived_lqi"}),Io.set(e,{states:t?.states||null,entities:t?.entities||null,devices:t?.devices||null,signature:s,result:a}),a}function jo(t,e,i){const s=Lo(t,e);if(s&&i.some(t=>t.key===Ao(s)))return s;const n=e.primary&&i.find(t=>t.key===`state:${e.primary}`);if(n)return n.source;const o=[t=>t.technical.includes("temperature"),t=>t.technical.includes("humidity"),t=>t.technical.includes("battery")];for(const t of o){const e=i.find(t);if(e)return e.source}return i.find(t=>"derived_lqi"!==t.source.kind)?.source||i[0]?.source||null}function Uo(t){return"boolean"==typeof t?.themes?.darkMode?t.themes.darkMode?"theme-dark":"theme-light":""}function Go(t){const e=[];if(1!==t.scale&&e.push(`--dev-scale:${t.scale}`),"none"!==t.pulse.kind){e.push(`--ripple-scale:${t.pulse.diameterScale}`);const n=(i=t.pulse.color,s=null,"string"==typeof i&&(Tt.test(i)||At.test(i))?i:s);n&&e.push(`--ripple-color:${n}`)}var i,s;return e}function Vo(t){const e=[...String(t)].reduce((t,e)=>/\s/.test(e)?t+.35:t+(e.codePointAt(0)>255?1:.62),0);return e<=8?.45:Math.max(.25,Math.round(3.6/e*1e3)/1e3)}function Ko(t,e){const i=t.pulse,s=i.generation%2==0,n=function(t){const e=t.valueBadge;if(!e||!1!==e.configured)return[];const i=[];return null!=t.tempText&&"temperature"!==e.tone&&i.push({kind:"temperature",text:t.tempText,suffix:"°"}),null!=t.humText&&"humidity"!==e.tone&&i.push({kind:"humidity",text:t.humText,suffix:"%"}),i}(t),o=t.valueBadge,a=o?.position||"right",r=!!o||n.length>0,l=["device-shell",null!=t.valueText?"text-shell":"",r?`with-values pos-${a}`:"",n.length?"with-legacy":""].filter(Boolean).join(" ");return W`
${"none"!==i.kind&&"dot"!==i.reducedMotionIndicator?W``:G}
diff --git a/src/styles.ts b/src/styles.ts
index b93f0481..32231f59 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -2,8 +2,9 @@
import { css } from 'lit';
import type { CSSResultGroup } from 'lit';
import { chromeStyles } from './styles/chrome.styles';
+import { dialogsStyles } from './styles/dialogs.styles';
-export { chromeStyles };
+export { chromeStyles, dialogsStyles };
const inlineStyles = css`
:host {
@@ -76,73 +77,15 @@ const inlineStyles = css`
@media (prefers-reduced-motion: reduce) {
.fixedfloor-loading { animation: none; }
}
- .head {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px var(--sp-5);
- border-bottom: 1px solid var(--hp-line);
- flex-wrap: wrap;
- }
- .title {
- font-size: var(--fs-l);
- font-weight: 600;
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- white-space: nowrap;
- }
- .title ha-icon {
- color: var(--hp-accent);
- --mdc-icon-size: 18px;
- }
- @media (max-width: 620px) {
- .head { gap: var(--sp-3); padding: var(--sp-4) 10px; }
- .head .count { display: none; }
- .head .title { font-size: var(--fs-m); }
- }
:host([data-pointer-hover]) .tab:hover {
color: var(--hp-txt);
}
- .count {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- }
.spacer {
flex: 1;
}
- .btn {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- background: transparent;
- color: var(--hp-txt);
- padding: var(--sp-3) 10px;
- border-radius: var(--rad-m);
- cursor: pointer;
- transition: 0.15s;
- font-family: inherit;
- font-size: var(--fs-m);
- }
- .btn ha-icon {
- --mdc-icon-size: 17px;
- }
:host([data-pointer-hover]) .btn:hover {
border-color: var(--hp-accent);
}
- .btn.on {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- border-color: var(--hp-accent);
- }
- .btn.ghost {
- border: none;
- }
- .btn[disabled] {
- opacity: 0.5;
- pointer-events: none;
- }
.stage.noplan {
background: #ffffff;
}
@@ -234,20 +177,6 @@ const inlineStyles = css`
.sunlayer {
pointer-events: none;
}
- /* the compass dial in the general settings (docs/SUN.md) */
- .sunrow {
- display: flex;
- align-items: center;
- gap: var(--sp-5);
- margin: var(--sp-3) 0;
- }
- .suncol {
- flex: 1;
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- min-width: 0;
- }
.compass {
width: 120px;
height: 120px;
@@ -326,43 +255,6 @@ const inlineStyles = css`
opacity: 0.85;
animation: hp-boot-pulse 1.3s ease-in-out infinite;
}
- .recoveryoverlay {
- position: absolute;
- inset: 0;
- /* Above the contextual editor tray (70): recovery is the one state in
- which every stage-editing surface must be inert. Dialogs live outside
- the stage and retain their own higher card-level stacking context. */
- z-index: 75;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: var(--sp-4);
- padding: var(--sp-6);
- box-sizing: border-box;
- /* The final solid layer guarantees an opaque recovery surface even when
- a custom HA theme exposes its card colour as rgba(). */
- background:
- linear-gradient(var(--ha-card-background, var(--card-background-color, #111)),
- var(--ha-card-background, var(--card-background-color, #111))),
- #111;
- color: var(--primary-text-color, #fff);
- text-align: center;
- pointer-events: auto;
- transition: opacity 0.15s ease;
- }
- .recoveryoverlay.phase-entering,
- .recoveryoverlay.phase-leaving {
- opacity: 0;
- }
- .recoveryoverlay.phase-fading-in,
- .recoveryoverlay.phase-opaque {
- opacity: 1;
- }
- .recoveryoverlay ha-icon {
- --mdc-icon-size: 44px;
- color: var(--hp-accent);
- }
@keyframes hp-boot-pulse {
0%, 100% { opacity: 0.3; transform: scale(0.94); }
50% { opacity: 0.9; transform: scale(1); }
@@ -700,118 +592,10 @@ const inlineStyles = css`
pointer-events: none;
cursor: default;
}
- .oplock {
- --oplock-size: calc(var(--icon-size, 2.5cqw) * 0.62);
- --oplock-core-size: calc(var(--oplock-size) / 1.26875);
- --oplock-stroke-ratio: 0.01875;
- --oplock-stroke-width: max(1px, calc(var(--oplock-core-size) * var(--oplock-stroke-ratio)));
- --oplock-core-bg: light-dark(#fff, #252525);
- --oplock-core-fg: light-dark(#252525, #fff);
- --oplock-shell-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
- --oplock-shell-shadow:
- 0 calc(var(--oplock-core-size) * .025) calc(var(--oplock-core-size) * .05) rgb(37 40 45 / 12%),
- 0 calc(var(--oplock-core-size) * .1) calc(var(--oplock-core-size) * .175)
- calc(var(--oplock-core-size) * -.025) rgb(37 40 45 / 18%);
- --oplock-core-shadow: 0 0 0 0 transparent;
- pointer-events: none; /* inert while editing; clickable in View (rule below) */
- position: absolute;
- transform: translate(-50%, -50%);
- width: var(--oplock-size);
- height: var(--oplock-size);
- display: grid;
- place-items: center;
- border: 0;
- background: transparent;
- z-index: 1;
- }
- .oplock.theme-light {
- --oplock-core-bg: #fff;
- --oplock-core-fg: #252525;
- --oplock-shell-stroke: #BCBCBC;
- }
- .oplock.theme-dark {
- --oplock-core-bg: #252525;
- --oplock-core-fg: #fff;
- --oplock-shell-stroke: rgb(37 37 37 / 75%);
- --oplock-core-shadow:
- inset 0 calc(var(--oplock-core-size) * .0125)
- calc(var(--oplock-core-size) * .0125) rgb(255 255 255 / 70%);
- }
.stage.mode-view .oplock {
pointer-events: auto;
cursor: pointer;
}
- .oplock-shell {
- box-sizing: border-box;
- width: 100%;
- height: 100%;
- border: var(--oplock-stroke-width) solid var(--oplock-shell-stroke);
- border-radius: 50%;
- background: transparent;
- box-shadow: var(--oplock-shell-shadow);
- display: grid;
- place-items: center;
- pointer-events: none;
- backdrop-filter: none;
- }
- .oplock-core {
- width: var(--oplock-core-size);
- height: var(--oplock-core-size);
- border-radius: 50%;
- background: var(--oplock-core-bg);
- color: var(--oplock-core-fg);
- box-shadow: var(--oplock-core-shadow);
- display: grid;
- place-items: center;
- pointer-events: none;
- }
- .oplock ha-icon {
- --mdc-icon-size: calc(var(--oplock-core-size) * 0.55);
- display: flex;
- line-height: 0;
- }
- .oplock.locked {
- --oplock-core-bg: #66D17A;
- --oplock-core-fg: light-dark(#fff, #252525);
- --oplock-shell-stroke: #66D17A;
- }
- .oplock.theme-light.locked { --oplock-core-fg: #fff; }
- .oplock.theme-dark.locked {
- --oplock-core-fg: #252525;
- --oplock-stroke-ratio: .025;
- }
- .oplock.unlocked {
- --oplock-core-bg: #F0410C;
- --oplock-core-fg: light-dark(#fff, #252525);
- --oplock-shell-stroke: #F0410C;
- }
- .oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
- .oplock.theme-dark.unlocked {
- --oplock-core-fg: #252525;
- --oplock-stroke-ratio: .025;
- }
- .oplock.unknown { --oplock-core-fg: var(--hp-muted); }
- .btn.lockact {
- width: 100%;
- justify-content: center;
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- margin-top: var(--sp-4);
- }
- .btn.lockact.warn {
- color: var(--error-color, #d33);
- border-color: var(--error-color, #d33);
- }
- .oprow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) 0;
- }
- .oprow b { margin-left: auto; }
- .oprow.ok b { color: #66d17a; }
- .oprow.warn b { color: var(--hp-open); }
@media (prefers-reduced-motion: reduce) {
.op-leaf, .op-arc { transition: none; }
}
@@ -1008,64 +792,9 @@ const inlineStyles = css`
display: inline-flex;
}
.roomlabel .rlm.lit { opacity: 1; }
- .bindharow {
- display: flex;
- align-items: center;
- gap: var(--sp-5);
- flex-wrap: wrap;
- }
- .bindharow .entcheck { opacity: 0.9; }
- .ctrlchips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; }
- .ctrlchip {
- display: inline-flex; align-items: center; gap: var(--sp-2);
- background: var(--hp-accent); color: var(--text-primary-color, #fff);
- border-radius: var(--rad-l); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-s);
- }
- .ctrlchip ha-icon { --mdc-icon-size: 14px; cursor: pointer; }
- .ctrllist { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); }
- .ctrlopt {
- display: flex; align-items: center; gap: var(--sp-4); text-align: left;
- border: 0; background: transparent; color: var(--hp-txt);
- padding: var(--sp-3) var(--sp-4); border-radius: var(--rad-s); cursor: pointer; font-family: inherit; font-size: var(--fs-m);
- }
:host([data-pointer-hover]) .ctrlopt:hover {
background: var(--secondary-background-color, rgba(128,128,128,0.15));
}
- .ctrlopt .sub { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
- .ctrlopt ha-icon { --mdc-icon-size: 16px; }
- .ctrlstates { display: flex; flex-direction: column; gap: var(--sp-2); }
- .ctrlstate { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--hp-muted); }
- .ctrlstate.on { color: var(--hp-txt); }
- .ctrlstate ha-icon { --mdc-icon-size: 15px; }
- .cardpreview {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: var(--sp-2);
- margin: var(--sp-4) 0 var(--sp-1);
- padding: 10px;
- border: 1px dashed var(--hp-muted);
- border-radius: var(--rad-m);
- }
- .cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
- .cardpreview .cpmeta {
- display: inline-flex;
- align-items: center;
- gap: 0.3em;
- font-weight: 600;
- opacity: 0.85;
- }
- .cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
- .iconauto {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- font-size: var(--fs-s);
- margin: var(--sp-2) 0 0;
- }
- .iconauto ha-icon { --mdc-icon-size: 18px; }
- .iconauto span { flex: 1; }
- .iconauto .btn { min-height: 32px; padding: 0 var(--sp-3); }
.rlhandle {
display: none;
position: absolute;
@@ -1349,12 +1078,6 @@ const inlineStyles = css`
.stage .openwalls {
opacity: var(--hp-mode-architecture-opacity, 1);
}
- hp-dialog .dfill {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-3);
- cursor: pointer;
- }
.opening-preview {
opacity: 0.5;
pointer-events: none;
@@ -1438,37 +1161,10 @@ const inlineStyles = css`
stroke-linecap: round;
pointer-events: none;
}
- .wallthick-dlg {
- position: absolute;
- z-index: 40;
- min-width: 200px;
- transform: translate(-50%, 8px);
- padding: 10px 12px;
- border-radius: 10px;
- background: var(--card-background-color, #fff);
- box-shadow: 0 8px 28px rgba(0,0,0,.22);
- display: flex;
- flex-direction: column;
- gap: 8px;
- pointer-events: auto;
- }
.wallthick-hover.isopen {
fill: var(--error-color, #f44336);
stroke: var(--error-color, #f44336);
}
- .wallthick-dlg .row {
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .wallthick-dlg input[type="number"] {
- width: 5.5em;
- padding: 4px 6px;
- border: 1px solid var(--divider-color, #ccc);
- border-radius: 6px;
- background: var(--input-fill, transparent);
- color: var(--primary-text-color);
- }
/* Boundary is neutral off-target, then advertises its resolved action. */
.stage.markup.tool-boundary { cursor: default; }
.stage.markup.tool-boundary.boundary-solid { cursor: crosshair; }
@@ -1759,19 +1455,6 @@ const inlineStyles = css`
forced-color-adjust: auto;
}
}
- .areasel,
- .namein {
- background: var(--hp-bg);
- border: 1px solid var(--hp-line);
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- font-size: var(--fs-m);
- font-family: inherit;
- }
- .namein {
- width: 130px;
- }
.dev.valonly {
/* Saved coordinates remain the centre of a Text shell. The shell may
expand, while the invisible anchor stays one core diameter wide. */
@@ -2176,24 +1859,6 @@ const inlineStyles = css`
border: 1px solid currentColor;
}
.dev .habadge ha-icon { width: 72%; height: 72%; }
- .habindingbanner {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- padding: var(--sp-3) var(--sp-4);
- margin-bottom: var(--sp-4);
- border: 1px solid var(--warning-color, #ff9800);
- border-radius: var(--rad-m);
- background: color-mix(in srgb, var(--warning-color, #ff9800) 12%, transparent);
- color: var(--primary-text-color, #f1f3f6);
- }
- .habindingbanner > span { flex: 1; min-width: 0; }
- .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
- .habindingbanner.limited {
- border-color: var(--secondary-text-color, #9aa0aa);
- background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
- }
- .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
.dev.sel {
--device-ring-color: #F0A00C;
--device-ring-width: calc(var(--dev-size) * 0.0375);
@@ -2261,39 +1926,6 @@ const inlineStyles = css`
:host([data-pointer-hover]) .tab:hover .tabedit {
opacity: 0.9;
}
- .srcrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- font-size: var(--fs-m);
- cursor: pointer;
- padding: var(--sp-1) 0;
- }
- .dispsection {
- margin-top: var(--sp-5) !important;
- padding-top: var(--sp-4);
- border-top: 1px solid var(--hp-line);
- font-weight: 600;
- color: var(--hp-txt) !important;
- }
- .colorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .gsrow > hp-color-opacity {
- min-width: min(100%, 210px);
- justify-content: space-between;
- }
- .ripple-colorrow > hp-color-opacity {
- width: 100%;
- justify-content: space-between;
- }
- .ripple-sizerow > .opl {
- min-width: 0;
- }
- .colorrow input[type='range'] { flex: 1; }
- .colorrow .tempin { width: 70px; flex: none; }
.temprange {
display: inline-flex;
align-items: center;
@@ -2302,200 +1934,6 @@ const inlineStyles = css`
color: var(--hp-muted);
font-size: var(--fs-s);
}
- /* beat the generic hp-dialog .body .namein { width:100% } rule */
- hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
- hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
- .srcrow { flex-wrap: nowrap; }
- /* native HA controls (rendered only when the HA frontend defines them;
- old HA and the smoke env keep the plain inputs). ha-switch is taller
- than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
- .srcrow ha-switch { flex: none; }
- .colorrow ha-slider { flex: 1; min-width: 0; }
- .srcrow > span:first-of-type { white-space: nowrap; }
- .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
- .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
- .markerlightgroup {
- min-width: 0;
- margin: var(--sp-5) 0 0;
- padding: var(--sp-4);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- }
- .markerlightgroup legend {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- padding: 0 var(--sp-2);
- color: var(--hp-txt);
- font-weight: 600;
- }
- .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
- .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
- .markerhelpfield { margin-top: var(--sp-4); }
- .markerhelplabel {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: var(--sp-1);
- margin-bottom: var(--sp-1);
- }
- .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
- .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
- .markerlightgroup .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
- .markerglowvalue hp-color-opacity { flex: none; }
- .markerlightdisabled {
- display: flex;
- align-items: center;
- gap: var(--sp-2);
- margin-top: var(--sp-2) !important;
- }
- .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
- .markerbadgetechnical {
- min-width: 0;
- margin: var(--sp-1) 0 var(--sp-2) !important;
- overflow-wrap: anywhere;
- }
- .markerbadgetechnical code { white-space: normal; }
- .planrow {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- /* the "already uploaded" picker: a plan is never deleted for being
- unreferenced, so it has to be findable again */
- .savedplans {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- max-height: 240px;
- overflow: auto;
- margin: var(--sp-3) 0 var(--sp-1);
- padding: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
- /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
- flex item whose automatic minimum size is ZERO (overflow != visible),
- so inside hp-dialog .body — a flex column taller than its 66vh cap —
- it shrank to a 14px sliver: the rows were in the DOM, the owner saw
- a thin rounded stripe under the "Already uploaded" button. Don't
- shrink, and keep a floor even when the box is empty or loading. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .savedplan {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
- .savedplan img {
- width: 56px;
- height: 40px;
- object-fit: contain;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- flex: none;
- }
- .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
- .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
- .savedmeta .muted { font-size: var(--fs-s); }
- .savedplan .btn.danger ha-icon { color: #f25a4a; }
- .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
- .planprev {
- max-width: 120px;
- max-height: 70px;
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- background: #fff;
- }
- .planname {
- font-size: var(--fs-m);
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .planname.muted {
- color: var(--hp-muted);
- }
- .filebtn {
- cursor: pointer;
- }
- .fileupload { display: inline-flex; min-width: 0; }
- .fileupload > input { display: none; }
- .btn.danger {
- border-color: #b3402a;
- color: #ff7a5c;
- }
- hp-dialog .row .spacer {
- flex: 1;
- }
- hp-dialog .body {
- max-height: 66vh;
- overflow-y: auto;
- }
- .descin {
- width: 100%;
- box-sizing: border-box;
- background: var(--hp-bg);
- border: 1px solid var(--hp-line);
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- font-size: var(--fs-m);
- font-family: inherit;
- resize: vertical;
- /* flex column of the dialog body squeezes textareas — keep a usable height */
- min-height: 92px;
- flex-shrink: 0;
- line-height: 1.35;
- }
- .bindsel {
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-m);
- padding: var(--sp-4);
- }
- .bindsel .opt {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- border: 1px solid var(--hp-line);
- background: transparent;
- color: var(--hp-txt);
- border-radius: var(--rad-s);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- font-size: var(--fs-m);
- font-family: inherit;
- }
- .bindsel .opt.on {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- border-color: var(--hp-accent);
- }
- .curbind {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- font-size: var(--fs-m);
- color: var(--hp-txt);
- flex-wrap: wrap;
- }
- .curbind .ref {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
/* live vacuum: a round puck, no badge plate, soft pulse (docs/VACUUM.md) */
.vacpuck {
position: absolute;
@@ -2576,59 +2014,6 @@ const inlineStyles = css`
stroke: rgba(255, 255, 255, 0.82);
stroke-width: 0.9;
}
- .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
- .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
- .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
- .vacdiag > div > span { color: var(--secondary-text-color); }
- .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
- .vacpicker { margin: var(--sp-3) 0; }
- .vacsource-warning { display: grid; gap: 8px; }
- .vacsource-warning .btn { justify-self: start; }
- .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
- .vacpicker > summary::-webkit-details-marker { display: none; }
- .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
- .vacsource-list details { padding: 6px 0 0; }
- .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
- .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
- width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
- border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
- text-align: left; cursor: pointer; }
- .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
- .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
- .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
- .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
- .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
- .vacfit {
- position: absolute;
- inset: 0;
- width: 100%;
- height: 100%;
- z-index: 12;
- overflow: visible;
- touch-action: none;
- cursor: grab;
- /* the devlayer is pointer-events: none and every child opts back in —
- without this line real clicks flew straight through the overlay
- (owner: «уголки не кликабельны»; synthetic smoke events bypass
- hit-testing, which is why they lied) */
- pointer-events: auto;
- }
- .vacfit:active { cursor: grabbing; }
- .vacfit polygon {
- fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- stroke-dasharray: 6 4;
- }
- .vacfit text {
- fill: var(--hp-accent);
- font-size: 26px;
- text-anchor: middle;
- dominant-baseline: middle;
- pointer-events: none;
- user-select: none;
- }
/* room resize tool (docs/RESIZE.md) */
/* wall handle: invisible finger-sized hit circle (HP-1550-04 hit priority
kept), the visible glyph lives in the sibling .rszicon */
@@ -2700,290 +2085,13 @@ const inlineStyles = css`
transform: translate(-50%, -50%);
border: 1px solid var(--hp-accent);
}
- /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
- .furnpalette {
- display: flex;
- flex-direction: column;
- max-height: 38vh;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- background: var(--card-background-color, var(--hp-bg));
- font-size: 0.85em;
- }
- .furnhd {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 8px;
- font-weight: 600;
- opacity: 0.9;
- }
- .furnhd .spacer { flex: 1; }
- .furnbody {
- overflow: auto;
- padding: 0 8px 6px;
- }
- .furngroup {
- margin: 6px 0 3px;
- font-size: 0.85em;
- text-transform: uppercase;
- letter-spacing: 0.04em;
- opacity: 0.6;
- }
- .furnrow {
- display: flex;
- flex-wrap: wrap;
- gap: 4px;
- }
- .furnitem {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 2px;
- width: 74px;
- padding: 4px 2px;
- border: 1px solid transparent;
- border-radius: 8px;
- background: rgba(127, 127, 127, 0.08);
- color: inherit;
- font: inherit;
- font-size: 0.8em;
- line-height: 1.15;
- text-align: center;
- cursor: pointer;
- }
:host([data-pointer-hover]) .furnitem:hover { background: rgba(127, 127, 127, 0.18); }
- .furnitem.on {
- border-color: var(--hp-accent);
- background: rgba(38, 166, 154, 0.18);
- }
- .furnprev {
- width: 40px;
- height: 40px;
- color: var(--primary-text-color, currentColor);
- }
- .furnsize {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- gap: 6px;
- padding: 6px 8px;
- border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
- }
- .furnsize label {
- display: flex;
- align-items: baseline;
- gap: 3px;
- opacity: 0.8;
- }
- .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
- .furnsize input {
- width: 5.5em;
- padding: 3px 5px;
- }
- .furnhint { opacity: 0.6; }
- .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
- /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
- .vacfithandle {
- fill: transparent;
- stroke: none;
- cursor: nwse-resize;
- }
- .vacfitknob {
- fill: var(--hp-bg);
- stroke: var(--hp-accent);
- stroke-width: 2;
- vector-effect: non-scaling-stroke;
- pointer-events: none;
- }
- .vaccalbar {
- position: fixed;
- left: 50%;
- bottom: 24px;
- transform: translateX(-50%);
- display: flex;
- gap: var(--sp-5);
- align-items: center;
- background: var(--hp-bg);
- color: var(--hp-txt);
- border: 1px solid var(--hp-accent);
- border-radius: var(--rad-l);
- padding: 10px var(--sp-5);
- z-index: 60;
- box-shadow: var(--shadow-2);
- }
- .candlist {
- max-height: 160px;
- overflow-y: auto;
- border-top: 1px solid var(--hp-line);
- /* A scrollable box is a flex item that HAPPILY collapses: inside the
- dialog body (a flex column) this list rendered its rows into a 1px
- sliver — the DOM had 26 candidates and the user saw nothing. In the
- binding dropdown it sits inside .droppanel (block context) and never
- showed the bug. Field report, 2026-07-30. */
- flex: 0 0 auto;
- min-height: 2.6em;
- }
- .cand {
- display: flex;
- justify-content: space-between;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- cursor: pointer;
- border-radius: var(--rad-s);
- font-size: var(--fs-m);
- }
:host([data-pointer-hover]) .cand:hover {
background: rgba(127, 127, 127, 0.15);
}
- .cand.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .cand .cs {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- white-space: nowrap;
- }
- .opening-entity-candidate {
- width: 100%;
- border: 0;
- background: transparent;
- color: inherit;
- font: inherit;
- text-align: left;
- }
- .opening-entity-candidate.sel {
- background: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- }
- .opening-entity-empty { cursor: default; }
- .cand.sel .cs {
- color: var(--text-primary-color, #fff);
- opacity: 0.85;
- }
- .cand.muted {
- color: var(--hp-muted);
- cursor: default;
- }
- .pdfedit {
- display: flex;
- flex-wrap: wrap;
- gap: var(--sp-3);
- align-items: center;
- }
- .pdftag {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .pdftag a {
- color: var(--hp-txt);
- text-decoration: none;
- max-width: 150px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .pdftag .x {
- --mdc-icon-size: 15px;
- cursor: pointer;
- color: var(--hp-muted);
- }
:host([data-pointer-hover]) .pdftag .x:hover {
color: #ff7a5c;
}
- .entlist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- margin-bottom: 10px;
- }
- .entrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-4);
- border-radius: var(--rad-m);
- background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
- }
- .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
- .entrow.on ha-icon { color: var(--hp-accent); }
- .entrow .en { flex: 1; font-size: var(--fs-m); }
- .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
- .entbtn {
- min-width: 74px;
- min-height: 32px;
- padding: var(--sp-2) var(--sp-5);
- border: 1px solid var(--hp-muted);
- border-radius: 999px;
- background: transparent;
- color: var(--hp-txt);
- font: inherit;
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .entbtn.on {
- background: var(--hp-accent);
- border-color: var(--hp-accent);
- color: var(--text-primary-color, #fff);
- font-weight: 600;
- }
- .inforow {
- display: flex;
- gap: 10px;
- font-size: var(--fs-m);
- margin: var(--sp-2) 0;
- }
- .inforow .k {
- color: var(--hp-muted);
- min-width: 84px;
- }
- .inforow a {
- color: var(--hp-accent);
- word-break: break-all;
- }
- .infodesc {
- font-size: var(--fs-m);
- white-space: pre-wrap;
- margin-top: var(--sp-3);
- }
- .infodesc.muted {
- color: var(--hp-muted);
- }
- .pdflist {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- }
- .pdf {
- display: inline-flex;
- align-items: center;
- gap: var(--sp-2);
- color: var(--hp-accent);
- text-decoration: none;
- }
- ha-icon-picker {
- display: block;
- }
- .floorrow {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- padding: var(--sp-3) var(--sp-2);
- font-size: var(--fs-m);
- cursor: pointer;
- }
- .floorrow .floorlvl {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: 0 var(--sp-3);
- }
.sr-only {
position: absolute !important;
width: 1px !important;
@@ -2995,214 +2103,10 @@ const inlineStyles = css`
white-space: nowrap !important;
border: 0 !important;
}
- .rtest {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- margin-bottom: var(--sp-4);
- }
- .rtest .namein { flex: 1; }
- .rtest ha-icon { color: var(--hp-accent); }
- .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
- .rrow {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- margin: var(--sp-1) 0;
- }
- .rrow .rpat { flex: 2; }
- .rrow .ricon { flex: 1.4; }
- .rrow .rpat.bad { border-color: #ff7a5c; }
- .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
- .rrow .ract {
- --mdc-icon-size: 16px;
- color: var(--hp-muted);
- cursor: pointer;
- }
:host([data-pointer-hover]) .rrow .ract:hover { color: var(--hp-txt); }
:host([data-pointer-hover]) .rrow .ract.del:hover { color: #ff7a5c; }
- .gsrow .gsl {
- min-width: 150px;
- font-size: var(--fs-m);
- color: var(--hp-muted);
- }
.alignmsg { margin: 0 0 8px; font-size: 13px; line-height: 1.45; }
- .optimize-live {
- display: grid;
- justify-items: start;
- gap: var(--sp-2);
- margin-bottom: var(--sp-3);
- }
- .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
- .optimize-cleanup { min-height: 44px; }
- .optimize-selected { color: var(--hp-txt); }
- .optimize-details {
- margin-top: var(--sp-3);
- color: var(--hp-muted);
- font-size: var(--fs-s);
- overflow-wrap: anywhere;
- }
- .optimize-details > summary {
- width: fit-content;
- color: var(--hp-txt);
- cursor: pointer;
- font-weight: 600;
- }
- .optimize-details > summary:focus-visible {
- outline: 2px solid var(--hp-accent);
- outline-offset: 3px;
- border-radius: var(--rad-s);
- }
- .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
- .optimize-details li + li { margin-top: var(--sp-1); }
- .btn.alignall { width: 100%; justify-content: center; }
- .backupactions {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-3);
- }
- .backupactions .btn { justify-content: center; min-width: 0; }
- .backupupload { display: inline-flex; min-width: 0; }
- .backupupload > .btn { width: 100%; justify-content: center; }
- .backupupload input { display: none; }
- .backupbody { min-width: 0; }
- .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
- .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
- .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
- .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
- .backupfile, .backupsummary, .backupcontent {
- display: flex;
- flex-direction: column;
- min-width: 0;
- gap: var(--sp-1);
- }
- .backupfile b, .backupcontent span {
- overflow-wrap: anywhere;
- }
- .backupfile span, .backupsummary span, .backupcontent span {
- color: var(--hp-muted);
- font-size: var(--fs-s);
- }
- .backupcounts {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: var(--sp-2) var(--sp-4);
- font-size: var(--fs-s);
- }
- .backupwarn, .backuperror {
- border-radius: var(--rad-s);
- padding: var(--sp-3);
- font-size: var(--fs-s);
- line-height: 1.45;
- overflow-wrap: anywhere;
- }
- .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
- .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
- .backupdetails {
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- padding: var(--sp-2) var(--sp-3);
- font-size: var(--fs-s);
- }
- .backupdetails summary { cursor: pointer; font-weight: 700; }
- .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
- .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
- .backupchoices {
- display: flex;
- flex-direction: column;
- gap: var(--sp-2);
- border: 1px solid var(--hp-line);
- border-radius: var(--rad-s);
- min-width: 0;
- }
- .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
- .backupconfirm { align-items: flex-start !important; }
- .backupconfirm > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- }
- @media (max-width: 520px) {
- .backupactions, .backupcounts { grid-template-columns: 1fr; }
- }
- .aboutver {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin: var(--sp-2) 0 var(--sp-3);
- }
- .aboutlink {
- display: flex;
- align-items: center;
- gap: var(--sp-3);
- width: fit-content;
- color: var(--hp-accent);
- text-decoration: none;
- font-size: var(--fs-m);
- padding: var(--sp-1) 0;
- }
:host([data-pointer-hover]) .aboutlink:hover { text-decoration: underline; }
- .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
- hp-dialog .body {
- padding: var(--sp-5) var(--sp-6);
- display: flex;
- flex-direction: column;
- gap: var(--sp-3);
- }
- hp-dialog .tapconfirm-body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog .tapconfirm-body p {
- max-width: 100%;
- min-width: 0;
- margin: 0;
- overflow-wrap: anywhere;
- white-space: normal;
- }
- hp-dialog .tapconfirm-line {
- color: var(--hp-muted);
- }
- hp-dialog .body label {
- font-size: var(--fs-s);
- color: var(--hp-muted);
- margin-top: var(--sp-3);
- }
- hp-dialog .body .namein,
- hp-dialog .body .areasel {
- width: 100%;
- box-sizing: border-box;
- }
- /* Room settings contains long radio labels, two source pickers and live
- previews, so it deliberately uses hp-dialog's medium width. Keep its
- content shrinkable as well: the generic srcrow rule is nowrap because
- compact switch rows need it, but here that created a horizontal scroll
- box even on a wide desktop viewport. */
- hp-dialog.roomdialog .body {
- min-width: 0;
- overflow-x: hidden;
- }
- hp-dialog.roomdialog .body > * {
- min-width: 0;
- max-width: 100%;
- box-sizing: border-box;
- }
- hp-dialog.roomdialog .srcrow {
- min-width: 0;
- align-items: flex-start;
- }
- hp-dialog.roomdialog .srcrow > span:first-of-type {
- min-width: 0;
- white-space: normal;
- overflow-wrap: anywhere;
- line-height: 1.35;
- }
- hp-dialog.roomdialog .dropbtn > b,
- hp-dialog.roomdialog .dropbtn > .ref {
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
.devicepreview-empty {
min-height: 82px;
margin: 4px 0 14px;
@@ -3216,84 +2120,6 @@ const inlineStyles = css`
color: var(--hp-muted);
text-align: center;
}
- hp-dialog .row {
- display: flex;
- justify-content: flex-end;
- gap: var(--sp-4);
- width: 100%;
- min-width: 0;
- box-sizing: border-box;
- padding: var(--sp-5) var(--sp-6);
- border-top: 1px solid var(--hp-line);
- }
- /* Stable destructive/commit footer contract. A flex spacer cannot react
- when translated labels no longer fit: justify-content then overflows
- the destructive button through the left inset. Two real groups wrap
- as units instead — destructive actions stay left, while Cancel/Save
- move together to a right-aligned second row when necessary. */
- hp-dialog .row.dialog-action-footer {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- row-gap: var(--sp-4);
- }
- hp-dialog .dialog-action-group {
- display: flex;
- flex: 0 1 auto;
- flex-wrap: wrap;
- align-items: center;
- gap: var(--sp-4);
- max-width: 100%;
- min-width: 0;
- }
- hp-dialog .dialog-action-group .btn {
- flex: 0 0 auto;
- min-height: 44px;
- }
- hp-dialog .dialog-action-danger {
- margin-right: auto;
- }
- hp-dialog .dialog-action-commit {
- margin-left: auto;
- justify-content: flex-end;
- }
- hp-dialog .row.markerfooter {
- justify-content: space-between;
- align-items: center;
- flex-wrap: wrap;
- }
- hp-dialog .row.roomfooter {
- align-items: center;
- flex-wrap: wrap;
- }
- /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
- wrapping remains as a phone fallback, but without a flex spacer (which
- used to strand Edit alone on the first line). */
- hp-dialog .row.infofooter {
- align-items: center;
- justify-content: flex-start;
- flex-wrap: wrap;
- gap: var(--sp-3);
- }
- hp-dialog .row.infofooter .btn {
- flex-shrink: 0;
- }
- hp-dialog .row.infofooter .infofooter-close {
- margin-left: auto;
- }
- @media (max-width: 480px) {
- hp-dialog .row.infofooter {
- padding: var(--sp-4) var(--sp-5);
- }
- }
- .markeractions,
- .markersaveactions {
- display: flex;
- align-items: center;
- gap: var(--sp-4);
- }
- .markeractions:empty { display: none; }
- .markersaveactions { margin-left: auto; }
:host([data-pointer-hover]) .menu .it:hover {
background: rgba(127, 127, 127, 0.15);
}
@@ -3316,5 +2142,5 @@ const inlineStyles = css`
`;
export const cardStyles: CSSResultGroup = [
- inlineStyles, chromeStyles,
+ inlineStyles, chromeStyles, dialogsStyles,
];
diff --git a/src/styles/dialogs.styles.ts b/src/styles/dialogs.styles.ts
new file mode 100644
index 00000000..6cd1be18
--- /dev/null
+++ b/src/styles/dialogs.styles.ts
@@ -0,0 +1,1180 @@
+/** Dialogs, forms, buttons and pickers (#266, split from styles.ts). */
+import { css } from 'lit';
+
+export const dialogsStyles = css`
+ .head {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px var(--sp-5);
+ border-bottom: 1px solid var(--hp-line);
+ flex-wrap: wrap;
+ }
+ .title {
+ font-size: var(--fs-l);
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ white-space: nowrap;
+ }
+ .title ha-icon {
+ color: var(--hp-accent);
+ --mdc-icon-size: 18px;
+ }
+ @media (max-width: 620px) {
+ .head { gap: var(--sp-3); padding: var(--sp-4) 10px; }
+ .head .count { display: none; }
+ .head .title { font-size: var(--fs-m); }
+ }
+ .count {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ }
+ .btn {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ padding: var(--sp-3) 10px;
+ border-radius: var(--rad-m);
+ cursor: pointer;
+ transition: 0.15s;
+ font-family: inherit;
+ font-size: var(--fs-m);
+ }
+ .btn ha-icon {
+ --mdc-icon-size: 17px;
+ }
+ .btn.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .btn.ghost {
+ border: none;
+ }
+ .btn[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+ }
+ /* the compass dial in the general settings (docs/SUN.md) */
+ .sunrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ margin: var(--sp-3) 0;
+ }
+ .suncol {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ min-width: 0;
+ }
+ .recoveryoverlay {
+ position: absolute;
+ inset: 0;
+ /* Above the contextual editor tray (70): recovery is the one state in
+ which every stage-editing surface must be inert. Dialogs live outside
+ the stage and retain their own higher card-level stacking context. */
+ z-index: 75;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: var(--sp-4);
+ padding: var(--sp-6);
+ box-sizing: border-box;
+ /* The final solid layer guarantees an opaque recovery surface even when
+ a custom HA theme exposes its card colour as rgba(). */
+ background:
+ linear-gradient(var(--ha-card-background, var(--card-background-color, #111)),
+ var(--ha-card-background, var(--card-background-color, #111))),
+ #111;
+ color: var(--primary-text-color, #fff);
+ text-align: center;
+ pointer-events: auto;
+ transition: opacity 0.15s ease;
+ }
+ .recoveryoverlay.phase-entering,
+ .recoveryoverlay.phase-leaving {
+ opacity: 0;
+ }
+ .recoveryoverlay.phase-fading-in,
+ .recoveryoverlay.phase-opaque {
+ opacity: 1;
+ }
+ .recoveryoverlay ha-icon {
+ --mdc-icon-size: 44px;
+ color: var(--hp-accent);
+ }
+ .oplock {
+ --oplock-size: calc(var(--icon-size, 2.5cqw) * 0.62);
+ --oplock-core-size: calc(var(--oplock-size) / 1.26875);
+ --oplock-stroke-ratio: 0.01875;
+ --oplock-stroke-width: max(1px, calc(var(--oplock-core-size) * var(--oplock-stroke-ratio)));
+ --oplock-core-bg: light-dark(#fff, #252525);
+ --oplock-core-fg: light-dark(#252525, #fff);
+ --oplock-shell-stroke: light-dark(#BCBCBC, rgb(37 37 37 / 75%));
+ --oplock-shell-shadow:
+ 0 calc(var(--oplock-core-size) * .025) calc(var(--oplock-core-size) * .05) rgb(37 40 45 / 12%),
+ 0 calc(var(--oplock-core-size) * .1) calc(var(--oplock-core-size) * .175)
+ calc(var(--oplock-core-size) * -.025) rgb(37 40 45 / 18%);
+ --oplock-core-shadow: 0 0 0 0 transparent;
+ pointer-events: none; /* inert while editing; clickable in View (rule below) */
+ position: absolute;
+ transform: translate(-50%, -50%);
+ width: var(--oplock-size);
+ height: var(--oplock-size);
+ display: grid;
+ place-items: center;
+ border: 0;
+ background: transparent;
+ z-index: 1;
+ }
+ .oplock.theme-light {
+ --oplock-core-bg: #fff;
+ --oplock-core-fg: #252525;
+ --oplock-shell-stroke: #BCBCBC;
+ }
+ .oplock.theme-dark {
+ --oplock-core-bg: #252525;
+ --oplock-core-fg: #fff;
+ --oplock-shell-stroke: rgb(37 37 37 / 75%);
+ --oplock-core-shadow:
+ inset 0 calc(var(--oplock-core-size) * .0125)
+ calc(var(--oplock-core-size) * .0125) rgb(255 255 255 / 70%);
+ }
+ .oplock-shell {
+ box-sizing: border-box;
+ width: 100%;
+ height: 100%;
+ border: var(--oplock-stroke-width) solid var(--oplock-shell-stroke);
+ border-radius: 50%;
+ background: transparent;
+ box-shadow: var(--oplock-shell-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ backdrop-filter: none;
+ }
+ .oplock-core {
+ width: var(--oplock-core-size);
+ height: var(--oplock-core-size);
+ border-radius: 50%;
+ background: var(--oplock-core-bg);
+ color: var(--oplock-core-fg);
+ box-shadow: var(--oplock-core-shadow);
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+ }
+ .oplock ha-icon {
+ --mdc-icon-size: calc(var(--oplock-core-size) * 0.55);
+ display: flex;
+ line-height: 0;
+ }
+ .oplock.locked {
+ --oplock-core-bg: #66D17A;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #66D17A;
+ }
+ .oplock.theme-light.locked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.locked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unlocked {
+ --oplock-core-bg: #F0410C;
+ --oplock-core-fg: light-dark(#fff, #252525);
+ --oplock-shell-stroke: #F0410C;
+ }
+ .oplock.theme-light.unlocked { --oplock-core-fg: #fff; }
+ .oplock.theme-dark.unlocked {
+ --oplock-core-fg: #252525;
+ --oplock-stroke-ratio: .025;
+ }
+ .oplock.unknown { --oplock-core-fg: var(--hp-muted); }
+ .btn.lockact {
+ width: 100%;
+ justify-content: center;
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin-top: var(--sp-4);
+ }
+ .btn.lockact.warn {
+ color: var(--error-color, #d33);
+ border-color: var(--error-color, #d33);
+ }
+ .oprow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) 0;
+ }
+ .oprow b { margin-left: auto; }
+ .oprow.ok b { color: #66d17a; }
+ .oprow.warn b { color: var(--hp-open); }
+ .bindharow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-5);
+ flex-wrap: wrap;
+ }
+ .bindharow .entcheck { opacity: 0.9; }
+ .ctrlchips { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin: var(--sp-2) 0; }
+ .ctrlchip {
+ display: inline-flex; align-items: center; gap: var(--sp-2);
+ background: var(--hp-accent); color: var(--text-primary-color, #fff);
+ border-radius: var(--rad-l); padding: var(--sp-2) var(--sp-4); font-size: var(--fs-s);
+ }
+ .ctrlchip ha-icon { --mdc-icon-size: 14px; cursor: pointer; }
+ .ctrllist { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-2); }
+ .ctrlopt {
+ display: flex; align-items: center; gap: var(--sp-4); text-align: left;
+ border: 0; background: transparent; color: var(--hp-txt);
+ padding: var(--sp-3) var(--sp-4); border-radius: var(--rad-s); cursor: pointer; font-family: inherit; font-size: var(--fs-m);
+ }
+ .ctrlopt .sub { color: var(--hp-muted); font-size: var(--fs-s); margin-left: auto; }
+ .ctrlopt ha-icon { --mdc-icon-size: 16px; }
+ .ctrlstates { display: flex; flex-direction: column; gap: var(--sp-2); }
+ .ctrlstate { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--hp-muted); }
+ .ctrlstate.on { color: var(--hp-txt); }
+ .ctrlstate ha-icon { --mdc-icon-size: 15px; }
+ .cardpreview {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: var(--sp-2);
+ margin: var(--sp-4) 0 var(--sp-1);
+ padding: 10px;
+ border: 1px dashed var(--hp-muted);
+ border-radius: var(--rad-m);
+ }
+ .cardpreview .cpname { font-weight: 700; letter-spacing: 0.04em; }
+ .cardpreview .cpmeta {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.3em;
+ font-weight: 600;
+ opacity: 0.85;
+ }
+ .cardpreview .cpmeta ha-icon { --mdc-icon-size: 1.05em; }
+ .iconauto {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-s);
+ margin: var(--sp-2) 0 0;
+ }
+ .iconauto ha-icon { --mdc-icon-size: 18px; }
+ .iconauto span { flex: 1; }
+ .iconauto .btn { min-height: 32px; padding: 0 var(--sp-3); }
+ hp-dialog .dfill {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-3);
+ cursor: pointer;
+ }
+ .wallthick-dlg {
+ position: absolute;
+ z-index: 40;
+ min-width: 200px;
+ transform: translate(-50%, 8px);
+ padding: 10px 12px;
+ border-radius: 10px;
+ background: var(--card-background-color, #fff);
+ box-shadow: 0 8px 28px rgba(0,0,0,.22);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ pointer-events: auto;
+ }
+ .wallthick-dlg .row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .wallthick-dlg input[type="number"] {
+ width: 5.5em;
+ padding: 4px 6px;
+ border: 1px solid var(--divider-color, #ccc);
+ border-radius: 6px;
+ background: var(--input-fill, transparent);
+ color: var(--primary-text-color);
+ }
+ .areasel,
+ .namein {
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .namein {
+ width: 130px;
+ }
+ .habindingbanner {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ padding: var(--sp-3) var(--sp-4);
+ margin-bottom: var(--sp-4);
+ border: 1px solid var(--warning-color, #ff9800);
+ border-radius: var(--rad-m);
+ background: color-mix(in srgb, var(--warning-color, #ff9800) 12%, transparent);
+ color: var(--primary-text-color, #f1f3f6);
+ }
+ .habindingbanner > span { flex: 1; min-width: 0; }
+ .habindingbanner > ha-icon { color: var(--warning-color, #ff9800); flex: 0 0 auto; }
+ .habindingbanner.limited {
+ border-color: var(--secondary-text-color, #9aa0aa);
+ background: color-mix(in srgb, var(--secondary-text-color, #9aa0aa) 10%, transparent);
+ }
+ .habindingbanner.limited > ha-icon { color: var(--secondary-text-color, #9aa0aa); }
+ .srcrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ padding: var(--sp-1) 0;
+ }
+ .dispsection {
+ margin-top: var(--sp-5) !important;
+ padding-top: var(--sp-4);
+ border-top: 1px solid var(--hp-line);
+ font-weight: 600;
+ color: var(--hp-txt) !important;
+ }
+ .colorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .gsrow > hp-color-opacity {
+ min-width: min(100%, 210px);
+ justify-content: space-between;
+ }
+ .ripple-colorrow > hp-color-opacity {
+ width: 100%;
+ justify-content: space-between;
+ }
+ .ripple-sizerow > .opl {
+ min-width: 0;
+ }
+ .colorrow input[type='range'] { flex: 1; }
+ .colorrow .tempin { width: 70px; flex: none; }
+ /* beat the generic hp-dialog .body .namein { width:100% } rule */
+ hp-dialog .body .temprange .tempin { width: 56px; flex: none; padding: var(--sp-2) var(--sp-3); }
+ hp-dialog .body .colorrow .tempin { width: 72px; flex: none; }
+ .srcrow { flex-wrap: nowrap; }
+ /* native HA controls (rendered only when the HA frontend defines them;
+ old HA and the smoke env keep the plain inputs). ha-switch is taller
+ than a checkbox - cap its footprint so .srcrow keeps its rhythm. */
+ .srcrow ha-switch { flex: none; }
+ .colorrow ha-slider { flex: 1; min-width: 0; }
+ .srcrow > span:first-of-type { white-space: nowrap; }
+ .colorrow .opl { color: var(--hp-muted); font-size: var(--fs-s); }
+ .colorrow .opv { font-size: var(--fs-s); min-width: 34px; text-align: right; }
+ .markerlightgroup {
+ min-width: 0;
+ margin: var(--sp-5) 0 0;
+ padding: var(--sp-4);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ }
+ .markerlightgroup legend {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ padding: 0 var(--sp-2);
+ color: var(--hp-txt);
+ font-weight: 600;
+ }
+ .markerlightgroup legend > span { min-width: 0; overflow-wrap: anywhere; }
+ .markerlightgroup[disabled] > :not(legend) { opacity: .62; }
+ .markerhelpfield { margin-top: var(--sp-4); }
+ .markerhelplabel {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: var(--sp-1);
+ margin-bottom: var(--sp-1);
+ }
+ .markerhelplabel > label { min-width: 0; overflow-wrap: anywhere; }
+ .markerradios { display: grid; gap: var(--sp-1); min-width: 0; }
+ .markerlightgroup .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ .markerglowvalue { margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .markerglowvalue hp-color-opacity { flex: none; }
+ .markerlightdisabled {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-2);
+ margin-top: var(--sp-2) !important;
+ }
+ .markerlightdisabled ha-icon { --mdc-icon-size: 18px; flex: none; }
+ .markerbadgetechnical {
+ min-width: 0;
+ margin: var(--sp-1) 0 var(--sp-2) !important;
+ overflow-wrap: anywhere;
+ }
+ .markerbadgetechnical code { white-space: normal; }
+ .planrow {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ /* the "already uploaded" picker: a plan is never deleted for being
+ unreferenced, so it has to be findable again */
+ .savedplans {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ max-height: 240px;
+ overflow: auto;
+ margin: var(--sp-3) 0 var(--sp-1);
+ padding: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ background: var(--hp-bg2, rgba(255, 255, 255, 0.03));
+ /* The same collapse that ate .candlist (v1.53.1): a scroll box is a
+ flex item whose automatic minimum size is ZERO (overflow != visible),
+ so inside hp-dialog .body — a flex column taller than its 66vh cap —
+ it shrank to a 14px sliver: the rows were in the DOM, the owner saw
+ a thin rounded stripe under the "Already uploaded" button. Don't
+ shrink, and keep a floor even when the box is empty or loading. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .savedplan {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ }
+ .savedplan.cur { outline: 1px solid var(--hp-accent); border-radius: var(--rad-s); }
+ .savedplan img {
+ width: 56px;
+ height: 40px;
+ object-fit: contain;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ flex: none;
+ }
+ .savedmeta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
+ .savedmeta b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+ .savedmeta .muted { font-size: var(--fs-s); }
+ .savedplan .btn.danger ha-icon { color: #f25a4a; }
+ .savedplan .btn[disabled] { opacity: 0.4; pointer-events: none; }
+ .planprev {
+ max-width: 120px;
+ max-height: 70px;
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ background: #fff;
+ }
+ .planname {
+ font-size: var(--fs-m);
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .planname.muted {
+ color: var(--hp-muted);
+ }
+ .filebtn {
+ cursor: pointer;
+ }
+ .fileupload { display: inline-flex; min-width: 0; }
+ .fileupload > input { display: none; }
+ .btn.danger {
+ border-color: #b3402a;
+ color: #ff7a5c;
+ }
+ hp-dialog .row .spacer {
+ flex: 1;
+ }
+ hp-dialog .body {
+ max-height: 66vh;
+ overflow-y: auto;
+ }
+ .descin {
+ width: 100%;
+ box-sizing: border-box;
+ background: var(--hp-bg);
+ border: 1px solid var(--hp-line);
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ font-size: var(--fs-m);
+ font-family: inherit;
+ resize: vertical;
+ /* flex column of the dialog body squeezes textareas — keep a usable height */
+ min-height: 92px;
+ flex-shrink: 0;
+ line-height: 1.35;
+ }
+ .bindsel {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-m);
+ padding: var(--sp-4);
+ }
+ .bindsel .opt {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ border: 1px solid var(--hp-line);
+ background: transparent;
+ color: var(--hp-txt);
+ border-radius: var(--rad-s);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ font-size: var(--fs-m);
+ font-family: inherit;
+ }
+ .bindsel .opt.on {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ border-color: var(--hp-accent);
+ }
+ .curbind {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ font-size: var(--fs-m);
+ color: var(--hp-txt);
+ flex-wrap: wrap;
+ }
+ .curbind .ref {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .vacbox .vacbtns { display: flex; gap: var(--sp-4); margin: var(--sp-3) 0; flex-wrap: wrap; }
+ .vacdiag { display: grid; gap: 4px; margin-bottom: var(--sp-3); }
+ .vacdiag > div { display: flex; justify-content: space-between; gap: var(--sp-5); }
+ .vacdiag > div > span { color: var(--secondary-text-color); }
+ .vacdiag > div > b { text-align: right; overflow-wrap: anywhere; }
+ .vacpicker { margin: var(--sp-3) 0; }
+ .vacsource-warning { display: grid; gap: 8px; }
+ .vacsource-warning .btn { justify-self: start; }
+ .vacpicker > summary { display: inline-flex; width: auto; list-style: none; cursor: pointer; }
+ .vacpicker > summary::-webkit-details-marker { display: none; }
+ .vacsource-list { display: grid; gap: 6px; margin-top: 8px; }
+ .vacsource-list details { padding: 6px 0 0; }
+ .vacsource-list details > summary { cursor: pointer; font-weight: 600; }
+ .vacsource { display: flex; align-items: center; justify-content: space-between; gap: 12px;
+ width: 100%; min-width: 0; padding: 9px 10px; border: 1px solid var(--divider-color);
+ border-radius: 10px; color: var(--primary-text-color); background: var(--secondary-background-color);
+ text-align: left; cursor: pointer; }
+ .vacsource.on { border-color: var(--accent-color); box-shadow: inset 3px 0 var(--accent-color); }
+ .vacsource > span:first-child { min-width: 0; display: grid; gap: 2px; }
+ .vacsource small { color: var(--secondary-text-color); overflow-wrap: anywhere; }
+ .vacsource-meta { color: var(--secondary-text-color); text-align: right; font-size: 0.82em; }
+ .vacxcme pre { margin: 8px 0 0; white-space: pre-wrap; user-select: text; }
+ .vacfit {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 12;
+ overflow: visible;
+ touch-action: none;
+ cursor: grab;
+ /* the devlayer is pointer-events: none and every child opts back in —
+ without this line real clicks flew straight through the overlay
+ (owner: «уголки не кликабельны»; synthetic smoke events bypass
+ hit-testing, which is why they lied) */
+ pointer-events: auto;
+ }
+ .vacfit:active { cursor: grabbing; }
+ .vacfit polygon {
+ fill: color-mix(in srgb, var(--hp-accent) 16%, transparent);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ stroke-dasharray: 6 4;
+ }
+ .vacfit text {
+ fill: var(--hp-accent);
+ font-size: 26px;
+ text-anchor: middle;
+ dominant-baseline: middle;
+ pointer-events: none;
+ user-select: none;
+ }
+ /* ---- the furniture palette (docs/FURNITURE.md §3) ------------------- */
+ .furnpalette {
+ display: flex;
+ flex-direction: column;
+ max-height: 38vh;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ background: var(--card-background-color, var(--hp-bg));
+ font-size: 0.85em;
+ }
+ .furnhd {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 8px;
+ font-weight: 600;
+ opacity: 0.9;
+ }
+ .furnhd .spacer { flex: 1; }
+ .furnbody {
+ overflow: auto;
+ padding: 0 8px 6px;
+ }
+ .furngroup {
+ margin: 6px 0 3px;
+ font-size: 0.85em;
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+ opacity: 0.6;
+ }
+ .furnrow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
+ .furnitem {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ width: 74px;
+ padding: 4px 2px;
+ border: 1px solid transparent;
+ border-radius: 8px;
+ background: rgba(127, 127, 127, 0.08);
+ color: inherit;
+ font: inherit;
+ font-size: 0.8em;
+ line-height: 1.15;
+ text-align: center;
+ cursor: pointer;
+ }
+ .furnitem.on {
+ border-color: var(--hp-accent);
+ background: rgba(38, 166, 154, 0.18);
+ }
+ .furnprev {
+ width: 40px;
+ height: 40px;
+ color: var(--primary-text-color, currentColor);
+ }
+ .furnsize {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 6px;
+ padding: 6px 8px;
+ border-top: 1px solid var(--hp-border, rgba(255, 255, 255, 0.12));
+ }
+ .furnsize label {
+ display: flex;
+ align-items: baseline;
+ gap: 3px;
+ opacity: 0.8;
+ }
+ .furnsize .furnunit { opacity: 0.6; font-size: 0.85em; }
+ .furnsize input {
+ width: 5.5em;
+ padding: 3px 5px;
+ }
+ .furnhint { opacity: 0.6; }
+ .vacfitdot { fill: var(--hp-accent); pointer-events: none; }
+ /* hit target: invisible and finger-sized; .vacfitknob is the visible bead */
+ .vacfithandle {
+ fill: transparent;
+ stroke: none;
+ cursor: nwse-resize;
+ }
+ .vacfitknob {
+ fill: var(--hp-bg);
+ stroke: var(--hp-accent);
+ stroke-width: 2;
+ vector-effect: non-scaling-stroke;
+ pointer-events: none;
+ }
+ .vaccalbar {
+ position: fixed;
+ left: 50%;
+ bottom: 24px;
+ transform: translateX(-50%);
+ display: flex;
+ gap: var(--sp-5);
+ align-items: center;
+ background: var(--hp-bg);
+ color: var(--hp-txt);
+ border: 1px solid var(--hp-accent);
+ border-radius: var(--rad-l);
+ padding: 10px var(--sp-5);
+ z-index: 60;
+ box-shadow: var(--shadow-2);
+ }
+ .candlist {
+ max-height: 160px;
+ overflow-y: auto;
+ border-top: 1px solid var(--hp-line);
+ /* A scrollable box is a flex item that HAPPILY collapses: inside the
+ dialog body (a flex column) this list rendered its rows into a 1px
+ sliver — the DOM had 26 candidates and the user saw nothing. In the
+ binding dropdown it sits inside .droppanel (block context) and never
+ showed the bug. Field report, 2026-07-30. */
+ flex: 0 0 auto;
+ min-height: 2.6em;
+ }
+ .cand {
+ display: flex;
+ justify-content: space-between;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ cursor: pointer;
+ border-radius: var(--rad-s);
+ font-size: var(--fs-m);
+ }
+ .cand.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .cand .cs {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ white-space: nowrap;
+ }
+ .opening-entity-candidate {
+ width: 100%;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ text-align: left;
+ }
+ .opening-entity-candidate.sel {
+ background: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ }
+ .opening-entity-empty { cursor: default; }
+ .cand.sel .cs {
+ color: var(--text-primary-color, #fff);
+ opacity: 0.85;
+ }
+ .cand.muted {
+ color: var(--hp-muted);
+ cursor: default;
+ }
+ .pdfedit {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ align-items: center;
+ }
+ .pdftag {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .pdftag a {
+ color: var(--hp-txt);
+ text-decoration: none;
+ max-width: 150px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .pdftag .x {
+ --mdc-icon-size: 15px;
+ cursor: pointer;
+ color: var(--hp-muted);
+ }
+ .entlist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ margin-bottom: 10px;
+ }
+ .entrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-4);
+ border-radius: var(--rad-m);
+ background: var(--secondary-background-color, rgba(128, 128, 128, 0.12));
+ }
+ .entrow ha-icon { --mdc-icon-size: 20px; color: var(--hp-muted); }
+ .entrow.on ha-icon { color: var(--hp-accent); }
+ .entrow .en { flex: 1; font-size: var(--fs-m); }
+ .entrow .ev { font-size: var(--fs-m); color: var(--hp-muted); }
+ .entbtn {
+ min-width: 74px;
+ min-height: 32px;
+ padding: var(--sp-2) var(--sp-5);
+ border: 1px solid var(--hp-muted);
+ border-radius: 999px;
+ background: transparent;
+ color: var(--hp-txt);
+ font: inherit;
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .entbtn.on {
+ background: var(--hp-accent);
+ border-color: var(--hp-accent);
+ color: var(--text-primary-color, #fff);
+ font-weight: 600;
+ }
+ .inforow {
+ display: flex;
+ gap: 10px;
+ font-size: var(--fs-m);
+ margin: var(--sp-2) 0;
+ }
+ .inforow .k {
+ color: var(--hp-muted);
+ min-width: 84px;
+ }
+ .inforow a {
+ color: var(--hp-accent);
+ word-break: break-all;
+ }
+ .infodesc {
+ font-size: var(--fs-m);
+ white-space: pre-wrap;
+ margin-top: var(--sp-3);
+ }
+ .infodesc.muted {
+ color: var(--hp-muted);
+ }
+ .pdflist {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ }
+ .pdf {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--sp-2);
+ color: var(--hp-accent);
+ text-decoration: none;
+ }
+ ha-icon-picker {
+ display: block;
+ }
+ .floorrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ padding: var(--sp-3) var(--sp-2);
+ font-size: var(--fs-m);
+ cursor: pointer;
+ }
+ .floorrow .floorlvl {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: 0 var(--sp-3);
+ }
+ .rtest {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ margin-bottom: var(--sp-4);
+ }
+ .rtest .namein { flex: 1; }
+ .rtest ha-icon { color: var(--hp-accent); }
+ .rtesticon { font-size: var(--fs-s); color: var(--hp-muted); }
+ .rrow {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ margin: var(--sp-1) 0;
+ }
+ .rrow .rpat { flex: 2; }
+ .rrow .ricon { flex: 1.4; }
+ .rrow .rpat.bad { border-color: #ff7a5c; }
+ .rrow .rprev { --mdc-icon-size: 18px; color: var(--hp-txt); min-width: 18px; }
+ .rrow .ract {
+ --mdc-icon-size: 16px;
+ color: var(--hp-muted);
+ cursor: pointer;
+ }
+ .gsrow .gsl {
+ min-width: 150px;
+ font-size: var(--fs-m);
+ color: var(--hp-muted);
+ }
+ .optimize-live {
+ display: grid;
+ justify-items: start;
+ gap: var(--sp-2);
+ margin-bottom: var(--sp-3);
+ }
+ .optimize-live .alignmsg, .optimize-live .rhint { margin-bottom: 0; }
+ .optimize-cleanup { min-height: 44px; }
+ .optimize-selected { color: var(--hp-txt); }
+ .optimize-details {
+ margin-top: var(--sp-3);
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ overflow-wrap: anywhere;
+ }
+ .optimize-details > summary {
+ width: fit-content;
+ color: var(--hp-txt);
+ cursor: pointer;
+ font-weight: 600;
+ }
+ .optimize-details > summary:focus-visible {
+ outline: 2px solid var(--hp-accent);
+ outline-offset: 3px;
+ border-radius: var(--rad-s);
+ }
+ .optimize-details ul { margin: var(--sp-3) 0; padding-inline-start: 22px; }
+ .optimize-details li + li { margin-top: var(--sp-1); }
+ .btn.alignall { width: 100%; justify-content: center; }
+ .backupactions {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-3);
+ }
+ .backupactions .btn { justify-content: center; min-width: 0; }
+ .backupupload { display: inline-flex; min-width: 0; }
+ .backupupload > .btn { width: 100%; justify-content: center; }
+ .backupupload input { display: none; }
+ .backupbody { min-width: 0; }
+ .backupplanonly { margin-inline-start: var(--sp-4) !important; align-items: flex-start !important; }
+ .backupplanonly > span:first-of-type { display: grid; gap: 2px; white-space: normal; }
+ .backupplanonly small { color: var(--secondary-text-color); line-height: 1.35; }
+ .backupplanonlystatus { color: var(--hp-accent) !important; font-weight: 700; }
+ .backupfile, .backupsummary, .backupcontent {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ gap: var(--sp-1);
+ }
+ .backupfile b, .backupcontent span {
+ overflow-wrap: anywhere;
+ }
+ .backupfile span, .backupsummary span, .backupcontent span {
+ color: var(--hp-muted);
+ font-size: var(--fs-s);
+ }
+ .backupcounts {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: var(--sp-2) var(--sp-4);
+ font-size: var(--fs-s);
+ }
+ .backupwarn, .backuperror {
+ border-radius: var(--rad-s);
+ padding: var(--sp-3);
+ font-size: var(--fs-s);
+ line-height: 1.45;
+ overflow-wrap: anywhere;
+ }
+ .backupwarn { background: color-mix(in srgb, var(--hp-accent) 12%, transparent); }
+ .backuperror { background: rgba(179, 64, 42, .16); color: #ff7a5c; }
+ .backupdetails {
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ padding: var(--sp-2) var(--sp-3);
+ font-size: var(--fs-s);
+ }
+ .backupdetails summary { cursor: pointer; font-weight: 700; }
+ .backupdetails > div { display: grid; gap: var(--sp-1); padding-block-start: var(--sp-2); }
+ .backupdetails code { overflow-wrap: anywhere; color: var(--hp-muted); }
+ .backupchoices {
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-2);
+ border: 1px solid var(--hp-line);
+ border-radius: var(--rad-s);
+ min-width: 0;
+ }
+ .backupchoices label { margin: 0 !important; display: flex; gap: var(--sp-2); }
+ .backupconfirm { align-items: flex-start !important; }
+ .backupconfirm > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ }
+ @media (max-width: 520px) {
+ .backupactions, .backupcounts { grid-template-columns: 1fr; }
+ }
+ .aboutver {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin: var(--sp-2) 0 var(--sp-3);
+ }
+ .aboutlink {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-3);
+ width: fit-content;
+ color: var(--hp-accent);
+ text-decoration: none;
+ font-size: var(--fs-m);
+ padding: var(--sp-1) 0;
+ }
+ .aboutlink ha-icon { --mdc-icon-size: 18px; line-height: 1; }
+ hp-dialog .body {
+ padding: var(--sp-5) var(--sp-6);
+ display: flex;
+ flex-direction: column;
+ gap: var(--sp-3);
+ }
+ hp-dialog .tapconfirm-body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog .tapconfirm-body p {
+ max-width: 100%;
+ min-width: 0;
+ margin: 0;
+ overflow-wrap: anywhere;
+ white-space: normal;
+ }
+ hp-dialog .tapconfirm-line {
+ color: var(--hp-muted);
+ }
+ hp-dialog .body label {
+ font-size: var(--fs-s);
+ color: var(--hp-muted);
+ margin-top: var(--sp-3);
+ }
+ hp-dialog .body .namein,
+ hp-dialog .body .areasel {
+ width: 100%;
+ box-sizing: border-box;
+ }
+ /* Room settings contains long radio labels, two source pickers and live
+ previews, so it deliberately uses hp-dialog's medium width. Keep its
+ content shrinkable as well: the generic srcrow rule is nowrap because
+ compact switch rows need it, but here that created a horizontal scroll
+ box even on a wide desktop viewport. */
+ hp-dialog.roomdialog .body {
+ min-width: 0;
+ overflow-x: hidden;
+ }
+ hp-dialog.roomdialog .body > * {
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ }
+ hp-dialog.roomdialog .srcrow {
+ min-width: 0;
+ align-items: flex-start;
+ }
+ hp-dialog.roomdialog .srcrow > span:first-of-type {
+ min-width: 0;
+ white-space: normal;
+ overflow-wrap: anywhere;
+ line-height: 1.35;
+ }
+ hp-dialog.roomdialog .dropbtn > b,
+ hp-dialog.roomdialog .dropbtn > .ref {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ hp-dialog .row {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--sp-4);
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+ padding: var(--sp-5) var(--sp-6);
+ border-top: 1px solid var(--hp-line);
+ }
+ /* Stable destructive/commit footer contract. A flex spacer cannot react
+ when translated labels no longer fit: justify-content then overflows
+ the destructive button through the left inset. Two real groups wrap
+ as units instead — destructive actions stay left, while Cancel/Save
+ move together to a right-aligned second row when necessary. */
+ hp-dialog .row.dialog-action-footer {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ row-gap: var(--sp-4);
+ }
+ hp-dialog .dialog-action-group {
+ display: flex;
+ flex: 0 1 auto;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--sp-4);
+ max-width: 100%;
+ min-width: 0;
+ }
+ hp-dialog .dialog-action-group .btn {
+ flex: 0 0 auto;
+ min-height: 44px;
+ }
+ hp-dialog .dialog-action-danger {
+ margin-right: auto;
+ }
+ hp-dialog .dialog-action-commit {
+ margin-left: auto;
+ justify-content: flex-end;
+ }
+ hp-dialog .row.markerfooter {
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ hp-dialog .row.roomfooter {
+ align-items: center;
+ flex-wrap: wrap;
+ }
+ /* Device info can have Edit + Open in HA + Close. It uses a wide dialog;
+ wrapping remains as a phone fallback, but without a flex spacer (which
+ used to strand Edit alone on the first line). */
+ hp-dialog .row.infofooter {
+ align-items: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+ gap: var(--sp-3);
+ }
+ hp-dialog .row.infofooter .btn {
+ flex-shrink: 0;
+ }
+ hp-dialog .row.infofooter .infofooter-close {
+ margin-left: auto;
+ }
+ @media (max-width: 480px) {
+ hp-dialog .row.infofooter {
+ padding: var(--sp-4) var(--sp-5);
+ }
+ }
+ .markeractions,
+ .markersaveactions {
+ display: flex;
+ align-items: center;
+ gap: var(--sp-4);
+ }
+ .markeractions:empty { display: none; }
+ .markersaveactions { margin-left: auto; }
+`;
diff --git a/test/color-picker.test.mjs b/test/color-picker.test.mjs
index faffa92b..48b53c7c 100644
--- a/test/color-picker.test.mjs
+++ b/test/color-picker.test.mjs
@@ -4,6 +4,7 @@ import test from 'node:test';
import {
hexToRgb, hsvToHex, hsvToRgb, normalizeHexColor, normalizeHue, rgbToHex, rgbToHsv,
} from '../test-build/color-picker.js';
+import { readAllStylesSource } from './styles-source.mjs';
test('hex drafts accept only three or six digits and normalize on commit', () => {
assert.equal(normalizeHexColor('#AbC'), '#aabbcc');
@@ -80,7 +81,7 @@ test('the hue range exposes one cyclic spectrum without restyling other ranges',
test('activity color and ripple size keep independent readable rows', () => {
const card = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
- const styles = readFileSync(new URL('../src/styles.ts', import.meta.url), 'utf8');
+ const styles = readAllStylesSource();
const start = card.indexOf("d.display === 'icon_ripple'");
const end = card.indexOf("marker.activity_alarm_note", start);
const ripple = card.slice(start, end);
diff --git a/test/device-marker-polish-contract.test.mjs b/test/device-marker-polish-contract.test.mjs
index 57608289..77c7396e 100644
--- a/test/device-marker-polish-contract.test.mjs
+++ b/test/device-marker-polish-contract.test.mjs
@@ -2,7 +2,13 @@ import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
-const source = (name) => readFileSync(new URL(`../src/${name}`, import.meta.url), 'utf8');
+import { readAllStylesSource } from './styles-source.mjs';
+
+// #266: styles.ts is being split into surface files — style greps must
+// see the whole sheet, other sources keep the direct read.
+const source = (name) => name === 'styles.ts'
+ ? readAllStylesSource()
+ : readFileSync(new URL(`../src/${name}`, import.meta.url), 'utf8');
test('issue 213 resolves the effective base before the face without a late visual factor', () => {
const styles = source('styles.ts');
diff --git a/test/isometric-contract.test.mjs b/test/isometric-contract.test.mjs
index e4c2af71..862bf57d 100644
--- a/test/isometric-contract.test.mjs
+++ b/test/isometric-contract.test.mjs
@@ -1,9 +1,10 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
+import { readAllStylesSource } from './styles-source.mjs';
const card = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
-const styles = readFileSync(new URL('../src/styles.ts', import.meta.url), 'utf8');
+const styles = readAllStylesSource();
const openings = readFileSync(new URL('../src/iso-openings.ts', import.meta.url), 'utf8');
const packageJson = readFileSync(new URL('../package.json', import.meta.url), 'utf8');
const spaceCard = readFileSync(new URL('../src/space-card.ts', import.meta.url), 'utf8');
diff --git a/test/open-passage-contract.test.mjs b/test/open-passage-contract.test.mjs
index ed710d4e..7b1374e6 100644
--- a/test/open-passage-contract.test.mjs
+++ b/test/open-passage-contract.test.mjs
@@ -1,9 +1,10 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
+import { readAllStylesSource } from './styles-source.mjs';
const card = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
-const styles = readFileSync(new URL('../src/styles.ts', import.meta.url), 'utf8');
+const styles = readAllStylesSource();
const staticRender = readFileSync(new URL('../src/space-render.ts', import.meta.url), 'utf8');
const backend = readFileSync(new URL('../custom_components/houseplan/websocket_api.py', import.meta.url), 'utf8');
const importer = readFileSync(new URL('../custom_components/houseplan/import_export.py', import.meta.url), 'utf8');
diff --git a/test/resize-production-path.test.mjs b/test/resize-production-path.test.mjs
index 9c7a52fa..65e07dd9 100644
--- a/test/resize-production-path.test.mjs
+++ b/test/resize-production-path.test.mjs
@@ -1,10 +1,11 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
+import { readAllStylesSource } from './styles-source.mjs';
const card = fs.readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
const resize = fs.readFileSync(new URL('../src/resize.ts', import.meta.url), 'utf8');
-const styles = fs.readFileSync(new URL('../src/styles.ts', import.meta.url), 'utf8');
+const styles = readAllStylesSource();
const en = JSON.parse(fs.readFileSync(new URL('../src/i18n/en.json', import.meta.url), 'utf8'));
const ru = JSON.parse(fs.readFileSync(new URL('../src/i18n/ru.json', import.meta.url), 'utf8'));
diff --git a/test/styles-source.mjs b/test/styles-source.mjs
new file mode 100644
index 00000000..93a30421
--- /dev/null
+++ b/test/styles-source.mjs
@@ -0,0 +1,16 @@
+/**
+ * #266: the style sheet is being split into surface files. Contract tests
+ * that grep the CSS source must see the WHOLE sheet regardless of the split
+ * state, so they read src/styles.ts plus every surface file it imports.
+ */
+import { readFileSync } from 'node:fs';
+
+export function readAllStylesSource() {
+ const aggregatorUrl = new URL('../src/styles.ts', import.meta.url);
+ const aggregator = readFileSync(aggregatorUrl, 'utf8');
+ const parts = [aggregator];
+ for (const m of aggregator.matchAll(/from '\.\/styles\/([\w-]+)\.styles'/g)) {
+ parts.push(readFileSync(new URL(`../src/styles/${m[1]}.styles.ts`, import.meta.url), 'utf8'));
+ }
+ return parts.join('\n');
+}