/******************************************************************************
Copyright (c) Microsoft Corporation.

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */


function __decorate(decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
}

typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
    var e = new Error(message);
    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};

/**
 * @license
 * Copyright 2019 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */
const t$2=globalThis,e$2=t$2.ShadowRoot&&(void 0===t$2.ShadyCSS||t$2.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$2=Symbol(),o$4=new WeakMap;let n$3 = class n{constructor(t,e,o){if(this._$cssResult$=true,o!==s$2)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$2&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=o$4.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&o$4.set(s,t));}return t}toString(){return this.cssText}};const r$4=t=>new n$3("string"==typeof t?t:t+"",void 0,s$2),i$3=(t,...e)=>{const o=1===t.length?t[0]:e.reduce((e,s,o)=>e+(t=>{if(true===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.")})(s)+t[o+1],t[0]);return new n$3(o,t,s$2)},S$1=(s,o)=>{if(e$2)s.adoptedStyleSheets=o.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const e of o){const o=document.createElement("style"),n=t$2.litNonce;void 0!==n&&o.setAttribute("nonce",n),o.textContent=e.cssText,s.appendChild(o);}},c$2=e$2?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$4(e)})(t):t;

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */const{is:i$2,defineProperty:e$1,getOwnPropertyDescriptor:h$1,getOwnPropertyNames:r$3,getOwnPropertySymbols:o$3,getPrototypeOf:n$2}=Object,a$1=globalThis,c$1=a$1.trustedTypes,l$1=c$1?c$1.emptyScript:"",p$1=a$1.reactiveElementPolyfillSupport,d$1=(t,s)=>t,u$1={toAttribute(t,s){switch(s){case Boolean:t=t?l$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,s){let i=t;switch(s){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}},f$1=(t,s)=>!i$2(t,s),b$1={attribute:true,type:String,converter:u$1,reflect:false,useDefault:false,hasChanged:f$1};Symbol.metadata??=Symbol("metadata"),a$1.litPropertyMetadata??=new WeakMap;let y$1 = class y 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,s=b$1){if(s.state&&(s.attribute=false),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=true),this.elementProperties.set(t,s),!s.noAccessor){const i=Symbol(),h=this.getPropertyDescriptor(t,i,s);void 0!==h&&e$1(this.prototype,t,h);}}static getPropertyDescriptor(t,s,i){const{get:e,set:r}=h$1(this.prototype,t)??{get(){return this[s]},set(t){this[s]=t;}};return {get:e,set(s){const h=e?.call(this);r?.call(this,s),this.requestUpdate(t,h,i);},configurable:true,enumerable:true}}static getPropertyOptions(t){return this.elementProperties.get(t)??b$1}static _$Ei(){if(this.hasOwnProperty(d$1("elementProperties")))return;const t=n$2(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties);}static finalize(){if(this.hasOwnProperty(d$1("finalized")))return;if(this.finalized=true,this._$Ei(),this.hasOwnProperty(d$1("properties"))){const t=this.properties,s=[...r$3(t),...o$3(t)];for(const i of s)this.createProperty(i,t[i]);}const t=this[Symbol.metadata];if(null!==t){const s=litPropertyMetadata.get(t);if(void 0!==s)for(const[t,i]of s)this.elementProperties.set(t,i);}this._$Eh=new Map;for(const[t,s]of this.elementProperties){const i=this._$Eu(t,s);void 0!==i&&this._$Eh.set(i,t);}this.elementStyles=this.finalizeStyles(this.styles);}static finalizeStyles(s){const i=[];if(Array.isArray(s)){const e=new Set(s.flat(1/0).reverse());for(const s of e)i.unshift(c$2(s));}else void 0!==s&&i.push(c$2(s));return i}static _$Eu(t,s){const i=s.attribute;return  false===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=false,this.hasUpdated=false,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,s=this.constructor.elementProperties;for(const i of s.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t);}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return S$1(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(true),this._$EO?.forEach(t=>t.hostConnected?.());}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.());}attributeChangedCallback(t,s,i){this._$AK(t,i);}_$ET(t,s){const i=this.constructor.elementProperties.get(t),e=this.constructor._$Eu(t,i);if(void 0!==e&&true===i.reflect){const h=(void 0!==i.converter?.toAttribute?i.converter:u$1).toAttribute(s,i.type);this._$Em=t,null==h?this.removeAttribute(e):this.setAttribute(e,h),this._$Em=null;}}_$AK(t,s){const i=this.constructor,e=i._$Eh.get(t);if(void 0!==e&&this._$Em!==e){const t=i.getPropertyOptions(e),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:u$1;this._$Em=e;const r=h.fromAttribute(s,t.type);this[e]=r??this._$Ej?.get(e)??r,this._$Em=null;}}requestUpdate(t,s,i,e=false,h){if(void 0!==t){const r=this.constructor;if(false===e&&(h=this[t]),i??=r.getPropertyOptions(t),!((i.hasChanged??f$1)(h,s)||i.useDefault&&i.reflect&&h===this._$Ej?.get(t)&&!this.hasAttribute(r._$Eu(t,i))))return;this.C(t,s,i);} false===this.isUpdatePending&&(this._$ES=this._$EP());}C(t,s,{useDefault:i,reflect:e,wrapped:h},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??s??this[t]),true!==h||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(s=void 0),this._$AL.set(t,s)),true===e&&this._$Em!==t&&(this._$Eq??=new Set).add(t));}async _$EP(){this.isUpdatePending=true;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,s]of this._$Ep)this[t]=s;this._$Ep=void 0;}const t=this.constructor.elementProperties;if(t.size>0)for(const[s,i]of t){const{wrapped:t}=i,e=this[s];true!==t||this._$AL.has(s)||void 0===e||this.C(s,void 0,i,e);}}let t=false;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(s)):this._$EM();}catch(s){throw t=false,this._$EM(),s}t&&this._$AE(s);}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=true,this.firstUpdated(t)),this.updated(t);}_$EM(){this._$AL=new Map,this.isUpdatePending=false;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return  true}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM();}updated(t){}firstUpdated(t){}};y$1.elementStyles=[],y$1.shadowRootOptions={mode:"open"},y$1[d$1("elementProperties")]=new Map,y$1[d$1("finalized")]=new Map,p$1?.({ReactiveElement:y$1}),(a$1.reactiveElementVersions??=[]).push("2.1.2");

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */
const t$1=globalThis,i$1=t=>t,s$1=t$1.trustedTypes,e=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,h="$lit$",o$2=`lit$${Math.random().toFixed(9).slice(2)}$`,n$1="?"+o$2,r$2=`<${n$1}>`,l=document,c=()=>l.createComment(""),a=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,d=t=>u(t)||"function"==typeof t?.[Symbol.iterator],f="[ \t\n\f\r]",v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_=/-->/g,m=/>/g,p=RegExp(`>|${f}(?:([^\\s"'>=/]+)(${f}*=${f}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),g=/'/g,$=/"/g,y=/^(?:script|style|textarea|title)$/i,x=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),b=x(1),E=Symbol.for("lit-noChange"),A=Symbol.for("lit-nothing"),C=new WeakMap,P=l.createTreeWalker(l,129);function V(t,i){if(!u(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==e?e.createHTML(i):i}const N=(t,i)=>{const s=t.length-1,e=[];let n,l=2===i?"<svg>":3===i?"<math>":"",c=v;for(let i=0;i<s;i++){const s=t[i];let a,u,d=-1,f=0;for(;f<s.length&&(c.lastIndex=f,u=c.exec(s),null!==u);)f=c.lastIndex,c===v?"!--"===u[1]?c=_:void 0!==u[1]?c=m:void 0!==u[2]?(y.test(u[2])&&(n=RegExp("</"+u[2],"g")),c=p):void 0!==u[3]&&(c=p):c===p?">"===u[0]?(c=n??v,d=-1):void 0===u[1]?d=-2:(d=c.lastIndex-u[2].length,a=u[1],c=void 0===u[3]?p:'"'===u[3]?$:g):c===$||c===g?c=p:c===_||c===m?c=v:(c=p,n=void 0);const x=c===p&&t[i+1].startsWith("/>")?" ":"";l+=c===v?s+r$2:d>=0?(e.push(a),s.slice(0,d)+h+s.slice(d)+o$2+x):s+o$2+(-2===d?i:x);}return [V(t,l+(t[s]||"<?>")+(2===i?"</svg>":3===i?"</math>":"")),e]};class S{constructor({strings:t,_$litType$:i},e){let r;this.parts=[];let l=0,a=0;const u=t.length-1,d=this.parts,[f,v]=N(t,i);if(this.el=S.createElement(f,e),P.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes);}for(;null!==(r=P.nextNode())&&d.length<u;){if(1===r.nodeType){if(r.hasAttributes())for(const t of r.getAttributeNames())if(t.endsWith(h)){const i=v[a++],s=r.getAttribute(t).split(o$2),e=/([.?@])?(.*)/.exec(i);d.push({type:1,index:l,name:e[2],strings:s,ctor:"."===e[1]?I:"?"===e[1]?L:"@"===e[1]?z:H}),r.removeAttribute(t);}else t.startsWith(o$2)&&(d.push({type:6,index:l}),r.removeAttribute(t));if(y.test(r.tagName)){const t=r.textContent.split(o$2),i=t.length-1;if(i>0){r.textContent=s$1?s$1.emptyScript:"";for(let s=0;s<i;s++)r.append(t[s],c()),P.nextNode(),d.push({type:2,index:++l});r.append(t[i],c());}}}else if(8===r.nodeType)if(r.data===n$1)d.push({type:2,index:l});else {let t=-1;for(;-1!==(t=r.data.indexOf(o$2,t+1));)d.push({type:7,index:l}),t+=o$2.length-1;}l++;}}static createElement(t,i){const s=l.createElement("template");return s.innerHTML=t,s}}function M(t,i,s=t,e){if(i===E)return i;let h=void 0!==e?s._$Co?.[e]:s._$Cl;const o=a(i)?void 0:i._$litDirective$;return h?.constructor!==o&&(h?._$AO?.(false),void 0===o?h=void 0:(h=new o(t),h._$AT(t,s,e)),void 0!==e?(s._$Co??=[])[e]=h:s._$Cl=h),void 0!==h&&(i=M(t,h._$AS(t,i.values),h,e)),i}class R{constructor(t,i){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=i;}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){const{el:{content:i},parts:s}=this._$AD,e=(t?.creationScope??l).importNode(i,true);P.currentNode=e;let h=P.nextNode(),o=0,n=0,r=s[0];for(;void 0!==r;){if(o===r.index){let i;2===r.type?i=new k(h,h.nextSibling,this,t):1===r.type?i=new r.ctor(h,r.name,r.strings,this,t):6===r.type&&(i=new Z(h,this,t)),this._$AV.push(i),r=s[++n];}o!==r?.index&&(h=P.nextNode(),o++);}return P.currentNode=l,e}p(t){let i=0;for(const s of this._$AV) void 0!==s&&(void 0!==s.strings?(s._$AI(t,s,i),i+=s.strings.length-2):s._$AI(t[i])),i++;}}class k{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,i,s,e){this.type=2,this._$AH=A,this._$AN=void 0,this._$AA=t,this._$AB=i,this._$AM=s,this.options=e,this._$Cv=e?.isConnected??true;}get parentNode(){let t=this._$AA.parentNode;const i=this._$AM;return void 0!==i&&11===t?.nodeType&&(t=i.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,i=this){t=M(this,t,i),a(t)?t===A||null==t||""===t?(this._$AH!==A&&this._$AR(),this._$AH=A):t!==this._$AH&&t!==E&&this._(t):void 0!==t._$litType$?this.$(t):void 0!==t.nodeType?this.T(t):d(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!==A&&a(this._$AH)?this._$AA.nextSibling.data=t:this.T(l.createTextNode(t)),this._$AH=t;}$(t){const{values:i,_$litType$:s}=t,e="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=S.createElement(V(s.h,s.h[0]),this.options)),s);if(this._$AH?._$AD===e)this._$AH.p(i);else {const t=new R(e,this),s=t.u(this.options);t.p(i),this.T(s),this._$AH=t;}}_$AC(t){let i=C.get(t.strings);return void 0===i&&C.set(t.strings,i=new S(t)),i}k(t){u(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,e=0;for(const h of t)e===i.length?i.push(s=new k(this.O(c()),this.O(c()),this,this.options)):s=i[e],s._$AI(h),e++;e<i.length&&(this._$AR(s&&s._$AB.nextSibling,e),i.length=e);}_$AR(t=this._$AA.nextSibling,s){for(this._$AP?.(false,true,s);t!==this._$AB;){const s=i$1(t).nextSibling;i$1(t).remove(),t=s;}}setConnected(t){ void 0===this._$AM&&(this._$Cv=t,this._$AP?.(t));}}class H{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,i,s,e,h){this.type=1,this._$AH=A,this._$AN=void 0,this.element=t,this.name=i,this._$AM=e,this.options=h,s.length>2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=A;}_$AI(t,i=this,s,e){const h=this.strings;let o=false;if(void 0===h)t=M(this,t,i,0),o=!a(t)||t!==this._$AH&&t!==E,o&&(this._$AH=t);else {const e=t;let n,r;for(t=h[0],n=0;n<h.length-1;n++)r=M(this,e[s+n],i,n),r===E&&(r=this._$AH[n]),o||=!a(r)||r!==this._$AH[n],r===A?t=A:t!==A&&(t+=(r??"")+h[n+1]),this._$AH[n]=r;}o&&!e&&this.j(t);}j(t){t===A?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"");}}class I extends H{constructor(){super(...arguments),this.type=3;}j(t){this.element[this.name]=t===A?void 0:t;}}class L extends H{constructor(){super(...arguments),this.type=4;}j(t){this.element.toggleAttribute(this.name,!!t&&t!==A);}}class z extends H{constructor(t,i,s,e,h){super(t,i,s,e,h),this.type=5;}_$AI(t,i=this){if((t=M(this,t,i,0)??A)===E)return;const s=this._$AH,e=t===A&&s!==A||t.capture!==s.capture||t.once!==s.once||t.passive!==s.passive,h=t!==A&&(s===A||e);e&&this.element.removeEventListener(this.name,this,s),h&&this.element.addEventListener(this.name,this,t),this._$AH=t;}handleEvent(t){"function"==typeof this._$AH?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t);}}class Z{constructor(t,i,s){this.element=t,this.type=6,this._$AN=void 0,this._$AM=i,this.options=s;}get _$AU(){return this._$AM._$AU}_$AI(t){M(this,t);}}const B=t$1.litHtmlPolyfillSupport;B?.(S,k),(t$1.litHtmlVersions??=[]).push("3.3.2");const D=(t,i,s)=>{const e=s?.renderBefore??i;let h=e._$litPart$;if(void 0===h){const t=s?.renderBefore??null;e._$litPart$=h=new k(i.insertBefore(c(),t),t,void 0,s??{});}return h._$AI(t),h};

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */const s=globalThis;class i extends y$1{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 r=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=D(r,this.renderRoot,this.renderOptions);}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(true);}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(false);}render(){return E}}i._$litElement$=true,i["finalized"]=true,s.litElementHydrateSupport?.({LitElement:i});const o$1=s.litElementPolyfillSupport;o$1?.({LitElement:i});(s.litElementVersions??=[]).push("4.2.2");

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */
const t=t=>(e,o)=>{ void 0!==o?o.addInitializer(()=>{customElements.define(t,e);}):customElements.define(t,e);};

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */const o={attribute:true,type:String,converter:u$1,reflect:false,hasChanged:f$1},r$1=(t=o,e,r)=>{const{kind:n,metadata:i}=r;let s=globalThis.litPropertyMetadata.get(i);if(void 0===s&&globalThis.litPropertyMetadata.set(i,s=new Map),"setter"===n&&((t=Object.create(t)).wrapped=true),s.set(r.name,t),"accessor"===n){const{name:o}=r;return {set(r){const n=e.get.call(this);e.set.call(this,r),this.requestUpdate(o,n,t,true,r);},init(e){return void 0!==e&&this.C(o,void 0,t,e),e}}}if("setter"===n){const{name:o}=r;return function(r){const n=this[o];e.call(this,r),this.requestUpdate(o,n,t,true,r);}}throw Error("Unsupported decorator location: "+n)};function n(t){return (e,o)=>"object"==typeof o?r$1(t,e,o):((t,e,o)=>{const r=e.hasOwnProperty(o);return e.constructor.createProperty(o,t),r?Object.getOwnPropertyDescriptor(e,o):void 0})(t,e,o)}

/**
 * @license
 * Copyright 2017 Google LLC
 * SPDX-License-Identifier: BSD-3-Clause
 */function r(r){return n({...r,state:true,attribute:false})}

// Color palette for automatic background graph coloring
const BACKGROUND_GRAPH_COLORS = [
    '#f44336', // Red
    '#9c27b0', // Purple
    '#3f51b5', // Indigo
    '#00bcd4', // Cyan
    '#009688', // Teal
    '#4caf50', // Green
    '#8bc34a', // Light Green
    '#cddc39', // Lime
    '#ffeb3b', // Yellow
    '#ffc107', // Amber
    '#ff9800', // Orange
    '#ff5722', // Deep Orange
    '#795548', // Brown
    '#607d8b', // Blue Grey
];
let KeyframeTimeline = class KeyframeTimeline extends i {
    constructor() {
        super(...arguments);
        this.duration = 24; // hours
        this.slots = 96; // time divisions (e.g., 15-min intervals in 24h)
        this.keyframes = [];
        this.minValue = 5; // Minimum Y axis value (default: 5°C)
        this.maxValue = 30; // Maximum Y axis value (default: 30°C)
        this.snapValue = 0; // Y axis snap step (0 = no snapping)
        this.xAxisLabel = ''; // X axis label
        this.yAxisLabel = ''; // Y axis label
        this.title = ''; // Title displayed in top left
        this.showHeader = true; // Show header with controls
        this.allowCollapse = true; // Allow collapsing the timeline
        this.readonly = false; // Disable all interactions
        this.showCurrentTime = false; // Automatically show indicator at current time
        this.backgroundGraphs = []; // Background reference graphs
        this.advanceHistory = []; // Array of {activated_at, target_time, cancelled_at, target_node}
        this.tooltipMode = 'cursor'; // Tooltip display mode (set from global settings)
        this.canvasWidth = 0;
        this.canvasHeight = 600;
        this.showConfig = false;
        this.draggingIndex = null;
        this.draggingSegment = null;
        this.selectedKeyframeIndex = null;
        this.collapsed = false;
        this.showScrollNavLeft = false;
        this.showScrollNavRight = false;
        this.undoStack = []; // Changed to store full keyframe arrays
        this.legendCollapsed = true;
        this.isDragging = false;
        this.hasMoved = false;
        this.dragStartX = 0;
        this.dragStartY = 0;
        this.isPanning = false;
        this.panStartX = 0;
        this.panStartScrollLeft = 0;
        this.lastClickTime = 0;
        this.lastClickIndex = -1; // Track which keyframe was last clicked
        this.lastClickX = 0;
        this.instanceId = Math.random().toString(36).substring(7); // Unique instance ID for debugging
        this.lastClickY = 0;
        this.justDeletedKeyframe = false; // Prevent dblclick from adding after delete
        this.holdStartX = 0;
        this.holdStartY = 0;
        this.hoverRenderPending = false;
        this.hoverX = 0;
        this.hoverY = 0;
    }
    willUpdate(changedProperties) {
        // Property change tracking
    }
    firstUpdated() {
        const canvasEl = this.shadowRoot?.querySelector('canvas');
        if (canvasEl) {
            this.canvas = canvasEl;
            this.ctx = canvasEl.getContext('2d') || undefined;
            this.updateCanvasSize();
        }
        this.tooltipEl = this.shadowRoot?.querySelector('.cursor-tooltip');
        this.wrapperEl = this.shadowRoot?.querySelector('.timeline-canvas-wrapper');
        this.checkScrollVisibility();
        // Listen for scroll events to update button visibility
        if (this.wrapperEl) {
            this.wrapperEl.addEventListener('scroll', () => this.checkScrollVisibility());
        }
        // Update canvas size on window resize
        window.addEventListener('resize', () => {
            this.updateCanvasSize();
            this.drawTimeline();
            this.checkScrollVisibility();
        });
        // Draw immediately - CSS variables should now be properly set via styles.css
        requestAnimationFrame(() => {
            this.updateCanvasSize();
            this.drawTimeline();
        });
        // Setup current time indicator if enabled
        if (this.showCurrentTime) {
            this.updateCurrentTime();
            this.startCurrentTimeTimer();
        }
        // Setup keyboard shortcuts
        this.keyboardHandler = (e) => {
            if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
                e.preventDefault();
                this.undo();
            }
        };
        document.addEventListener('keydown', this.keyboardHandler);
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        this.stopCurrentTimeTimer();
        // Remove keyboard listener
        if (this.keyboardHandler) {
            document.removeEventListener('keydown', this.keyboardHandler);
        }
    }
    updated(changedProperties) {
        super.updated(changedProperties);
        // Redraw when keyframes, backgroundGraphs or advanceHistory changes
        if (changedProperties.has('keyframes')) {
            this.drawTimeline();
            this.updateNavigationButtonsState();
        }
        // Redraw when backgroundGraphs or advanceHistory changes
        if (changedProperties.has('backgroundGraphs') || changedProperties.has('advanceHistory')) {
            this.drawTimeline();
        }
        // Start/stop timer when showCurrentTime changes
        if (changedProperties.has('showCurrentTime')) {
            if (this.showCurrentTime) {
                this.updateCurrentTime();
                this.startCurrentTimeTimer();
            }
            else {
                this.stopCurrentTimeTimer();
            }
        }
    }
    updateCurrentTime() {
        const now = new Date();
        const hours = now.getHours();
        const minutes = now.getMinutes();
        const seconds = now.getSeconds();
        // Calculate time as decimal hours
        const currentTime = hours + (minutes / 60) + (seconds / 3600);
        // Only update if within the timeline duration
        if (currentTime <= this.duration) {
            this.indicatorTime = currentTime;
            this.drawTimeline();
        }
    }
    startCurrentTimeTimer() {
        // Update every 10 seconds for smooth movement
        this.currentTimeTimer = window.setInterval(() => {
            this.updateCurrentTime();
        }, 10000);
    }
    stopCurrentTimeTimer() {
        if (this.currentTimeTimer) {
            window.clearInterval(this.currentTimeTimer);
            this.currentTimeTimer = undefined;
        }
    }
    updateCanvasSize() {
        if (!this.canvas)
            return;
        // Use offsetWidth to get actual canvas width (not clipped by scroll container)
        const canvasCSSWidth = this.canvas.offsetWidth;
        this.canvasWidth = canvasCSSWidth * window.devicePixelRatio;
        // Read height from CSS variables
        const computedStyle = getComputedStyle(this);
        const cssHeightVar = this.collapsed ? '--timeline-height-collapsed' : '--timeline-height';
        const cssHeight = computedStyle.getPropertyValue(cssHeightVar).trim();
        const baseHeight = parseInt(cssHeight);
        this.canvasHeight = baseHeight * window.devicePixelRatio;
        this.canvas.width = this.canvasWidth;
        this.canvas.height = this.canvasHeight;
    }
    normalizeValue(value) {
        // Convert value from minValue-maxValue range to 0-1 range
        return (value - this.minValue) / (this.maxValue - this.minValue);
    }
    denormalizeValue(normalized) {
        // Convert value from 0-1 range to minValue-maxValue range
        return normalized * (this.maxValue - this.minValue) + this.minValue;
    }
    getThemeColor(cssVar) {
        return getComputedStyle(this).getPropertyValue(cssVar).trim();
    }
    getBackgroundGraphColor(bgGraph, graphIndex) {
        return bgGraph.color || BACKGROUND_GRAPH_COLORS[graphIndex % BACKGROUND_GRAPH_COLORS.length];
    }
    toggleLegend(e) {
        e.stopPropagation();
        this.legendCollapsed = !this.legendCollapsed;
    }
    getBaseFontSize() {
        // Get computed font size from host element to respect browser/accessibility settings
        const computedStyle = getComputedStyle(this);
        return parseFloat(computedStyle.fontSize);
    }
    snapValueToGrid(value) {
        // Snap value to grid if snapValue is set
        if (this.snapValue > 0) {
            const snapped = Math.round(value / this.snapValue) * this.snapValue;
            return this.roundToStepPrecision(snapped, this.snapValue);
        }
        return this.roundToPrecision(value, 4);
    }
    roundToStepPrecision(value, step) {
        return this.roundToPrecision(value, this.getStepPrecision(step));
    }
    roundToPrecision(value, precision) {
        const safePrecision = Math.max(0, Math.min(precision, 10));
        const factor = 10 ** safePrecision;
        return Math.round((value + Number.EPSILON) * factor) / factor;
    }
    getStepPrecision(step) {
        if (!Number.isFinite(step) || step <= 0) {
            return 3;
        }
        const stepString = step.toString().toLowerCase();
        if (stepString.includes('e-')) {
            const exponent = Number(stepString.split('e-')[1]);
            return Number.isFinite(exponent) ? exponent : 3;
        }
        const decimalPart = stepString.split('.')[1];
        return decimalPart ? decimalPart.length : 0;
    }
    getGraphDimensions(rect) {
        const labelHeight = 30;
        const leftMargin = 35;
        const yAxisWidth = 35;
        const rightMargin = 35;
        const topMargin = 25;
        const bottomMargin = 25;
        const graphHeight = rect.height - labelHeight - topMargin - bottomMargin;
        // Use canvas width (not rect width which is clipped by scroll container)
        const canvasWidthCSS = this.canvas.width / (window.devicePixelRatio || 1);
        const graphWidth = canvasWidthCSS - leftMargin - yAxisWidth - rightMargin;
        return {
            labelHeight,
            leftMargin,
            yAxisWidth,
            rightMargin,
            topMargin,
            bottomMargin,
            graphHeight,
            graphWidth,
            canvasWidthCSS
        };
    }
    sortKeyframes() {
        // Keep track of what was selected/dragging before sort
        const selectedKeyframe = this.selectedKeyframeIndex !== null ? this.keyframes[this.selectedKeyframeIndex] : null;
        const draggingKeyframe = this.draggingIndex !== null ? this.keyframes[this.draggingIndex] : null;
        // Sort keyframes by time
        this.keyframes = [...this.keyframes].sort((a, b) => a.time - b.time);
        // Update indices to point to same keyframes after sort
        if (selectedKeyframe) {
            this.selectedKeyframeIndex = this.keyframes.findIndex(kf => kf === selectedKeyframe);
        }
        if (draggingKeyframe) {
            this.draggingIndex = this.keyframes.findIndex(kf => kf === draggingKeyframe);
        }
    }
    drawTimeline() {
        if (!this.ctx || !this.canvas)
            return;
        this.canvas.getBoundingClientRect();
        const dpr = window.devicePixelRatio;
        this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
        this.canvasWidth / this.slots;
        // Reserve space at bottom for labels, left for Y axis, right margin, and top for margin
        const labelHeight = 30 * dpr;
        const leftMargin = 35 * dpr;
        const yAxisWidth = 35 * dpr;
        const rightMargin = 35 * dpr;
        const topMargin = 45 * dpr;
        const bottomMargin = 25 * dpr;
        const graphHeight = this.canvasHeight - labelHeight - topMargin - bottomMargin;
        const graphWidth = this.canvasWidth - leftMargin - yAxisWidth - rightMargin;
        // Draw Y axis labels and horizontal grid lines
        const numYLabels = this.collapsed ? 2 : 5;
        const baseFontSize = this.getBaseFontSize();
        const yLabelOffset = 5 * dpr; // Distance from Y-axis to label text
        const yAxisLabelOffset = -10 * dpr; // Distance from left edge to vertical Y-axis label
        const xAxisLabelOffset = 25 * dpr; // Distance from bottom to horizontal X-axis label
        this.ctx.fillStyle = this.getThemeColor('--canvas-text-secondary');
        this.ctx.font = `${baseFontSize * dpr}px sans-serif`;
        this.ctx.textAlign = 'right';
        this.ctx.textBaseline = 'middle';
        this.ctx.strokeStyle = this.getThemeColor('--canvas-grid-line');
        this.ctx.lineWidth = 1 * dpr;
        for (let i = 0; i < numYLabels; i++) {
            const ratio = i / (numYLabels - 1);
            const value = this.minValue + ratio * (this.maxValue - this.minValue);
            const y = topMargin + (graphHeight * (1 - ratio));
            // Draw label
            this.ctx.fillText(value.toFixed(1), leftMargin + yAxisWidth - yLabelOffset, y);
            // Draw horizontal grid line
            this.ctx.beginPath();
            this.ctx.moveTo(leftMargin + yAxisWidth, y);
            this.ctx.lineTo(leftMargin + yAxisWidth + graphWidth, y);
            this.ctx.stroke();
        }
        // Draw Y axis label (vertical text on left side)
        if (this.yAxisLabel && !this.collapsed) {
            this.ctx.save();
            this.ctx.translate(leftMargin + yAxisLabelOffset, this.canvasHeight / 2);
            this.ctx.rotate(-Math.PI / 2);
            this.ctx.fillStyle = this.getThemeColor('--canvas-text-primary');
            this.ctx.font = `${baseFontSize * dpr}px sans-serif`;
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.fillText(this.yAxisLabel, 0, 0);
            this.ctx.restore();
        }
        // Adjust slot width for graph area
        graphWidth / this.slots;
        // Draw hour markers (full height) and labels
        this.ctx.fillStyle = this.getThemeColor('--canvas-text-secondary');
        this.ctx.font = `${baseFontSize * dpr}px sans-serif`;
        this.ctx.textAlign = 'center';
        this.ctx.textBaseline = 'middle';
        const hoursToShow = Math.ceil(this.duration);
        const labelInterval = this.collapsed ? 3 : 1; // Show labels every 3 hours when collapsed
        for (let i = 0; i <= hoursToShow; i++) {
            const x = leftMargin + yAxisWidth + ((i / this.duration) * graphWidth);
            // Draw hour marker line with thicker lines every 6 hours
            const isMajorLine = i % 6 === 0;
            this.ctx.strokeStyle = isMajorLine
                ? this.getThemeColor('--canvas-grid-line-major')
                : this.getThemeColor('--canvas-grid-line');
            this.ctx.lineWidth = isMajorLine ? 2 * dpr : 1 * dpr;
            this.ctx.beginPath();
            this.ctx.moveTo(x, topMargin);
            this.ctx.lineTo(x, topMargin + graphHeight);
            this.ctx.stroke();
            // Draw hour label only at specified intervals (below graph area)
            if (i % labelInterval === 0) {
                const hour = i % 24;
                const label = hour === 0 ? '00' : hour.toString().padStart(2, '0');
                this.ctx.fillText(label, x, topMargin + graphHeight + (15 * dpr));
            }
        }
        // Draw axis lines (like SVG graph)
        this.ctx.strokeStyle = this.getThemeColor('--canvas-text-primary');
        this.ctx.lineWidth = 2 * dpr;
        // X-axis (bottom)
        this.ctx.beginPath();
        this.ctx.moveTo(leftMargin + yAxisWidth, topMargin + graphHeight);
        this.ctx.lineTo(leftMargin + yAxisWidth + graphWidth, topMargin + graphHeight);
        this.ctx.stroke();
        // Y-axis (left)
        this.ctx.beginPath();
        this.ctx.moveTo(leftMargin + yAxisWidth, topMargin);
        this.ctx.lineTo(leftMargin + yAxisWidth, topMargin + graphHeight);
        this.ctx.stroke();
        // Current time indicator (green dashed line like SVG)
        const now = new Date();
        const currentHours = now.getHours() + now.getMinutes() / 60;
        const currentX = leftMargin + yAxisWidth + ((currentHours / this.duration) * graphWidth);
        this.ctx.strokeStyle = '#00ff00';
        this.ctx.lineWidth = 2 * dpr;
        this.ctx.setLineDash([5 * dpr, 5 * dpr]);
        this.ctx.globalAlpha = 0.7;
        this.ctx.beginPath();
        this.ctx.moveTo(currentX, topMargin);
        this.ctx.lineTo(currentX, topMargin + graphHeight);
        this.ctx.stroke();
        this.ctx.setLineDash([]);
        this.ctx.globalAlpha = 1.0;
        // Current time label at top
        const hours = Math.floor(currentHours);
        const minutes = Math.floor((currentHours - hours) * 60);
        const timeLabel = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
        this.ctx.fillStyle = this.getThemeColor('--canvas-text-primary');
        this.ctx.font = `bold ${baseFontSize * dpr}px sans-serif`;
        this.ctx.textAlign = 'center';
        this.ctx.textBaseline = 'bottom';
        this.ctx.fillText(timeLabel, currentX, topMargin - (5 * dpr));
        // Draw X axis label (below time labels)
        if (this.xAxisLabel && !this.collapsed) {
            this.ctx.fillStyle = this.getThemeColor('--canvas-text-primary');
            this.ctx.font = `${baseFontSize * dpr}px sans-serif`;
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'top';
            this.ctx.fillText(this.xAxisLabel, leftMargin + yAxisWidth + (graphWidth / 2), this.canvasHeight - xAxisLabelOffset);
        }
        // Draw background graphs (reference data)
        const ctx = this.ctx;
        this.backgroundGraphs.forEach((bgGraph, graphIndex) => {
            if (bgGraph.keyframes.length === 0)
                return;
            // Sort keyframes by time for proper line drawing
            const sortedKeyframes = [...bgGraph.keyframes].sort((a, b) => a.time - b.time);
            // Use specified color or cycle through palette
            const color = this.getBackgroundGraphColor(bgGraph, graphIndex);
            const rgbaMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
            if (rgbaMatch) {
                ctx.strokeStyle = `rgba(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]}, 0.5)`;
            }
            else {
                // Try hex to rgba conversion
                const hexMatch = color.match(/#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i);
                if (hexMatch) {
                    const r = parseInt(hexMatch[1], 16);
                    const g = parseInt(hexMatch[2], 16);
                    const b = parseInt(hexMatch[3], 16);
                    ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, 0.5)`;
                }
                else {
                    ctx.strokeStyle = color;
                }
            }
            ctx.lineWidth = 1.5 * dpr;
            ctx.setLineDash([3 * dpr, 3 * dpr]); // Dashed line for background
            // Draw lines between keyframes
            for (let i = 0; i < sortedKeyframes.length - 1; i++) {
                const kf1 = sortedKeyframes[i];
                const kf2 = sortedKeyframes[i + 1];
                const x1 = leftMargin + yAxisWidth + ((kf1.time / this.duration) * graphWidth);
                const y1 = topMargin + ((1 - this.normalizeValue(kf1.value)) * graphHeight);
                const x2 = leftMargin + yAxisWidth + ((kf2.time / this.duration) * graphWidth);
                const y2 = topMargin + ((1 - this.normalizeValue(kf2.value)) * graphHeight);
                // Draw line between points
                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();
            }
            ctx.setLineDash([]); // Reset dash
            // Draw small circle markers at each keyframe (smaller than main graph)
            sortedKeyframes.forEach(kf => {
                const x = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
                const y = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
                this.ctx.fillStyle = color;
                this.ctx.beginPath();
                this.ctx.arc(x, y, 2 * dpr, 0, Math.PI * 2);
                this.ctx.fill();
            });
        });
        // Draw FLAT/STEP lines between keyframes (like scheduler-card)
        if (this.keyframes.length > 0) {
            this.ctx.strokeStyle = this.getThemeColor('--keyframe-color');
            this.ctx.lineWidth = 3 * dpr;
            // Keyframes are already sorted by time
            for (let i = 0; i < this.keyframes.length - 1; i++) {
                const kf1 = this.keyframes[i];
                const kf2 = this.keyframes[i + 1];
                const x1 = leftMargin + yAxisWidth + ((kf1.time / this.duration) * graphWidth);
                const y1 = topMargin + ((1 - this.normalizeValue(kf1.value)) * graphHeight);
                const x2 = leftMargin + yAxisWidth + ((kf2.time / this.duration) * graphWidth);
                const y2 = topMargin + ((1 - this.normalizeValue(kf2.value)) * graphHeight);
                // Draw flat line (hold value until next keyframe)
                this.ctx.beginPath();
                this.ctx.moveTo(x1, y1);
                this.ctx.lineTo(x2, y1); // Horizontal to next time
                this.ctx.lineTo(x2, y2); // Vertical step to new value
                this.ctx.stroke();
            }
            // Wraparound: extend last keyframe to end, then wrap to first keyframe
            if (this.keyframes.length > 0) {
                const lastKf = this.keyframes[this.keyframes.length - 1];
                const firstKf = this.keyframes[0];
                const lastX = leftMargin + yAxisWidth + ((lastKf.time / this.duration) * graphWidth);
                const lastY = topMargin + ((1 - this.normalizeValue(lastKf.value)) * graphHeight);
                const firstY = topMargin + ((1 - this.normalizeValue(firstKf.value)) * graphHeight);
                // Determine the starting value (from previous day or from last keyframe)
                const startValue = this.previousDayEndValue !== undefined ? this.previousDayEndValue : lastKf.value;
                topMargin + ((1 - this.normalizeValue(startValue)) * graphHeight);
                // Calculate the position of the last hour marker (24:00/00:00)
                const endX = leftMargin + yAxisWidth + ((this.duration / this.duration) * graphWidth);
                // Extend last keyframe to right edge
                this.ctx.beginPath();
                this.ctx.moveTo(lastX, lastY);
                this.ctx.lineTo(endX, lastY);
                this.ctx.stroke();
                // Extend from left edge to first keyframe
                // In multi-day mode, use previousDayEndValue; in 24hr mode, wrap to own last value
                const wrapValue = this.previousDayEndValue !== undefined && this.previousDayEndValue !== null
                    ? this.previousDayEndValue
                    : lastKf.value;
                const wrapY = topMargin + ((1 - this.normalizeValue(wrapValue)) * graphHeight);
                if (firstKf.time > 0) {
                    // Hold wrap value horizontally, then step to first keyframe
                    const firstX = leftMargin + yAxisWidth + ((firstKf.time / this.duration) * graphWidth);
                    this.ctx.beginPath();
                    this.ctx.moveTo(leftMargin + yAxisWidth, wrapY);
                    this.ctx.lineTo(firstX, wrapY);
                    this.ctx.lineTo(firstX, firstY);
                    this.ctx.stroke();
                }
                else {
                    // First keyframe at 0, just draw vertical step
                    this.ctx.beginPath();
                    this.ctx.moveTo(leftMargin + yAxisWidth, wrapY);
                    this.ctx.lineTo(leftMargin + yAxisWidth, firstY);
                    this.ctx.stroke();
                }
            }
        }
        // Draw advance history markers
        if (this.advanceHistory && this.advanceHistory.length > 0) {
            this.advanceHistory.forEach(event => {
                if (event.target_node && event.target_node.temp !== null && event.target_node.temp !== undefined) {
                    // Parse activated_at time to get hour position
                    const activatedDate = new Date(event.activated_at);
                    const activatedHours = activatedDate.getHours() + (activatedDate.getMinutes() / 60);
                    // Only draw if within today's 24-hour range
                    if (activatedHours >= 0 && activatedHours < 24) {
                        const x = leftMargin + yAxisWidth + ((activatedHours / this.duration) * graphWidth);
                        const y = topMargin + ((1 - this.normalizeValue(event.target_node.temp)) * graphHeight);
                        // Draw diamond marker for advance activation
                        this.ctx.save();
                        this.ctx.fillStyle = '#00ff00';
                        this.ctx.strokeStyle = '#00aa00';
                        this.ctx.lineWidth = 2 * dpr;
                        this.ctx.translate(x, y);
                        this.ctx.rotate(Math.PI / 4);
                        const markerSize = 8 * dpr;
                        this.ctx.fillRect(-markerSize, -markerSize, markerSize * 2, markerSize * 2);
                        this.ctx.strokeRect(-markerSize, -markerSize, markerSize * 2, markerSize * 2);
                        this.ctx.restore();
                    }
                }
            });
        }
        // Draw current time indicator (vertical bar) if set
        if (this.indicatorTime !== undefined && this.indicatorTime >= 0 && this.indicatorTime <= this.duration) {
            const indicatorX = leftMargin + yAxisWidth + ((this.indicatorTime / this.duration) * graphWidth);
            this.ctx.strokeStyle = '#00ff00';
            this.ctx.lineWidth = 2 * dpr;
            this.ctx.setLineDash([5 * dpr, 5 * dpr]); // Dashed line
            // Draw vertical line through graph area
            this.ctx.beginPath();
            this.ctx.moveTo(indicatorX, topMargin);
            this.ctx.lineTo(indicatorX, topMargin + graphHeight);
            this.ctx.stroke();
            this.ctx.setLineDash([]); // Reset dash
        }
        // Draw keyframe markers
        this.keyframes.forEach((kf, index) => {
            const x = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
            const y = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
            // Highlight if being dragged or selected
            const isDragging = this.draggingIndex === index;
            const isSelected = this.selectedKeyframeIndex === index;
            if (isDragging) {
                this.ctx.fillStyle = this.getThemeColor('--keyframe-dragging-color');
            }
            else if (isSelected) {
                this.ctx.fillStyle = this.getThemeColor('--keyframe-selected-color');
            }
            else {
                this.ctx.fillStyle = this.getThemeColor('--keyframe-color');
            }
            // Draw diamond marker (smaller when collapsed)
            this.ctx.save();
            this.ctx.translate(x, y);
            this.ctx.rotate(Math.PI / 4);
            const baseSize = this.collapsed ? 4 : 6;
            const size = (isDragging || isSelected) ? baseSize + 2 : baseSize;
            this.ctx.fillRect(-size * dpr, -size * dpr, size * 2 * dpr, size * 2 * dpr);
            this.ctx.restore();
            // Draw selection ring for selected keyframe (hide when collapsed)
            if (isSelected && !isDragging && !this.collapsed) {
                this.ctx.strokeStyle = this.getThemeColor('--keyframe-selected-color');
                this.ctx.lineWidth = 2;
                this.ctx.beginPath();
                this.ctx.arc(x, y, 12 * dpr, 0, Math.PI * 2);
                this.ctx.stroke();
            }
            // Draw hover ring for draggable indication (hide when collapsed)
            if (!isDragging && !isSelected && !this.collapsed) {
                const textColor = this.getThemeColor('--canvas-text-secondary');
                // Apply opacity to text color for hover ring
                const rgbaMatch = textColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
                if (rgbaMatch) {
                    this.ctx.strokeStyle = `rgba(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]}, 0.3)`;
                }
                else {
                    this.ctx.strokeStyle = textColor;
                }
                this.ctx.lineWidth = 1;
                this.ctx.beginPath();
                this.ctx.arc(x, y, 10 * dpr, 0, Math.PI * 2);
                this.ctx.stroke();
            }
            // Draw value label above each keyframe (hide when collapsed)
            if (!this.collapsed) {
                const valueText = kf.value.toFixed(this.snapValue < 1 ? 1 : 0);
                this.ctx.font = `${baseFontSize * dpr}px system-ui, -apple-system, sans-serif`;
                this.ctx.textAlign = 'center';
                this.ctx.textBaseline = 'bottom';
                // Position label above the keyframe with some padding
                const labelY = y - 18 * dpr;
                // Draw background for better readability
                const textMetrics = this.ctx.measureText(valueText);
                const textWidth = textMetrics.width;
                const textHeight = 14 * dpr;
                const padding = 4 * dpr;
                this.ctx.fillStyle = this.getThemeColor('--canvas-label-bg');
                this.ctx.fillRect(x - textWidth / 2 - padding, labelY - textHeight - padding, textWidth + padding * 2, textHeight + padding * 2);
                // Draw text - use keyframe color
                if (isDragging) {
                    this.ctx.fillStyle = this.getThemeColor('--keyframe-dragging-color');
                }
                else if (isSelected) {
                    this.ctx.fillStyle = this.getThemeColor('--keyframe-selected-color');
                }
                else {
                    this.ctx.fillStyle = this.getThemeColor('--keyframe-color');
                }
                this.ctx.fillText(valueText, x, labelY);
            }
        });
        // Tooltip is rendered as a lightweight DOM overlay to avoid canvas redraw on hover.
    }
    buildHoverTooltip(x, y, rect) {
        const { leftMargin, yAxisWidth, rightMargin, topMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check if in graph area
        if (x < leftMargin + yAxisWidth || x > rect.width - rightMargin ||
            y < topMargin || y > topMargin + graphHeight) {
            return null;
        }
        const adjustedX = x - leftMargin - yAxisWidth;
        const time = (adjustedX / graphWidth) * this.duration;
        let tooltipLines = [];
        if (this.tooltipMode === 'cursor') {
            // Show interpolated value at cursor position on schedule line
            const tooltipValue = this.getInterpolatedValue(time);
            if (tooltipValue !== null) {
                const hours = Math.floor(time);
                const minutes = Math.round((time - hours) * 60);
                tooltipLines.push({
                    text: `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')} - ${tooltipValue.toFixed(1)}°`,
                    color: this.getThemeColor('--keyframe-color')
                });
            }
        }
        else {
            // Get current time in decimal hours
            const now = new Date();
            const currentTime = now.getHours() + now.getMinutes() / 60;
            // Only show history if hovering over past time or close to current time (within 30 minutes)
            const timeDiff = time - currentTime;
            const showHistory = timeDiff <= 0.5; // Show if in past or within 30 minutes of now
            if (showHistory && this.backgroundGraphs && this.backgroundGraphs.length > 0) {
                this.backgroundGraphs.forEach((historyGraph, graphIndex) => {
                    if (!historyGraph || !historyGraph.keyframes || historyGraph.keyframes.length === 0)
                        return;
                    let displayPoint = null;
                    if (time >= currentTime) {
                        // At or past current time (but close to now): show latest temperature
                        displayPoint = historyGraph.keyframes[historyGraph.keyframes.length - 1];
                    }
                    else {
                        // Before current time: find closest data point to hover time
                        let closestPoint = null;
                        let closestDist = Infinity;
                        for (const point of historyGraph.keyframes) {
                            const dist = Math.abs(point.time - time);
                            if (dist < closestDist) {
                                closestDist = dist;
                                closestPoint = point;
                            }
                        }
                        if (closestPoint && closestDist < 0.5) { // Within 30 minutes
                            displayPoint = closestPoint;
                        }
                    }
                    if (displayPoint) {
                        const label = historyGraph.label || 'Temperature';
                        const color = historyGraph.color || BACKGROUND_GRAPH_COLORS[graphIndex % BACKGROUND_GRAPH_COLORS.length];
                        tooltipLines.push({ text: `${label}: ${displayPoint.value.toFixed(1)}°`, color });
                    }
                });
            }
        }
        if (tooltipLines.length === 0)
            return null;
        return {
            lines: tooltipLines
        };
    }
    hideHoverTooltip() {
        if (!this.tooltipEl)
            return;
        this.tooltipEl.hidden = true;
    }
    getOpaqueColor(color) {
        if (!this.shadowRoot)
            return color;
        if (!this.colorProbeEl) {
            this.colorProbeEl = document.createElement('span');
            this.colorProbeEl.style.position = 'absolute';
            this.colorProbeEl.style.visibility = 'hidden';
            this.colorProbeEl.style.pointerEvents = 'none';
            this.colorProbeEl.style.width = '0';
            this.colorProbeEl.style.height = '0';
            this.colorProbeEl.style.overflow = 'hidden';
            this.shadowRoot.appendChild(this.colorProbeEl);
        }
        this.colorProbeEl.style.color = color;
        const resolved = getComputedStyle(this.colorProbeEl).color;
        const rgbaMatch = resolved.match(/rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([0-9]*\.?[0-9]+)\s*\)/i);
        if (rgbaMatch) {
            return `rgb(${rgbaMatch[1]}, ${rgbaMatch[2]}, ${rgbaMatch[3]})`;
        }
        return resolved || color;
    }
    renderHoverTooltip() {
        if (!this.canvas || !this.tooltipEl || this.collapsed || this.isDragging || this.isPanning) {
            this.hideHoverTooltip();
            return;
        }
        const rect = this.canvas.getBoundingClientRect();
        const tooltip = this.buildHoverTooltip(this.hoverX, this.hoverY, rect);
        if (!tooltip) {
            this.hideHoverTooltip();
            return;
        }
        this.tooltipEl.replaceChildren();
        tooltip.lines.forEach(line => {
            const lineEl = document.createElement('div');
            lineEl.className = 'cursor-tooltip-line';
            lineEl.textContent = line.text;
            if (line.color) {
                lineEl.style.color = this.getOpaqueColor(line.color);
            }
            this.tooltipEl.appendChild(lineEl);
        });
        this.tooltipEl.hidden = false;
        // Stable anchor: above-left by default; flip horizontally to the right when near left edge.
        const cursorOffsetX = 32;
        const cursorOffsetY = 10;
        const margin = 10;
        const tooltipWidth = this.tooltipEl.offsetWidth;
        const tooltipHeight = this.tooltipEl.offsetHeight;
        const maxX = Math.max(margin, rect.width - tooltipWidth - margin);
        const maxY = Math.max(margin, rect.height - tooltipHeight - margin);
        let tooltipX = this.hoverX - tooltipWidth - cursorOffsetX;
        if (tooltipX < margin) {
            tooltipX = this.hoverX + cursorOffsetX;
        }
        tooltipX = Math.max(margin, Math.min(tooltipX, maxX));
        const tooltipY = Math.max(margin, Math.min(this.hoverY - tooltipHeight - cursorOffsetY, maxY));
        this.tooltipEl.style.left = `${Math.round(tooltipX)}px`;
        this.tooltipEl.style.top = `${Math.round(tooltipY)}px`;
    }
    queueHoverTooltipRender(x, y) {
        this.hoverX = x;
        this.hoverY = y;
        if (this.hoverRenderPending)
            return;
        this.hoverRenderPending = true;
        requestAnimationFrame(() => {
            this.hoverRenderPending = false;
            this.renderHoverTooltip();
        });
    }
    getInterpolatedValue(time) {
        if (this.keyframes.length === 0)
            return null;
        if (this.keyframes.length === 1)
            return this.keyframes[0].value;
        // Find surrounding keyframes
        let beforeKf = this.keyframes[0];
        let afterKf = this.keyframes[this.keyframes.length - 1];
        for (let i = 0; i < this.keyframes.length - 1; i++) {
            if (this.keyframes[i].time <= time && this.keyframes[i + 1].time >= time) {
                beforeKf = this.keyframes[i];
                afterKf = this.keyframes[i + 1];
                break;
            }
        }
        // Linear interpolation
        if (beforeKf.time === afterKf.time)
            return beforeKf.value;
        const ratio = (time - beforeKf.time) / (afterKf.time - beforeKf.time);
        return beforeKf.value + (afterKf.value - beforeKf.value) * ratio;
    }
    handleCanvasMouseDown(e) {
        if (!this.canvas || this.collapsed || this.readonly)
            return;
        const rect = this.canvas.getBoundingClientRect();
        const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
        const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
        const x = clientX - rect.left;
        const y = clientY - rect.top;
        const { leftMargin, yAxisWidth, rightMargin, topMargin, bottomMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check if scrollable (only in expanded mode)
        const isScrollable = !this.collapsed && this.wrapperEl && this.wrapperEl.scrollWidth > this.wrapperEl.clientWidth;
        // For touch devices, set up long-press detection FIRST (before any returns)
        if (e instanceof TouchEvent) {
            const currentTime = Date.now();
            const timeSinceLastClick = currentTime - this.lastClickTime;
            const dx = x - this.lastClickX;
            const dy = y - this.lastClickY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            // Double-tap detected (within 300ms and 30px)
            if (timeSinceLastClick < 300 && distance < 30) {
                this.handleDoubleClick(x, y, rect);
                this.lastClickTime = 0; // Reset to prevent triple-tap
                return;
            }
            this.lastClickTime = currentTime;
            this.lastClickX = x;
            this.lastClickY = y;
            // Start long-press timer for touch devices (600ms)
            this.holdStartX = x;
            this.holdStartY = y;
            this.clearHoldTimer();
            this.holdTimer = window.setTimeout(() => {
                this.handleContextMenu(x, y, rect);
                // Clear dragging state if long press completes
                this.isDragging = false;
                this.draggingIndex = null;
            }, 600);
        }
        // Check if clicking on existing keyframe
        const clickedIndex = this.keyframes.findIndex(kf => {
            const kfX = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
            const kfY = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
            const distance = Math.sqrt(Math.pow(x - kfX, 2) + Math.pow(y - kfY, 2));
            return distance < 20;
        });
        if (clickedIndex >= 0) {
            // Check for double-click to delete (desktop)
            if (e instanceof MouseEvent) {
                const currentTime = Date.now();
                const timeSinceLastClick = currentTime - this.lastClickTime;
                // Double-click detected on same keyframe (within 300ms)
                if (timeSinceLastClick < 300 && this.lastClickIndex === clickedIndex) {
                    this.deleteKeyframe(clickedIndex);
                    this.lastClickTime = 0; // Reset
                    this.lastClickIndex = -1;
                    this.draggingIndex = null; // Clear dragging state
                    this.isDragging = false;
                    this.justDeletedKeyframe = true; // Prevent dblclick handler from adding
                    setTimeout(() => this.justDeletedKeyframe = false, 100); // Reset after event propagation
                    e.preventDefault();
                    e.stopPropagation();
                    return;
                }
                this.lastClickTime = currentTime;
                this.lastClickIndex = clickedIndex;
            }
            // Prepare to drag existing keyframe (but wait to see if it's a long press or just a click)
            this.draggingIndex = clickedIndex;
            this.hasMoved = false; // Reset movement tracking
            this.dragStartX = x;
            this.dragStartY = y;
            // Don't set isDragging yet - we'll set it on first move
            // This allows clicks to work properly
            e.preventDefault();
            return;
        }
        // Check if clicking on a segment (line between two keyframes)
        const segmentIndex = this.findSegmentAtPoint(x, y, rect);
        if (segmentIndex >= 0) {
            // Prepare to drag segment
            this.draggingSegment = {
                startIndex: segmentIndex,
                endIndex: segmentIndex + 1,
                initialStartTime: this.keyframes[segmentIndex].time,
                initialEndTime: this.keyframes[segmentIndex + 1].time,
                initialPointerX: x
            };
            this.hasMoved = false;
            this.dragStartX = x;
            e.preventDefault();
            return;
        }
        // If scrollable and not clicking on keyframe, prepare for panning
        if (isScrollable) {
            this.isPanning = true;
            this.panStartX = clientX;
            this.panStartScrollLeft = this.wrapperEl.scrollLeft;
            e.preventDefault();
        }
    }
    handleCanvasMouseMove(e) {
        // Update hover position for tooltip (only for mouse, not touch)
        if (e instanceof MouseEvent && !this.isDragging && !this.isPanning && this.canvas) {
            this.queueHoverTooltipRender(e.offsetX, e.offsetY);
        }
        else if (e instanceof TouchEvent) {
            this.hideHoverTooltip();
        }
        // Handle panning first (takes priority when active)
        if (this.isPanning && this.wrapperEl) {
            const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
            const deltaX = clientX - this.panStartX;
            this.wrapperEl.scrollLeft = this.panStartScrollLeft - deltaX;
            e.preventDefault();
            return;
        }
        // Cancel long-press if moving during touch hold
        if (e instanceof TouchEvent && this.holdTimer) {
            const rect = this.canvas?.getBoundingClientRect();
            if (rect) {
                this.wrapperEl?.scrollLeft || 0;
                const x = e.touches[0].clientX - rect.left;
                const y = e.touches[0].clientY - rect.top;
                const dx = x - this.holdStartX;
                const dy = y - this.holdStartY;
                const distance = Math.sqrt(dx * dx + dy * dy);
                // Cancel hold if moved more than 5px - user is dragging (match threshold below)
                if (distance > 5) {
                    this.clearHoldTimer();
                }
            }
        }
        if (!this.canvas)
            return;
        // Handle segment dragging
        if (this.draggingSegment !== null) {
            const rect = this.canvas.getBoundingClientRect();
            const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
            this.wrapperEl?.scrollLeft || 0;
            const currentX = clientX - rect.left;
            const dx = currentX - this.dragStartX;
            const distance = Math.abs(dx);
            if (distance < 5)
                return; // Minimum movement threshold
            if (!this.hasMoved) {
                this.saveUndoState();
                this.hasMoved = true;
            }
            const { leftMargin, yAxisWidth, graphWidth } = this.getGraphDimensions(rect);
            // Calculate time delta
            const pixelDelta = currentX - this.draggingSegment.initialPointerX;
            const timeDelta = (pixelDelta / graphWidth) * this.duration;
            // Apply delta to both keyframes
            let newStartTime = this.draggingSegment.initialStartTime + timeDelta;
            let newEndTime = this.draggingSegment.initialEndTime + timeDelta;
            // Constrain to not pass adjacent keyframes
            const { startIndex, endIndex } = this.draggingSegment;
            if (startIndex > 0) {
                const prevTime = this.keyframes[startIndex - 1].time;
                const minGap = this.duration / this.slots;
                if (newStartTime < prevTime + minGap) {
                    const shift = (prevTime + minGap) - newStartTime;
                    newStartTime += shift;
                    newEndTime += shift;
                }
            }
            if (endIndex < this.keyframes.length - 1) {
                const nextTime = this.keyframes[endIndex + 1].time;
                const minGap = this.duration / this.slots;
                if (newEndTime > nextTime - minGap) {
                    const shift = newEndTime - (nextTime - minGap);
                    newStartTime -= shift;
                    newEndTime -= shift;
                }
            }
            // Clamp to boundaries (max 23:59 to prevent 24:00)
            newStartTime = Math.max(0, newStartTime);
            newEndTime = Math.min(23 + (59 / 60), newEndTime);
            // Snap times to nearest slot (15-minute intervals)
            const slotDuration = this.duration / this.slots;
            newStartTime = Math.round(newStartTime / slotDuration) * slotDuration;
            newEndTime = Math.round(newEndTime / slotDuration) * slotDuration;
            // Update keyframes
            this.keyframes[startIndex].time = newStartTime;
            this.keyframes[endIndex].time = newEndTime;
            // Fire update event for settings panel
            this.dispatchEvent(new CustomEvent('nodeSettingsUpdate', {
                detail: {
                    index: startIndex,
                    keyframe: this.keyframes[startIndex]
                },
                bubbles: true,
                composed: true
            }));
            this.drawTimeline();
            return;
        }
        // Handle single keyframe dragging
        if (this.draggingIndex === null)
            return;
        const rect = this.canvas.getBoundingClientRect();
        const clientX = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
        const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
        this.wrapperEl?.scrollLeft || 0;
        const currentX = clientX - rect.left;
        const currentY = clientY - rect.top;
        // Calculate distance from drag start
        const dx = currentX - this.dragStartX;
        const dy = currentY - this.dragStartY;
        const distance = Math.sqrt(dx * dx + dy * dy);
        // Only consider it a move if moved more than 5 pixels
        if (distance < 5)
            return;
        // Start dragging on first move
        if (!this.isDragging) {
            this.isDragging = true;
            // Save undo state when starting to drag
            this.saveUndoState();
        }
        this.hasMoved = true; // Track that movement occurred
        if (!this.isDragging)
            return;
        let x = currentX;
        let y = currentY;
        const { leftMargin, yAxisWidth, rightMargin, topMargin, bottomMargin, graphHeight, graphWidth, canvasWidthCSS } = this.getGraphDimensions(rect);
        // Clamp to canvas bounds (graph area only)
        x = Math.max(leftMargin + yAxisWidth, Math.min(x, canvasWidthCSS - rightMargin));
        y = Math.max(topMargin, Math.min(y, topMargin + graphHeight));
        // Snap time to nearest slot (adjust for Y axis offset)
        const adjustedX = x - leftMargin - yAxisWidth;
        const slotWidth = graphWidth / this.slots;
        const slotIndex = Math.round(adjustedX / slotWidth);
        // Clamp to 23:59 (23.9833 hours) to prevent 24:00
        let time = Math.min((slotIndex / this.slots) * this.duration, 23 + (59 / 60));
        // Constrain time to not pass adjacent keyframes (array is already sorted)
        if (this.draggingIndex > 0) {
            const prevTime = this.keyframes[this.draggingIndex - 1].time;
            const minTime = prevTime + (this.duration / this.slots); // At least one slot apart
            time = Math.max(time, minTime);
        }
        if (this.draggingIndex < this.keyframes.length - 1) {
            const nextTime = this.keyframes[this.draggingIndex + 1].time;
            const maxTime = nextTime - (this.duration / this.slots); // At least one slot apart
            time = Math.min(time, maxTime);
        }
        // Value is in minValue-maxValue range (adjust for top margin)
        const adjustedY = y - topMargin;
        const normalizedValue = Math.max(0, Math.min(1, 1 - (adjustedY / graphHeight)));
        let value = Math.max(this.minValue, Math.min(this.maxValue, this.denormalizeValue(normalizedValue)));
        // Apply snapping if configured
        value = this.snapValueToGrid(value);
        // Update keyframe position
        const oldTime = this.keyframes[this.draggingIndex].time;
        this.keyframes = this.keyframes.map((kf, i) => i === this.draggingIndex ? { time, value } : kf);
        // Re-sort if time changed (might change position in array)
        if (Math.abs(time - oldTime) > 0.01) {
            this.sortKeyframes();
        }
        // Fire update event for settings panel
        this.dispatchEvent(new CustomEvent('nodeSettingsUpdate', {
            detail: {
                index: this.draggingIndex,
                keyframe: this.keyframes[this.draggingIndex]
            },
            bubbles: true,
            composed: true
        }));
        this.drawTimeline();
        e.preventDefault();
    }
    handleCanvasMouseUp(e) {
        this.clearHoldTimer();
        this.hideHoverTooltip();
        // Handle segment dragging completion
        if (this.draggingSegment !== null && this.hasMoved) {
            this.dispatchEvent(new CustomEvent('segment-moved', {
                detail: {
                    startIndex: this.draggingSegment.startIndex,
                    endIndex: this.draggingSegment.endIndex
                },
                bubbles: true,
                composed: true
            }));
            this.draggingSegment = null;
            this.hasMoved = false;
            return;
        }
        // Handle single keyframe dragging completion
        if (this.draggingIndex !== null && this.hasMoved) {
            this.dispatchEvent(new CustomEvent('keyframe-moved', {
                detail: {
                    index: this.draggingIndex,
                    keyframe: this.keyframes[this.draggingIndex]
                },
                bubbles: true,
                composed: true
            }));
        }
        // Handle tap selection for touch devices (when not dragging)
        if (e instanceof TouchEvent && this.draggingIndex !== null && !this.hasMoved) {
            // Select the keyframe
            this.selectedKeyframeIndex = this.draggingIndex;
            this.drawTimeline();
            this.dispatchEvent(new CustomEvent('keyframe-clicked', {
                detail: {
                    index: this.draggingIndex,
                    keyframe: this.keyframes[this.draggingIndex]
                },
                bubbles: true,
                composed: true
            }));
        }
        this.draggingIndex = null;
        this.draggingSegment = null;
        this.isDragging = false;
        this.hasMoved = false;
        this.isPanning = false; // Reset panning state
    }
    handleCanvasMouseLeave(e) {
        // Clear hover tooltip overlay
        this.hideHoverTooltip();
        // Also handle mouseup logic if needed
        this.handleCanvasMouseUp(e);
    }
    handleCanvasClick(e) {
        if (!this.canvas || this.collapsed || this.readonly)
            return;
        // If we just finished dragging, don't process click
        if (this.hasMoved)
            return;
        const rect = this.canvas.getBoundingClientRect();
        this.wrapperEl?.scrollLeft || 0;
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        // Use CSS pixels for click detection (canvas drawing uses device pixels with dpr scaling)
        const { leftMargin, yAxisWidth, topMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check if clicking on existing keyframe
        const clickedIndex = this.keyframes.findIndex(kf => {
            const kfX = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
            const kfY = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
            const distance = Math.sqrt(Math.pow(x - kfX, 2) + Math.pow(y - kfY, 2));
            return distance < 20;
        });
        if (clickedIndex >= 0) {
            // Select the keyframe
            this.selectedKeyframeIndex = clickedIndex;
            this.drawTimeline();
            this.dispatchEvent(new CustomEvent('keyframe-clicked', {
                detail: {
                    index: clickedIndex,
                    keyframe: this.keyframes[clickedIndex]
                },
                bubbles: true,
                composed: true
            }));
        }
        else {
            // Clicked on empty area - deselect
            this.selectedKeyframeIndex = null;
            this.drawTimeline();
        }
    }
    handleCanvasContextMenu(e) {
        if (!this.canvas || this.collapsed || this.readonly)
            return;
        e.preventDefault(); // Prevent default context menu
        const rect = this.canvas.getBoundingClientRect();
        this.wrapperEl?.scrollLeft || 0;
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        this.handleContextMenu(x, y, rect);
    }
    handleContextMenu(x, y, rect) {
        const { leftMargin, yAxisWidth, topMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check if clicking on existing keyframe
        const clickedIndex = this.keyframes.findIndex(kf => {
            const kfX = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
            const kfY = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
            const distance = Math.sqrt(Math.pow(x - kfX, 2) + Math.pow(y - kfY, 2));
            return distance < 20;
        });
        // Delete keyframe if clicked on one
        if (clickedIndex >= 0) {
            this.deleteKeyframe(clickedIndex);
        }
    }
    clearHoldTimer() {
        if (this.holdTimer) {
            window.clearTimeout(this.holdTimer);
            this.holdTimer = undefined;
        }
    }
    handleCanvasDoubleClick(e) {
        if (!this.canvas || this.collapsed || this.readonly || this.justDeletedKeyframe)
            return;
        const rect = this.canvas.getBoundingClientRect();
        this.wrapperEl?.scrollLeft || 0;
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        this.handleDoubleClick(x, y, rect);
    }
    handleDoubleClick(x, y, rect) {
        const { leftMargin, yAxisWidth, topMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check if clicking on existing keyframe
        const clickedIndex = this.keyframes.findIndex(kf => {
            const kfX = leftMargin + yAxisWidth + ((kf.time / this.duration) * graphWidth);
            const kfY = topMargin + ((1 - this.normalizeValue(kf.value)) * graphHeight);
            const distance = Math.sqrt(Math.pow(x - kfX, 2) + Math.pow(y - kfY, 2));
            return distance < 20;
        });
        if (clickedIndex >= 0)
            return; // Don't add if clicking on existing
        // Check if click is within graph area
        if (x < leftMargin + yAxisWidth || y < topMargin || y > topMargin + graphHeight)
            return;
        // Snap time to nearest slot (adjust for Y axis offset)
        const adjustedX = x - leftMargin - yAxisWidth;
        const slotWidth = graphWidth / this.slots;
        const slotIndex = Math.round(adjustedX / slotWidth);
        // Clamp to 23:59 (23.9833 hours) to prevent 24:00
        const time = Math.min((slotIndex / this.slots) * this.duration, 23 + (59 / 60));
        // Value is in minValue-maxValue range (adjust for top margin)
        const adjustedY = y - topMargin;
        const normalizedValue = 1 - (adjustedY / graphHeight);
        let value = this.denormalizeValue(normalizedValue);
        // Apply snapping if configured
        value = this.snapValueToGrid(value);
        // Save state before adding
        this.saveUndoState();
        this.keyframes = [...this.keyframes, { time, value }];
        this.sortKeyframes(); // Keep array sorted
        this.drawTimeline();
        this.dispatchEvent(new CustomEvent('keyframe-added', {
            detail: { time, value },
            bubbles: true,
            composed: true
        }));
    }
    // Helper method to delete a keyframe
    deleteKeyframe(index) {
        if (index < 0 || index >= this.keyframes.length)
            return;
        // Save state before deleting
        this.saveUndoState();
        const deletedKeyframe = this.keyframes[index];
        // Remove from keyframes
        this.keyframes = this.keyframes.filter((_, i) => i !== index);
        // Clear selection if deleting selected keyframe
        if (this.selectedKeyframeIndex === index) {
            this.selectedKeyframeIndex = null;
        }
        else if (this.selectedKeyframeIndex !== null && this.selectedKeyframeIndex > index) {
            // Adjust selection index if deleting a keyframe before selected one
            this.selectedKeyframeIndex--;
        }
        this.drawTimeline();
        this.dispatchEvent(new CustomEvent('keyframe-deleted', {
            detail: { index, keyframe: deletedKeyframe },
            bubbles: true,
            composed: true
        }));
    }
    // Find segment (line between keyframes) at given point
    findSegmentAtPoint(x, y, rect) {
        const { leftMargin, yAxisWidth, topMargin, graphHeight, graphWidth } = this.getGraphDimensions(rect);
        // Check each segment
        for (let i = 0; i < this.keyframes.length - 1; i++) {
            const kf1 = this.keyframes[i];
            const kf2 = this.keyframes[i + 1];
            const x1 = leftMargin + yAxisWidth + ((kf1.time / this.duration) * graphWidth);
            const y1 = topMargin + ((1 - this.normalizeValue(kf1.value)) * graphHeight);
            const x2 = leftMargin + yAxisWidth + ((kf2.time / this.duration) * graphWidth);
            const y2 = topMargin + ((1 - this.normalizeValue(kf2.value)) * graphHeight);
            // Check distance to horizontal line segment (flat hold)
            if (x >= x1 && x <= x2) {
                const distanceToHorizontal = Math.abs(y - y1);
                if (distanceToHorizontal < 10)
                    return i;
            }
            // Check distance to vertical line segment (step)
            if (Math.abs(x - x2) < 10 && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))) {
                return i;
            }
        }
        return -1;
    }
    clearKeyframes() {
        // Save state before clearing
        this.saveUndoState();
        this.keyframes = [];
        this.selectedKeyframeIndex = null; // Clear selection
        this.drawTimeline();
        this.dispatchEvent(new CustomEvent('keyframes-cleared', {
            bubbles: true,
            composed: true
        }));
    }
    // Save current state to undo stack
    emitUndoStackChanged(action, size, removedCount = 0) {
        this.dispatchEvent(new CustomEvent('undo-stack-changed', {
            detail: {
                action,
                size,
                removedCount,
                keyframeCount: this.keyframes.length
            },
            bubbles: true,
            composed: true
        }));
    }
    saveUndoState() {
        // Deep copy current keyframes
        const stateCopy = this.keyframes.map(kf => ({ ...kf }));
        this.undoStack = [...this.undoStack, stateCopy];
        this.emitUndoStackChanged('push', this.undoStack.length);
        // Limit undo stack to 50 entries
        if (this.undoStack.length > 50) {
            const removedCount = this.undoStack.length - 50;
            this.undoStack = this.undoStack.slice(-50);
            this.emitUndoStackChanged('trim', this.undoStack.length, removedCount);
        }
        this.updateUndoButtonState();
        this.updateNavigationButtonsState();
    }
    // Public undo method (can be called externally or via Ctrl+Z)
    undo() {
        if (this.undoStack.length === 0)
            return;
        // Pop last state from undo stack
        const previousState = this.undoStack[this.undoStack.length - 1];
        this.undoStack = this.undoStack.slice(0, -1);
        this.emitUndoStackChanged('pop', this.undoStack.length, 1);
        // Restore previous state
        this.keyframes = previousState.map(kf => ({ ...kf }));
        this.drawTimeline();
        this.updateUndoButtonState();
        this.updateNavigationButtonsState();
        this.dispatchEvent(new CustomEvent('keyframe-restored', {
            detail: { keyframes: this.keyframes },
            bubbles: true,
            composed: true
        }));
    }
    // Set external undo button reference
    setUndoButton(buttonElement) {
        this.undoButton = buttonElement;
        // Add click handler
        if (this.undoButton) {
            this.undoButton.addEventListener('click', () => this.undo());
            this.updateUndoButtonState();
        }
    }
    // Set external previous button reference
    setPreviousButton(buttonElement) {
        this.previousButton = buttonElement;
        if (this.previousButton) {
            this.previousButton.addEventListener('click', () => this.selectPrevious());
            this.updateNavigationButtonsState();
        }
    }
    // Set external next button reference
    setNextButton(buttonElement) {
        this.nextButton = buttonElement;
        if (this.nextButton) {
            this.nextButton.addEventListener('click', () => this.selectNext());
            this.updateNavigationButtonsState();
        }
    }
    // Set external clear button reference
    setClearButton(buttonElement) {
        if (buttonElement) {
            buttonElement.addEventListener('click', () => this.clearKeyframes());
        }
    }
    // Update navigation buttons disabled state
    updateNavigationButtonsState() {
        const canNavigate = this.keyframes.length >= 2;
        if (this.previousButton) {
            this.previousButton.disabled = !canNavigate;
        }
        if (this.nextButton) {
            this.nextButton.disabled = !canNavigate;
        }
    }
    // Update undo button disabled state
    updateUndoButtonState() {
        if (this.undoButton) {
            if (this.undoStack.length > 0) {
                this.undoButton.disabled = false;
                this.undoButton.style.opacity = '1';
            }
            else {
                this.undoButton.disabled = true;
                this.undoButton.style.opacity = '0.5';
            }
        }
    }
    selectPrevious() {
        if (this.keyframes.length === 0)
            return;
        // Array is already sorted by time
        if (this.selectedKeyframeIndex === null || this.selectedKeyframeIndex === 0) {
            // No selection or at start - select last keyframe
            this.selectedKeyframeIndex = this.keyframes.length - 1;
        }
        else {
            // Select previous
            this.selectedKeyframeIndex--;
        }
        this.drawTimeline();
        this.dispatchEvent(new CustomEvent('keyframe-selected', {
            detail: {
                index: this.selectedKeyframeIndex,
                keyframe: this.keyframes[this.selectedKeyframeIndex]
            },
            bubbles: true,
            composed: true
        }));
    }
    selectNext() {
        if (this.keyframes.length === 0)
            return;
        // Array is already sorted by time
        if (this.selectedKeyframeIndex === null || this.selectedKeyframeIndex >= this.keyframes.length - 1) {
            // No selection or at end - select first keyframe
            this.selectedKeyframeIndex = 0;
        }
        else {
            // Select next
            this.selectedKeyframeIndex++;
        }
        this.drawTimeline();
        this.dispatchEvent(new CustomEvent('keyframe-selected', {
            detail: {
                index: this.selectedKeyframeIndex,
                keyframe: this.keyframes[this.selectedKeyframeIndex]
            },
            bubbles: true,
            composed: true
        }));
    }
    toggleConfig() {
        this.showConfig = !this.showConfig;
    }
    toggleCollapse() {
        if (!this.allowCollapse)
            return; // Don't allow collapsing if disabled
        this.hideHoverTooltip();
        this.collapsed = !this.collapsed;
        // Update canvas height after state change
        setTimeout(() => {
            this.updateCanvasSize();
            this.drawTimeline();
            this.checkScrollVisibility();
        }, 50);
    }
    checkScrollVisibility() {
        if (!this.wrapperEl)
            return;
        const isScrollable = !this.collapsed && this.wrapperEl.scrollWidth > this.wrapperEl.clientWidth;
        if (!isScrollable) {
            this.showScrollNavLeft = false;
            this.showScrollNavRight = false;
            return;
        }
        const scrollLeft = this.wrapperEl.scrollLeft;
        const maxScroll = this.wrapperEl.scrollWidth - this.wrapperEl.clientWidth;
        // Show left button if not at start (with 1px tolerance)
        this.showScrollNavLeft = scrollLeft > 1;
        // Show right button if not at end (with 1px tolerance)
        this.showScrollNavRight = scrollLeft < maxScroll - 1;
    }
    scrollToStart() {
        if (this.wrapperEl) {
            this.wrapperEl.scrollTo({ left: 0, behavior: 'smooth' });
        }
    }
    scrollToEnd() {
        if (this.wrapperEl) {
            this.wrapperEl.scrollTo({ left: this.wrapperEl.scrollWidth, behavior: 'smooth' });
        }
    }
    updateSlots(e) {
        const input = e.target;
        const newSlots = Math.max(1, Math.min(288, parseInt(input.value) || 1));
        if (newSlots !== this.slots) {
            this.slots = newSlots;
            this.updateCanvasSize();
            this.drawTimeline();
        }
    }
    updateDuration(e) {
        const input = e.target;
        const newDuration = Math.max(1, Math.min(168, parseInt(input.value) || 1));
        if (newDuration !== this.duration) {
            this.duration = newDuration;
            this.drawTimeline();
        }
    }
    updatePreviousDayEnd(e) {
        const input = e.target;
        if (input.value === '') {
            this.previousDayEndValue = undefined;
        }
        else {
            const value = parseFloat(input.value);
            this.previousDayEndValue = Math.max(this.minValue, Math.min(this.maxValue, value));
        }
        this.drawTimeline();
    }
    updateMinValue(e) {
        const input = e.target;
        const newMin = parseFloat(input.value);
        if (!isNaN(newMin) && newMin < this.maxValue) {
            this.minValue = newMin;
            this.drawTimeline();
        }
    }
    updateMaxValue(e) {
        const input = e.target;
        const newMax = parseFloat(input.value);
        if (!isNaN(newMax) && newMax > this.minValue) {
            this.maxValue = newMax;
            this.drawTimeline();
        }
    }
    updateSnapValue(e) {
        const input = e.target;
        if (input.value === '') {
            this.snapValue = 0;
        }
        else {
            const newSnap = parseFloat(input.value);
            this.snapValue = !isNaN(newSnap) && newSnap >= 0 ? newSnap : 0;
        }
    }
    updateXAxisLabel(e) {
        const input = e.target;
        this.xAxisLabel = input.value;
        this.drawTimeline();
    }
    updateYAxisLabel(e) {
        const input = e.target;
        this.yAxisLabel = input.value;
        this.drawTimeline();
    }
    render() {
        const slotDuration = this.duration / this.slots;
        const slotMinutes = slotDuration * 60;
        return b `
      <div class="timeline-container">
        ${!this.showHeader && this.title && this.allowCollapse ? b `
          <div class="timeline-title" @click=${this.toggleCollapse} title="Click to ${this.collapsed ? 'expand' : 'collapse'}">
            ${this.title}
          </div>
        ` : !this.showHeader && this.title ? b `
          <div class="timeline-title" style="cursor: default;">
            ${this.title}
          </div>
        ` : ''}
        
        ${this.showHeader ? b `
          <div class="timeline-header">
            <span @click=${this.allowCollapse ? this.toggleCollapse : null} title="${this.allowCollapse ? `Click to ${this.collapsed ? 'expand' : 'collapse'}` : ''}" style="cursor: ${this.allowCollapse ? 'pointer' : 'default'};">
              ${this.title || `Timeline Editor (${this.duration}h • ${this.slots} slots @ ${slotMinutes.toFixed(0)}min)`}
            </span>
            <div class="timeline-controls">
              <button class="secondary" @click=${this.selectPrevious} ?disabled=${this.keyframes.length === 0} title="Previous keyframe">
                ◀
              </button>
              <button class="secondary" @click=${this.selectNext} ?disabled=${this.keyframes.length === 0} title="Next keyframe">
                ▶
              </button>
              ${this.allowCollapse ? b `
                <button class="secondary" @click=${this.toggleCollapse}>
                  ${this.collapsed ? '▼ Expand' : '▲ Collapse'}
                </button>
              ` : ''}
              <button class="secondary" @click=${this.toggleConfig}>
                ${this.showConfig ? 'Hide' : 'Show'} Config
              </button>
              <button class="secondary" @click=${this.undo} ?disabled=${this.undoStack.length === 0}>
                ↶ Undo
              </button>
              <button @click=${this.clearKeyframes}>Clear</button>
            </div>
          </div>
        ` : ''}
        
        ${this.showConfig ? b `
          <div class="config-panel">
            <div class="config-row">
              <label>Duration (hours):</label>
              <input 
                type="number" 
                min="1" 
                max="168" 
                .value=${this.duration.toString()}
                @change=${this.updateDuration}
              />
            </div>
            <div class="config-row">
              <label>Time Slots:</label>
              <input 
                type="number" 
                min="1" 
                max="288" 
                .value=${this.slots.toString()}
                @change=${this.updateSlots}
              />
            </div>
            <div class="config-row">
              <label>Prev Day End:</label>
              <input 
                type="number" 
                min="${this.minValue}" 
                max="${this.maxValue}" 
                step="0.01"
                placeholder="Auto"
                .value=${this.previousDayEndValue?.toString() || ''}
                @change=${this.updatePreviousDayEnd}
              />
            </div>
            <div class="config-row">
              <label>Min Value:</label>
              <input 
                type="number" 
                step="0.1"
                .value=${this.minValue.toString()}
                @change=${this.updateMinValue}
              />
            </div>
            <div class="config-row">
              <label>Max Value:</label>
              <input 
                type="number" 
                step="0.1"
                .value=${this.maxValue.toString()}
                @change=${this.updateMaxValue}
              />
            </div>
            <div class="config-row">
              <label>Snap Value:</label>
              <input 
                type="number" 
                min="0"
                step="0.01"
                placeholder="None"
                .value=${this.snapValue > 0 ? this.snapValue.toString() : ''}
                @change=${this.updateSnapValue}
              />
            </div>
            <div class="config-row">
              <label>X Axis Label:</label>
              <input 
                type="text" 
                placeholder="e.g. Time"
                .value=${this.xAxisLabel}
                @input=${this.updateXAxisLabel}
              />
            </div>
            <div class="config-row">
              <label>Y Axis Label:</label>
              <input 
                type="text" 
                placeholder="e.g. Value"
                .value=${this.yAxisLabel}
                @input=${this.updateYAxisLabel}
              />
            </div>
          </div>
        ` : ''}
        
        <div style="position: relative;">
          ${this.backgroundGraphs.length > 0 ? b `
            <div class="graph-legend ${this.legendCollapsed ? 'collapsed' : ''}">
              <div class="graph-legend-header" @click=${this.toggleLegend}>
                <span class="graph-legend-toggle">▶</span>
                <span>Legend</span>
              </div>
              ${!this.legendCollapsed ? b `
                <div class="graph-legend-items">
                  ${this.backgroundGraphs.map((bgGraph, graphIndex) => b `
                    <div class="graph-legend-item" title=${bgGraph.label || `Entity ${graphIndex + 1}`}>
                      <span class="graph-legend-swatch" style="background: ${this.getBackgroundGraphColor(bgGraph, graphIndex)};"></span>
                      <span class="graph-legend-label">${bgGraph.label || `Entity ${graphIndex + 1}`}</span>
                    </div>
                  `)}
                </div>
              ` : ''}
            </div>
          ` : ''}
          <div class="timeline-canvas-wrapper ${this.collapsed ? '' : 'expanded'}" @click=${this.collapsed && this.allowCollapse ? this.toggleCollapse : null}>
            <div class="timeline-canvas ${this.collapsed ? 'collapsed' : ''} ${this.isDragging ? 'dragging' : ''}">
              <canvas 
                style="touch-action: none;"
                @click=${this.handleCanvasClick}
                @dblclick=${this.handleCanvasDoubleClick}
                @contextmenu=${this.handleCanvasContextMenu}
                @mousedown=${this.handleCanvasMouseDown}
                @mousemove=${this.handleCanvasMouseMove}
                @mouseup=${this.handleCanvasMouseUp}
                @mouseleave=${this.handleCanvasMouseLeave}
                @touchstart=${this.handleCanvasMouseDown}
                @touchmove=${this.handleCanvasMouseMove}
                @touchend=${this.handleCanvasMouseUp}
                @touchcancel=${this.handleCanvasMouseUp}
              ></canvas>
              <div class="cursor-tooltip" hidden></div>
            </div>
            ${this.collapsed ? b `<div class="expand-hint">Click to expand</div>` : ''}
          </div>
          ${this.showScrollNavLeft ? b `
            <div class="scroll-nav left" @click=${this.scrollToStart}>◀</div>
          ` : ''}
          ${this.showScrollNavRight ? b `
            <div class="scroll-nav right" @click=${this.scrollToEnd}>▶</div>
          ` : ''}
        </div>
        
        <div class="info">
          Double-click to add • Drag to move • Right-click/hold to delete • ${this.keyframes.length} keyframes
        </div>
      </div>
    `;
    }
};
KeyframeTimeline.styles = i$3 `
    :host {
      display: block;
      width: 100%;
      --timeline-height: 400px;
      --timeline-height-collapsed: 100px;
      --timeline-bg: var(--card-background-color, #1c1c1c);
      --timeline-track: var(--secondary-background-color, #2c2c2c);
      --timeline-ruler: var(--divider-color, rgba(255, 255, 255, 0.12));
      --keyframe-color: var(--accent-color, var(--primary-color, #03a9f4));
      --keyframe-selected-color: var(--success-color, #4caf50);
      --keyframe-dragging-color: var(--warning-color, #ff9800);
      --canvas-text-primary: var(--primary-text-color, rgba(255, 255, 255, 0.9));
      --canvas-text-secondary: var(--secondary-text-color, rgba(255, 255, 255, 0.7));
      --canvas-grid-line: rgba(68, 68, 68, 0.3);
      --canvas-grid-line-major: rgba(68, 68, 68, 0.5);
      --canvas-label-bg: var(--card-background-color, rgba(0, 0, 0, 0.7));
      --indicator-color: var(--accent-color, #ff9800);
    }
    
    .timeline-container {
      background: var(--background);
      border-radius: 4px;
      padding: 0;
      user-select: none;
    }
    
    .timeline-title {
      font-size: 16px;
      font-weight: 600;
      color: var(--primary-text-color, #e1e1e1);
      cursor: pointer;
      user-select: none;
      margin-bottom: 12px;
    }
    
    .timeline-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 12px;
      color: var(--primary-text-color, #e1e1e1);
    }
    
    .timeline-header span {
      cursor: pointer;
      user-select: none;
    }
    
    .timeline-controls {
      display: flex;
      gap: 8px;
    }
    
    button {
      background: var(--primary-color, #03a9f4);
      color: var(--primary-text-color, white);
      border: none;
      padding: 6px 12px;
      border-radius: 4px;
      cursor: pointer;
      font-size: 14px;
    }
    
    button:hover {
      opacity: 0.9;
    }
    
    button:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
    
    button.secondary {
      background: var(--secondary-background-color, #2c2c2c);
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.12));
    }
    
    .config-panel {
      background: var(--secondary-background-color, #2c2c2c);
      border-radius: 4px;
      padding: 12px;
      margin-bottom: 12px;
    }
    
    .config-row {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 8px;
    }
    
    .config-row label {
      min-width: 80px;
      font-size: 14px;
    }
    
    input[type="number"], input[type="text"] {
      background: var(--timeline-track);
      color: var(--primary-text-color, #e1e1e1);
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.12));
      border-radius: 4px;
      padding: 4px 8px;
      font-size: 14px;
    }
    
    .slot-list {
      display: flex;
      flex-direction: column;
      gap: 6px;
      margin-top: 8px;
    }
    
    .slot-item {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 4px;
      background: var(--timeline-track);
      border-radius: 4px;
    }
    
    .slot-color {
      width: 20px;
      height: 20px;
      border-radius: 3px;
      cursor: pointer;
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.12));
    }
    
    .timeline-canvas-wrapper {
      width: 100%;
      overflow-x: auto;
      overflow-y: hidden;
      background: var(--timeline-track);
      border-radius: 4px;
      transition: height 0.3s ease;
      cursor: pointer;
      scrollbar-width: thin;
      scrollbar-color: var(--scrollbar-thumb-color, rgba(128, 128, 128, 0.4)) transparent;
    }
    
    .timeline-canvas-wrapper::-webkit-scrollbar {
      height: 8px;
    }
    
    .timeline-canvas-wrapper::-webkit-scrollbar-track {
      background: transparent;
    }
    
    .timeline-canvas-wrapper::-webkit-scrollbar-thumb {
      background: var(--scrollbar-thumb-color, rgba(128, 128, 128, 0.4));
      border-radius: 4px;
    }
    
    .timeline-canvas-wrapper::-webkit-scrollbar-thumb:hover {
      background: var(--scrollbar-thumb-color-hover, rgba(128, 128, 128, 0.6));
    }
    
    .timeline-canvas-wrapper.expanded {
      cursor: default;
    }
    
    .timeline-canvas-wrapper:not(.expanded) {
      overflow-x: hidden;
    }
    
    .timeline-canvas {
      min-width: max(100%, 800px);
      height: var(--timeline-height);
      background: var(--timeline-track);
      cursor: crosshair;
      position: relative;
      touch-action: none;
      transition: height 0.3s ease;
    }
    
    .timeline-canvas.collapsed {
      min-width: 100%;
      height: var(--timeline-height-collapsed);
      cursor: pointer;
    }
    
    .timeline-canvas.dragging {
      cursor: grabbing;
    }
    
    canvas {
      display: block;
      width: 100%;
      height: 100%;
    }
    
    @media (max-width: 800px) {
      .timeline-canvas:not(.collapsed) {
        min-width: 800px;
      }
    }
    
    .expand-hint {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: var(--card-background-color, rgba(0, 0, 0, 0.7));
      color: var(--primary-text-color, white);
      padding: 8px 16px;
      border-radius: 4px;
      font-size: 12px;
      pointer-events: none;
      opacity: 0;
      transition: opacity 0.2s ease;
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.2));
    }
    
    .timeline-canvas-wrapper:hover .expand-hint {
      opacity: 1;
    }
    
    .timeline-canvas-wrapper.expanded .expand-hint {
      display: none;
    }
    
    .scroll-nav {
      position: absolute;
      top: 10px;
      transform: none;
      background: var(--card-background-color, rgba(0, 0, 0, 0.6));
      color: var(--primary-text-color, white);
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.2));
      border-radius: 4px;
      width: 32px;
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      z-index: 10;
      transition: background 0.2s ease;
      user-select: none;
      pointer-events: auto; /* Ensure button is clickable but doesn't block canvas unnecessarily */
    }
    
    .scroll-nav:hover {
      background: var(--secondary-background-color, rgba(0, 0, 0, 0.8));
    }
    
    .scroll-nav.left {
      left: -10px;
    }
    
    .scroll-nav.right {
      right: -10px;
    }

    .graph-legend {
      position: absolute;
      top: 10px;
      left: 70px;
      z-index: 12;
      min-width: 160px;
      max-width: 240px;
      background: rgba(0, 0, 0, 0.5);
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.2));
      border-radius: 6px;
      color: var(--canvas-text-primary);
      font-size: 12px;
    }

    .graph-legend-header {
      display: flex;
      align-items: center;
      gap: 6px;
      padding: 6px 8px;
      cursor: pointer;
      user-select: none;
      font-weight: 600;
    }

    .graph-legend-toggle {
      display: inline-block;
      font-size: 11px;
      transform: rotate(90deg);
      transition: transform 0.2s ease;
    }

    .graph-legend.collapsed .graph-legend-toggle {
      transform: rotate(0deg);
    }

    .graph-legend-items {
      padding: 0 8px 8px 8px;
      display: flex;
      flex-direction: column;
      gap: 6px;
      max-height: 180px;
      overflow-y: auto;
    }

    .graph-legend-item {
      display: flex;
      align-items: center;
      gap: 8px;
      min-width: 0;
    }

    .graph-legend-swatch {
      width: 10px;
      height: 10px;
      border-radius: 2px;
      border: 1px solid var(--divider-color, rgba(255, 255, 255, 0.3));
      flex: 0 0 auto;
    }

    .graph-legend-label {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: var(--canvas-text-secondary);
    }

    .cursor-tooltip {
      position: absolute;
      z-index: 20;
      pointer-events: none;
      background-color: rgba(var(--rgb-card-background-color, 224, 224, 224), 0.9);
      background-image: none;
      border: 1px solid var(--primary-color);
      border-radius: 4px;
      padding: 8px;
      font-size: 13px;
      line-height: 18px;
      white-space: nowrap;
    }

    .cursor-tooltip-line {
      color: var(--keyframe-color);
      font-weight: 600;
      opacity: 1;
    }
    
    .info {
      margin-top: 12px;
      font-size: 12px;
      color: var(--secondary-text-color, #9e9e9e);
    }
  `;
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "duration", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "slots", void 0);
__decorate([
    n({ type: Array })
], KeyframeTimeline.prototype, "keyframes", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "previousDayEndValue", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "minValue", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "maxValue", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "snapValue", void 0);
__decorate([
    n({ type: String })
], KeyframeTimeline.prototype, "xAxisLabel", void 0);
__decorate([
    n({ type: String })
], KeyframeTimeline.prototype, "yAxisLabel", void 0);
__decorate([
    n({ type: String })
], KeyframeTimeline.prototype, "title", void 0);
__decorate([
    n({ type: Boolean })
], KeyframeTimeline.prototype, "showHeader", void 0);
__decorate([
    n({ type: Boolean })
], KeyframeTimeline.prototype, "allowCollapse", void 0);
__decorate([
    n({ type: Boolean })
], KeyframeTimeline.prototype, "readonly", void 0);
__decorate([
    n({ type: Number })
], KeyframeTimeline.prototype, "indicatorTime", void 0);
__decorate([
    n({ type: Boolean })
], KeyframeTimeline.prototype, "showCurrentTime", void 0);
__decorate([
    n({ type: Array })
], KeyframeTimeline.prototype, "backgroundGraphs", void 0);
__decorate([
    n({ type: Array })
], KeyframeTimeline.prototype, "advanceHistory", void 0);
__decorate([
    n({ type: String, attribute: false })
], KeyframeTimeline.prototype, "tooltipMode", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "canvasWidth", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "canvasHeight", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "showConfig", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "draggingIndex", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "draggingSegment", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "selectedKeyframeIndex", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "collapsed", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "showScrollNavLeft", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "showScrollNavRight", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "undoStack", void 0);
__decorate([
    r()
], KeyframeTimeline.prototype, "legendCollapsed", void 0);
KeyframeTimeline = __decorate([
    t('keyframe-timeline')
], KeyframeTimeline);

export { KeyframeTimeline };
//# sourceMappingURL=keyframe-timeline.js.map
