{const i=new Image;i.onload=()=>I(i.naturalWidth&&i.naturalHeight?i.naturalWidth/i.naturalHeight:1.414),i.onerror=()=>I(1.414),i.src=c});URL.revokeObjectURL(c),this._spaceDialog={...this._spaceDialog,planFile:{ext:N,b64:j,aspect:g,name:M.name}}}async _saveSpaceDialog(){const I=this._spaceDialog;if(I&&!I.busy&&I.title.trim()){this._spaceDialog={...I,busy:!0};try{const i=this._serverCfg;let M;if("create"===I.mode?(M={id:"s"+Date.now().toString(36),title:I.title.trim(),plan_url:null,aspect:1.414,view_box:[0,0,1,1],rooms:[],segments:[]},i.spaces.push(M)):(M=i.spaces.find(i=>i.id===I.spaceId),M.title=I.title.trim()),I.planFile){const i=await this.hass.callWS({type:"houseplan/plan/set",space_id:M.id,ext:I.planFile.ext,data:I.planFile.b64});M.plan_url=i.url,M.aspect=I.planFile.aspect}await this._saveConfigNow(),this._spaceDialog=null,"create"===I.mode&&(this._space=M.id),this._regSignature="",this._maybeRebuildDevices(),this._showToast("create"===I.mode?"Пространство добавлено":"Пространство сохранено")}catch(I){this._spaceDialog={...this._spaceDialog,busy:!1},this._showToast("Ошибка: "+(I?.message||I))}}}async _deleteSpace(){const I=this._spaceDialog;if(!I||"edit"!==I.mode)return;const i=this._serverCfg.spaces.find(i=>i.id===I.spaceId);if(confirm(`Удалить пространство «${i.title}» со всеми комнатами и разметкой?`)){this._serverCfg.spaces=this._serverCfg.spaces.filter(i=>i.id!==I.spaceId);try{await this._saveConfigNow(),this._spaceDialog=null,this._space===I.spaceId&&(this._space=this._serverCfg.spaces[0]?.id||""),this._regSignature="",this._maybeRebuildDevices(),this._showToast("Пространство удалено")}catch(I){this._showToast("Ошибка удаления: "+(I?.message||I))}}}async _saveConfigNow(){const I=await this.hass.callWS({type:"houseplan/config/set",config:this._serverCfg,expected_rev:this._cfgRev});this._cfgRev=I?.rev??this._cfgRev+1}async _migrateToServer(){if(this._serverStorage&&!this._norm&&!this._migrating&&confirm("Перенести текущую конфигурацию (планы, комнаты, раскладку) на сервер HA?")){this._migrating=!0;try{const I=[];for(const i of Object.keys(zI)){const M=zI[i],N=aI[i]||M,[T,D,j,c]=N;let g=null,w=j/c;const z=OI[i];if(z&&aI[i]){const I=bI[i];I&&(w=I[0]/I[1]);const M=z.split(",")[1];g=(await this.hass.callWS({type:"houseplan/plan/set",space_id:i,ext:"svg",data:M})).url}const d=wI.filter(I=>I.floor===i).map((I,M)=>({id:i+"_r"+M,name:I.name,area:I.area||null,x:(I.x-T)/j,y:(I.y-D)/c,w:I.w/j,h:I.h/c}));I.push({id:i,title:dI[i]||i,plan_url:g,aspect:w,view_box:[(M[0]-T)/j,(M[1]-D)/c,M[2]/j,M[3]/c],rooms:d})}const i={spaces:I,device_overrides:{},virtual_devices:[],settings:{exclude_integrations:[...lI],group_lights:!0}},M={};for(const I of this._devices){if(I.virtual)continue;const i=this._layout[I.id]||this._defPos[I.id];if(!i)continue;const N=aI[I.space]||zI[I.space];M[I.id]={s:I.space,x:(i.x-N[0])/N[2],y:(i.y-N[1])/N[3]}}await this.hass.callWS({type:"houseplan/config/set",config:i}),await this.hass.callWS({type:"houseplan/layout/set",layout:M}),this._serverCfg=i,this._layout=M,this._regSignature="",this._maybeRebuildDevices(),this._showToast("Конфигурация перенесена на сервер — карта работает от server config")}catch(I){this._showToast("Ошибка миграции: "+(I?.message||I))}finally{this._migrating=!1}}}render(){if(!this._config||!this.hass)return v;const I=this._model;if(!I.length)return Q`Нет настроенных пространств
`;const i=this._spaceModel(),M=i.vb,N=this._devices.filter(I=>I.space===i.id),T=this._config.icon_size??2.5,D=T>8?2.5:T;return Q`
${this._config.title||"План дома"}
${I.map(I=>Q``)}
${this._norm?Q``:v}
${N.length} устр.
${this._norm?Q`
`:v}
${this._edit?this._renderEditbar():v}
${this._markup?this._renderMarkupBar():v}
this._markupClick(I)}
@mousemove=${I=>this._markupMove(I)}>
${N.map(I=>this._renderDevice(I,M))}
${this._roomDialog?this._renderRoomDialog():v}
${this._spaceDialog?this._renderSpaceDialog():v}
${this._markerDialog?this._renderMarkerDialog():v}
${this._infoCard?this._renderInfoCard():v}
${this._tip?Q`
${this._tip.title}${this._tip.meta?Q`${this._tip.meta}`:v}
${null!=this._tip.lqi?Q`средний сигнал zigbee:
${this._tip.lqi}`:v}
`:v}
${this._toast?Q`${this._toast}
`:v}
`}_renderDevice(I,i){const M=this._pos(I),N=(M.x-i[0])/i[2]*100,T=(M.y-i[1])/i[3]*100,D=this._stateClass(I),j=this._liveTemp(I),c=this._config?.show_signal&&!I.virtual?this._lqiFor(I.entities):null;return Q`this._clickDevice(i,I)}
@mousemove=${i=>this._showTip(i,I.name,I.model+(null!=j?" · "+j+"°":"")+(null!=c?" · LQI "+c:""))}
@mouseleave=${()=>this._tip=null}
@pointerdown=${i=>this._pointerDown(i,I)}
@pointermove=${i=>this._pointerMove(i,I)}
@pointerup=${i=>this._pointerUp(i,I)}
>
${null!=j?Q`${j}°`:v}
${null!=c?Q`${c}`:v}
`}_roomCenter(I){if(I.poly){const i=I.poly.length;return[I.poly.reduce((I,i)=>I+i[0],0)/i,I.poly.reduce((I,i)=>I+i[1],0)/i]}return[I.x+I.w/2,I.y+.1*Math.min(I.w,I.h)]}_renderMarkupDefs(I){const i=this._gridPitch,M=.14*i;return W`
`}_renderMarkupLayer(I){const i=this._segments,M=this._path,N=this._gridPitch;return W`
${i.map(I=>W``)}
${M.length>1?W``:v}
${M.length&&this._cursorPt&&"draw"===this._tool&&!this._contourClosed?W``:v}
${M.map((I,i)=>W``)}
`}_renderMarkupBar(){return Q`
${"draw"===this._tool?Q`${this._path.length?"точек: "+this._path.length+" · Esc/Ctrl+Z — убрать точку · замкните контур кликом по первой":"кликните точку сетки, чтобы начать контур"}
${this._path.length?Q``:v}`:v}
`}_renderInfoCard(){const I=this._infoCard,i=I.primary?this.hass.states[I.primary]:void 0,M=i?this.hass.formatEntityState?.(i)??i.state:null;return Q``}_renderMarkerDialog(){const I=this._markerDialog,i="virtual"===I.binding,M=this._bindingCandidates(),N=(()=>{if(i)return null;const N=M.find(i=>i.value===I.binding);if(N)return N.label;const[T,D]=I.binding.split(":");return"device"===T?this.hass.devices[D]?.name_by_user||this.hass.devices[D]?.name||D:this.hass.states[D]?.attributes?.friendly_name||D})();return Q``}_renderSpaceDialog(){const I=this._spaceDialog;return Q``}_renderRoomDialog(){const I=this._freeAreas;return Q``}_renderEditbar(){const I=this._selId?this._devices.find(I=>I.id===this._selId):null,i=I?this._pos(I):null;return Q`
${I?I.name:"Режим правки — тащите иконки мышью"}
${I&&i?Q`this._applyXY("x",I.target.value)} />
this._applyXY("y",I.target.value)} />`:v}
${this._serverStorage?this._norm?"конфиг и раскладка: сервер":"раскладка: сервер · конфиг: встроенный":"сохранение: этот браузер"}
${this._serverStorage&&!this._norm?Q``:v}
`}}LI.properties={hass:{attribute:!1},_config:{state:!0},_space:{state:!0},_edit:{state:!0},_layout:{state:!0},_devices:{state:!0},_tip:{state:!0},_selId:{state:!0},_toast:{state:!0},_serverCfg:{state:!0},_migrating:{state:!0},_markup:{state:!0},_tool:{state:!0},_path:{state:!0},_cursorPt:{state:!0},_areaSel:{state:!0},_nameSel:{state:!0},_roomDialog:{state:!0},_spaceDialog:{state:!0},_infoCard:{state:!0},_markerDialog:{state:!0}},LI.styles=((I,...i)=>{const N=1===I.length?I[0]:i.reduce((i,M,N)=>i+(I=>{if(!0===I._$cssResult$)return I.cssText;if("number"==typeof I)return I;throw Error("Value passed to 'css' function must be a 'css' function result: "+I+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(M)+I[N+1],I[0]);return new T(N,I,M)})`
:host {
--hp-bg: var(--card-background-color, #16212e);
--hp-line: var(--divider-color, #2b3d4f);
--hp-txt: var(--primary-text-color, #e6edf3);
--hp-muted: var(--secondary-text-color, #8aa0b3);
--hp-accent: var(--primary-color, #3ea6ff);
--hp-on: #ffd45c;
--hp-open: #ff9f43;
}
ha-card {
overflow: visible; /* overflow:hidden ломает position:sticky у шапки */
}
.empty {
padding: 32px;
color: var(--hp-muted);
text-align: center;
}
.hdr {
position: sticky;
top: var(--header-height, 56px);
z-index: 20;
background: var(--card-background-color, var(--hp-bg));
border-radius: var(--ha-card-border-radius, 12px) var(--ha-card-border-radius, 12px) 0 0;
}
.head {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-bottom: 1px solid var(--hp-line);
flex-wrap: wrap;
}
.title {
font-size: 15px;
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.title ha-icon {
color: var(--hp-accent);
--mdc-icon-size: 18px;
}
.tabs {
display: flex;
gap: 4px;
background: rgba(127, 127, 127, 0.12);
padding: 3px;
border-radius: 10px;
}
.tab {
border: 0;
background: transparent;
color: var(--hp-muted);
padding: 6px 13px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: 0.15s;
font-family: inherit;
}
.tab:hover {
color: var(--hp-txt);
}
.tab.active {
background: var(--hp-accent);
color: var(--text-primary-color, #fff);
}
.count {
font-size: 12px;
color: var(--hp-muted);
}
.spacer {
flex: 1;
}
.btn {
display: inline-flex;
align-items: center;
gap: 6px;
border: 1px solid var(--hp-line);
background: transparent;
color: var(--hp-txt);
padding: 6px 10px;
border-radius: 8px;
cursor: pointer;
transition: 0.15s;
font-family: inherit;
font-size: 12.5px;
}
.btn ha-icon {
--mdc-icon-size: 17px;
}
.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 {
position: relative;
width: 100%;
container-type: inline-size;
}
.stage svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
.room {
transition: 0.12s;
cursor: pointer;
}
.room.overlay {
fill: transparent;
stroke: transparent;
stroke-width: 2;
}
.room.overlay:hover {
fill: rgba(62, 166, 255, 0.18);
stroke: var(--hp-accent);
}
.room.yard {
fill: rgba(75, 140, 90, 0.14);
stroke: #4b8c5a;
stroke-width: 2;
}
.room.yard:hover {
fill: rgba(75, 140, 90, 0.24);
stroke: #6fbf86;
}
.rlabel {
fill: var(--hp-muted);
font-size: 15px;
font-weight: 600;
pointer-events: none;
text-anchor: middle;
}
.stage.edit .room {
pointer-events: none;
}
.stage.markup {
cursor: crosshair;
}
.stage.markup .room {
pointer-events: none;
}
.stage.markup .devlayer {
display: none; /* в разметке иконки не мешают */
}
.room.outlined {
stroke: rgba(62, 166, 255, 0.55);
fill: rgba(62, 166, 255, 0.06);
}
.griddot {
fill: var(--hp-accent);
opacity: 0.75;
stroke: rgba(0, 0, 0, 0.35);
stroke-width: 0.4;
}
.seg {
stroke: var(--hp-accent);
stroke-width: 2.5;
stroke-linecap: round;
}
.pathline {
stroke: #ffc14d;
stroke-width: 3;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
.preview {
stroke: #ffc14d;
stroke-width: 2;
stroke-dasharray: 6 5;
opacity: 0.7;
}
.vertex {
fill: #ffc14d;
stroke: #4a2800;
stroke-width: 1;
}
.vertex.first {
fill: #4bd28f;
stroke: #04121f;
}
.areasel,
.namein {
background: var(--hp-bg);
border: 1px solid var(--hp-line);
color: var(--hp-txt);
border-radius: 6px;
padding: 6px 8px;
font-size: 13px;
font-family: inherit;
}
.namein {
width: 130px;
}
.devlayer {
position: absolute;
inset: 0;
pointer-events: none;
}
.dev {
position: absolute;
width: var(--icon-size, 2.5cqw);
height: var(--icon-size, 2.5cqw);
margin: calc(var(--icon-size, 2.5cqw) / -2) 0 0 calc(var(--icon-size, 2.5cqw) / -2);
border-radius: 22%;
background: var(--hp-bg);
border: 1px solid var(--hp-line);
display: flex;
align-items: center;
justify-content: center;
color: var(--hp-txt);
cursor: pointer;
pointer-events: auto;
transition: background 0.15s, border-color 0.15s, opacity 0.2s;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
z-index: 2;
}
.dev ha-icon {
--mdc-icon-size: calc(var(--icon-size, 2.5cqw) * 0.62);
}
.dev:hover {
background: var(--hp-accent);
color: var(--text-primary-color, #fff);
z-index: 5;
}
.dev.on {
background: var(--hp-on);
border-color: var(--hp-on);
color: #503c00;
box-shadow: 0 0 8px rgba(255, 212, 92, 0.7);
}
.dev.open {
background: var(--hp-open);
border-color: var(--hp-open);
color: #4a2800;
}
.dev.unavail {
opacity: 0.35;
}
.dev.virtual {
border-style: dashed;
}
.dev.sel {
border-color: #ffc14d;
box-shadow: 0 0 0 3px rgba(255, 193, 77, 0.35);
}
.stage.edit .dev {
cursor: grab;
}
.dev .tval {
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
margin-left: calc(var(--icon-size, 2.5cqw) * 0.1);
background: rgba(4, 18, 31, 0.9);
border: 1px solid var(--hp-accent);
border-radius: calc(var(--icon-size, 2.5cqw) * 0.18);
padding: 0 calc(var(--icon-size, 2.5cqw) * 0.14);
font-size: calc(var(--icon-size, 2.5cqw) * 0.45);
font-weight: 700;
line-height: calc(var(--icon-size, 2.5cqw) * 0.68);
color: #dff1ff;
white-space: nowrap;
pointer-events: none;
}
.dev .lqi {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
margin-top: calc(var(--icon-size, 2.5cqw) * 0.05);
font-size: calc(var(--icon-size, 2.5cqw) * 0.38);
font-weight: 700;
line-height: 1;
text-shadow: 0 0 3px rgba(0, 0, 0, 0.9), 0 0 2px rgba(0, 0, 0, 0.9);
white-space: nowrap;
pointer-events: none;
}
.editbar {
display: flex;
align-items: center;
gap: 10px;
padding: 9px 14px;
border-bottom: 1px solid var(--hp-line);
font-size: 13px;
flex-wrap: wrap;
}
.tab .tabedit {
--mdc-icon-size: 13px;
margin-left: 6px;
opacity: 0.4;
vertical-align: middle;
}
.tab:hover .tabedit {
opacity: 0.9;
}
.tab.tabadd {
padding: 6px 8px;
}
.tab.tabadd ha-icon {
--mdc-icon-size: 15px;
}
.planrow {
display: flex;
align-items: center;
gap: 10px;
}
.planprev {
max-width: 120px;
max-height: 70px;
border: 1px solid var(--hp-line);
border-radius: 6px;
background: #fff;
}
.planname {
font-size: 12.5px;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.planname.muted {
color: var(--hp-muted);
}
.filebtn {
cursor: pointer;
}
.btn.danger {
border-color: #b3402a;
color: #ff7a5c;
}
.dialog .row .spacer {
flex: 1;
}
.dialog.wide {
width: min(440px, 94vw);
}
.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: 6px;
padding: 6px 8px;
font-size: 13px;
font-family: inherit;
resize: vertical;
}
.bindsel {
display: flex;
flex-direction: column;
gap: 6px;
border: 1px solid var(--hp-line);
border-radius: 8px;
padding: 8px;
}
.bindsel .opt {
display: flex;
align-items: center;
gap: 6px;
border: 1px solid var(--hp-line);
background: transparent;
color: var(--hp-txt);
border-radius: 6px;
padding: 6px 8px;
cursor: pointer;
font-size: 12.5px;
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: 6px;
font-size: 12.5px;
color: var(--hp-txt);
flex-wrap: wrap;
}
.curbind .ref {
color: var(--hp-muted);
font-size: 11px;
}
.candlist {
max-height: 160px;
overflow-y: auto;
border-top: 1px solid var(--hp-line);
}
.cand {
display: flex;
justify-content: space-between;
gap: 8px;
padding: 6px 8px;
cursor: pointer;
border-radius: 6px;
font-size: 12.5px;
}
.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: 11px;
white-space: nowrap;
}
.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: 6px;
align-items: center;
}
.pdftag {
display: inline-flex;
align-items: center;
gap: 4px;
border: 1px solid var(--hp-line);
border-radius: 6px;
padding: 3px 6px;
font-size: 12px;
}
.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);
}
.pdftag .x:hover {
color: #ff7a5c;
}
.inforow {
display: flex;
gap: 10px;
font-size: 13px;
margin: 3px 0;
}
.inforow .k {
color: var(--hp-muted);
min-width: 84px;
}
.inforow a {
color: var(--hp-accent);
word-break: break-all;
}
.infodesc {
font-size: 13px;
white-space: pre-wrap;
margin-top: 6px;
}
.infodesc.muted {
color: var(--hp-muted);
}
.pdflist {
display: flex;
flex-direction: column;
gap: 4px;
}
.pdf {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--hp-accent);
text-decoration: none;
}
ha-icon-picker {
display: block;
}
.dialogwrap {
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
z-index: 90;
}
.dialog {
background: var(--card-background-color, var(--hp-bg));
border: 1px solid var(--hp-accent);
border-radius: 14px;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5);
width: min(360px, 92vw);
overflow: hidden;
}
.dialog .hd {
padding: 12px 16px;
font-weight: 600;
border-bottom: 1px solid var(--hp-line);
display: flex;
align-items: center;
gap: 8px;
}
.dialog .hd ha-icon {
color: var(--hp-accent);
}
.dialog .body {
padding: 14px 16px;
display: flex;
flex-direction: column;
gap: 6px;
}
.dialog .body label {
font-size: 12px;
color: var(--hp-muted);
margin-top: 6px;
}
.dialog .body .namein,
.dialog .body .areasel {
width: 100%;
box-sizing: border-box;
}
.dialog .row {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid var(--hp-line);
}
.editbar .warn {
color: #ffc14d;
}
.editbar .sname {
font-weight: 600;
}
.editbar input {
width: 74px;
background: transparent;
border: 1px solid var(--hp-line);
color: var(--hp-txt);
border-radius: 6px;
padding: 5px 7px;
font-size: 13px;
}
.editbar label,
.editbar .hint {
color: var(--hp-muted);
font-size: 12px;
}
.menuwrap {
position: fixed;
inset: 0;
z-index: 80;
}
.menu {
position: fixed;
background: var(--hp-bg);
border: 1px solid var(--hp-accent);
border-radius: 10px;
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
min-width: 210px;
max-width: 300px;
overflow: hidden;
transform: translate(0, 8px);
}
.menu .hd {
padding: 8px 12px;
font-weight: 600;
font-size: 12.5px;
border-bottom: 1px solid var(--hp-line);
display: flex;
align-items: center;
gap: 6px;
}
.menu .hd ha-icon,
.menu .it.all ha-icon {
color: var(--hp-accent);
--mdc-icon-size: 16px;
}
.menu .it {
padding: 8px 12px;
font-size: 12.5px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.menu .it ha-icon {
--mdc-icon-size: 16px;
color: var(--hp-muted);
}
.menu .it:hover {
background: rgba(127, 127, 127, 0.15);
}
.menu .it.all {
color: var(--hp-accent);
font-weight: 600;
}
.tip {
position: fixed;
pointer-events: none;
background: var(--hp-bg);
border: 1px solid var(--hp-accent);
color: var(--hp-txt);
padding: 6px 10px;
border-radius: 8px;
font-size: 12.5px;
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
z-index: 99;
max-width: 260px;
}
.tip .m {
color: var(--hp-muted);
font-size: 11px;
display: block;
}
.toast {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
background: var(--hp-bg);
border: 1px solid var(--hp-accent);
color: var(--hp-txt);
padding: 9px 16px;
border-radius: 10px;
font-size: 13px;
box-shadow: 0 6px 22px rgba(0, 0, 0, 0.45);
z-index: 120;
max-width: 90vw;
}
`,customElements.get("houseplan-card")||customElements.define("houseplan-card",LI),window.customCards=window.customCards||[],window.customCards.find(I=>"houseplan-card"===I.type)||window.customCards.push({type:"houseplan-card",name:"House Plan Card",description:"Интерактивный план дома: пространства, комнаты, устройства с живыми состояниями и drag-раскладкой."}),console.info("%c HOUSEPLAN-CARD %c v1.6.1 ","background:#3ea6ff;color:#04121f;font-weight:700","");